UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_trigger.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 Original file from Quake 2 v3.21: quake2-2.31/game/g_spawn.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11 
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21 See the GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 
27 */
28 
29 
30 #include "g_trigger.h"
31 #include "g_client.h"
32 #include "g_combat.h"
33 #include "g_actor.h"
34 #include "g_edicts.h"
35 #include "g_match.h"
36 #include "g_spawn.h"
37 #include "g_utils.h"
38 #include "g_health.h"
39 
47 bool G_TriggerIsInList (Edict* self, Edict* activator)
48 {
49  if (activator == nullptr)
50  return true;
51 
52  const linkedList_t* entry = self->touchedList;
53  while (entry) {
54  if (entry->data == activator)
55  return true;
56  entry = entry->next;
57  }
58 
59  return false;
60 }
61 
67 void G_TriggerAddToList (Edict* self, Edict* activator)
68 {
69  if (activator == nullptr)
70  return;
71 
72  if (G_TriggerIsInList(self, activator))
73  return;
74 
75  linkedList_t* entry = static_cast<linkedList_t*>(G_TagMalloc(sizeof(linkedList_t), TAG_LEVEL));
76  entry->data = activator;
77  entry->next = self->touchedList;
78  self->touchedList = entry;
79 }
80 
87 bool G_TriggerRemoveFromList (Edict* self, Edict* activator)
88 {
89  if (activator == nullptr)
90  return true;
91 
92  linkedList_t** list = &self->touchedList;
93  while (*list) {
94  linkedList_t* entry = *list;
95  if (entry->data == activator) {
96  *list = entry->next;
97  G_MemFree(entry);
98  return true;
99  }
100  list = &entry->next;
101  }
102 
103  return false;
104 }
105 
107 {
108  Edict* trigger = G_Spawn("trigger");
109  trigger->type = ET_TRIGGER;
110  /* set the owner, e.g. link the door into the trigger */
111  trigger->_owner = owner;
112 
113  AABB aabb(owner->absBox);
114  aabb.expandXY(UNIT_SIZE / 2); /* expand the trigger box */
115 
116  trigger->entBox.set(aabb);
117 
118  trigger->solid = SOLID_TRIGGER;
119  trigger->reset = nullptr;
120 
121  /* link into the world */
122  gi.LinkEdict(trigger);
123 
124  return trigger;
125 }
126 
131 static bool Touch_NextMapTrigger (Edict* self, Edict* activator)
132 {
133  if (activator != nullptr && activator->isSameTeamAs(self)) {
134  char command[MAX_VAR];
135  self->inuse = false;
136  G_ClientPrintf(activator->getPlayer(), PRINT_HUD, _("Switching map!"));
137  Com_sprintf(command, sizeof(command), "map %s %s\n",
138  level.day ? "day" : "night", self->nextmap);
139  level.mapEndCommand = (char*)G_TagMalloc(strlen(command) + 1, TAG_GAME);
140  Q_strncpyz(level.mapEndCommand, command, strlen(command));
141 
142  level.nextMapSwitch = true;
143  G_MatchEndTrigger(self->getTeam(), 0);
144  }
145  return true;
146 }
147 
154 {
155  vec3_t center;
156  pos3_t centerPos;
157 
158  self->absBox.getCenter(center);
159 
160  /* spawn the particle to mark the trigger */
161  G_SpawnParticle(center, self->spawnflags, self->particle);
162  VecToPos(center, centerPos);
163  G_EventCenterViewAt(PM_ALL, centerPos);
164 
165  gi.BroadcastPrintf(PRINT_HUD, _("You are now ready to switch the map."));
166 
167  self->setTouch(Touch_NextMapTrigger);
168  self->think = nullptr;
169 }
170 
172 {
173  /* only used in single player */
174  if (G_IsMultiPlayer()) {
175  G_FreeEdict(ent);
176  return;
177  }
178 
179  if (!ent->particle) {
180  gi.DPrintf("particle isn't set for %s\n", ent->classname);
181  G_FreeEdict(ent);
182  return;
183  }
184  if (!ent->nextmap) {
185  gi.DPrintf("nextmap isn't set for %s\n", ent->classname);
186  G_FreeEdict(ent);
187  return;
188  }
189 
190  if (Q_streq(ent->nextmap, level.mapname)) {
191  gi.DPrintf("nextmap loop detected\n");
192  G_FreeEdict(ent);
193  return;
194  }
195 
196  ent->classname = "trigger_nextmap";
197  ent->type = ET_TRIGGER_NEXTMAP;
198 
199  ent->solid = SOLID_TRIGGER;
200  gi.SetModel(ent, ent->model);
201 
202  ent->reset = nullptr;
203  ent->setChild(nullptr);
204 
205  gi.LinkEdict(ent);
206 }
207 
212 bool Touch_HurtTrigger (Edict* self, Edict* activator)
213 {
214  /* Dead actors should really not be able to trigger this - they can't be hurt anymore anyway */
215  if (!G_IsLivingActor(activator))
216  return false;
217 
218  /* If no damage is dealt don't count it as triggered */
219  const int damage = G_ApplyProtection(activator, self->dmgtype, self->dmg);
220  if (damage == 0)
221  return false;
222 
223  const bool stunEl = (self->dmgtype == gi.csi->damStunElectro);
224  const bool stunGas = (self->dmgtype == gi.csi->damStunGas);
225  const bool shock = (self->dmgtype == gi.csi->damShock);
226  const bool isRobot = activator->chr.teamDef->robot;
227  Actor* actor = makeActor(activator);
228 
229  if (stunEl || (stunGas && !isRobot)) {
230  actor->addStun(damage);
231  } else if (shock) {
233  } else {
234  G_DamageActor(actor, damage, nullptr);
235  }
236  /* Play hurt sound unless this is shock damage -- it doesn't do anything
237  * because we don't actually handle it above yet */
238  if (!shock) {
239  const teamDef_t* teamDef = activator->chr.teamDef;
240  const int gender = activator->chr.gender;
241  const char* sound = teamDef->getActorSound(gender, SND_HURT);
242  G_EventSpawnSound(G_PlayerToPM(activator->getPlayer()), *activator, nullptr, sound);
243  }
244 
245  G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage);
246 
247  return true;
248 }
249 
256 {
257  ent->classname = "trigger_hurt";
258  ent->type = ET_TRIGGER_HURT;
259 
260  if (!ent->dmg)
261  ent->dmg = 5;
262  ent->dmgtype = gi.csi->damFire;
263 
264  ent->solid = SOLID_TRIGGER;
265  gi.SetModel(ent, ent->model);
266 
268  ent->reset = nullptr;
269  ent->setChild(nullptr);
270 
271  gi.LinkEdict(ent);
272 }
273 
274 #define TRIGGER_TOUCH_ONCE (1 << 0)
275 
280 static bool Touch_TouchTrigger (Edict* self, Edict* activator)
281 {
282  /* these actors should really not be able to trigger this - they don't move anymore */
283  assert(!G_IsDead(activator));
284 
285  self->_owner = G_EdictsFindTargetEntity(self->target);
286  if (!self->owner()) {
287  gi.DPrintf("Target '%s' wasn't found for %s\n", self->target, self->classname);
288  G_FreeEdict(self);
289  return false;
290  }
291 
292  if (self->owner()->flags & FL_CLIENTACTION) {
293  G_ActorSetClientAction(activator, self->owner());
294  } else if (!(self->spawnflags & TRIGGER_TOUCH_ONCE) || self->touchedList == nullptr) {
295  if (!self->owner()->use) {
296  gi.DPrintf("Owner of %s doesn't have a use function\n", self->classname);
297  G_FreeEdict(self);
298  return false;
299  }
300  G_UseEdict(self->owner(), activator);
301  }
302 
303  return false;
304 }
305 
306 static void Reset_TouchTrigger (Edict* self, Edict* activator)
307 {
308  /* fire the use function on leaving the trigger area */
309  if (activator != nullptr && (self->owner()->flags & FL_CLIENTACTION))
310  G_ActorSetClientAction(activator, nullptr);
311  else if ((self->spawnflags & TRIGGER_TOUCH_ONCE) && self->touchedList == nullptr)
312  G_UseEdict(self->owner(), activator);
313 }
314 
321 {
322  ent->classname = "trigger_touch";
323  ent->type = ET_TRIGGER_TOUCH;
324 
325  if (!ent->target) {
326  gi.DPrintf("No target given for %s\n", ent->classname);
327  G_FreeEdict(ent);
328  return;
329  }
330 
331  ent->solid = SOLID_TRIGGER;
332  gi.SetModel(ent, ent->model);
333 
335  ent->reset = Reset_TouchTrigger;
336  ent->setChild(nullptr);
337 
338  gi.LinkEdict(ent);
339 }
340 
345 static bool Touch_RescueTrigger (Edict* self, Edict* activator)
346 {
347  /* these actors should really not be able to trigger this - they don't move anymore */
348  assert(!G_IsDead(activator));
349 
350  if (G_IsActor(activator)) {
351  Actor* actor = makeActor(activator);
352  if (self->isSameTeamAs(actor))
353  actor->setInRescueZone(true);
354  }
355 
356  return false;
357 }
358 
359 static void Reset_RescueTrigger (Edict* self, Edict* activator)
360 {
361  if (G_IsActor(activator)) {
362  Actor* actor = makeActor(activator);
363  if (self->isSameTeamAs(actor))
364  actor->setInRescueZone(false);
365  }
366 }
367 
376 {
377  /* only used in single player */
378  if (G_IsMultiPlayer()) {
379  G_FreeEdict(ent);
380  return;
381  }
382 
383  ent->classname = "trigger_rescue";
384  ent->type = ET_TRIGGER_RESCUE;
385 
386  ent->solid = SOLID_TRIGGER;
387  gi.SetModel(ent, ent->model);
388 
389  if (ent->spawnflags == 0)
390  ent->spawnflags |= 0xFF;
392  ent->reset = Reset_RescueTrigger;
393  ent->setChild(nullptr);
394 
395  gi.LinkEdict(ent);
396 }
char mapname[MAX_QPATH]
Definition: g_local.h:85
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition: g_client.cpp:206
static void Reset_TouchTrigger(Edict *self, Edict *activator)
Definition: g_trigger.cpp:306
static void Reset_RescueTrigger(Edict *self, Edict *activator)
Definition: g_trigger.cpp:359
int damStunElectro
Definition: q_shared.h:535
bool isSameTeamAs(const Edict *other) const
Definition: g_edict.h:278
solid_t solid
Definition: g_edict.h:58
void G_MatchEndTrigger(int team, int timeGap)
Triggers the end of the game. Will be executed in the next server (or game) frame.
Definition: g_match.cpp:125
static bool Touch_TouchTrigger(Edict *self, Edict *activator)
Touch trigger.
Definition: g_trigger.cpp:280
byte dmgtype
Definition: g_edict.h:139
const teamDef_t * teamDef
Definition: chr_shared.h:394
int damStunGas
Definition: q_shared.h:533
bool robot
Definition: chr_shared.h:322
#define _(String)
Definition: cl_shared.h:43
Misc utility functions for game module.
void * data
Definition: list.h:31
#define FL_CLIENTACTION
Edict flag to indicate, that the edict can be used in the context of a client action.
Definition: g_local.h:296
Edict * G_TriggerSpawn(Edict *owner)
Definition: g_trigger.cpp:106
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
Definition: aabb.h:42
character_t chr
Definition: g_edict.h:116
bool nextMapSwitch
Definition: g_local.h:90
int damShock
Definition: q_shared.h:529
void SP_trigger_hurt(Edict *ent)
Trigger for grid fields if they are under fire.
Definition: g_trigger.cpp:255
void Think_NextMapTrigger(Edict *self)
Register this think function once you would like to end the match This think function will register t...
Definition: g_trigger.cpp:153
Trigger functions.
AABB entBox
Definition: g_edict.h:60
AABB absBox
Definition: g_edict.h:61
const char * target
Definition: g_edict.h:125
#define PM_ALL
Definition: g_events.h:36
void set(const AABB &other)
Copies the values from the given aabb.
Definition: aabb.h:60
static bool Touch_NextMapTrigger(Edict *self, Edict *activator)
Next map trigger that is going to get active once all opponents are killed.
Definition: g_trigger.cpp:131
#define TAG_GAME
Definition: g_local.h:58
#define G_IsActor(ent)
Definition: g_local.h:127
#define G_MemFree(ptr)
Definition: g_local.h:65
void G_CheckDeathOrKnockout(Actor *target, Actor *attacker, const fireDef_t *fd, int damage)
Definition: g_combat.cpp:499
#define TRIGGER_TOUCH_ONCE
Definition: g_trigger.cpp:274
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition: g_actor.cpp:43
Actor * makeActor(Edict *ent)
Convert an Edict pointer into an Actor pointer.
Definition: g_edicts.cpp:327
const csi_t * csi
Definition: game.h:176
Player & getPlayer() const
Definition: g_edict.h:265
Edict * G_EdictsFindTargetEntity(const char *target)
Searches the edict that has the given target as targetname set.
Definition: g_edicts.cpp:290
int spawnflags
Definition: g_edict.h:118
void setTouch(bool(*touch_)(Edict *self, Edict *activator))
Definition: g_edict.h:321
Match related functions.
int damFire
Definition: q_shared.h:528
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
#define G_IsMultiPlayer()
Definition: g_local.h:145
#define TAG_LEVEL
Definition: g_local.h:59
void SP_trigger_touch(Edict *ent)
Touch trigger to call the use function of the attached target.
Definition: g_trigger.cpp:320
game_import_t gi
Definition: g_main.cpp:39
const char * nextmap
Definition: g_edict.h:129
#define MAX_VAR
Definition: shared.h:36
int dmg
Definition: g_edict.h:138
void setInRescueZone(bool inRescueZone_)
Set the rescue zone flag.
Definition: g_edict.h:414
All parts of the main game logic that are combat related.
void SP_trigger_nextmap(Edict *ent)
Definition: g_trigger.cpp:171
#define G_PlayerToPM(player)
Definition: g_events.h:37
Edict * G_Spawn(const char *classname)
Either finds a free edict, or allocates a new one.
Definition: g_spawn.cpp:403
#define VecToPos(v, p)
Map boundary is +/- MAX_WORLD_WIDTH - to get into the positive area we add the possible max negative ...
Definition: mathlib.h:100
#define G_TagMalloc(size, tag)
Definition: g_local.h:64
An Edict of type Actor.
Definition: g_edict.h:348
Edict * G_SpawnParticle(const vec3_t origin, int spawnflags, const char *particle)
Definition: g_spawn.cpp:551
Edict * owner()
Definition: g_edict.h:240
void SP_trigger_rescue(Edict *ent)
Rescue trigger to mark an actor to be in the rescue zone. Aborting a game would not kill the actors i...
Definition: g_trigger.cpp:375
void G_ActorSetClientAction(Edict *actor, Edict *ent)
Handles the client actions (interaction with the world)
Definition: g_actor.cpp:82
void(* reset)(Edict *self, Edict *activator)
Definition: g_edict.h:148
void G_DamageActor(Edict *target, const int damage, const vec3_t impact)
Deals damage and causes wounds.
Definition: g_health.cpp:52
pos_t pos3_t[3]
Definition: ufotypes.h:58
bool Touch_HurtTrigger(Edict *self, Edict *activator)
Hurt trigger.
Definition: g_trigger.cpp:212
void expandXY(const float byVal)
expand the box in four directions, but clip them to the maximum boundaries
Definition: aabb.h:232
void G_EventCenterViewAt(playermask_t playerMask, const pos3_t pos)
Centers the view for all clients that are seeing the given edict on the world position of the edict...
Definition: g_events.cpp:489
#define UNIT_SIZE
Definition: defines.h:121
bool G_TriggerIsInList(Edict *self, Edict *activator)
Checks whether the activator of this trigger_touch was already recognized.
Definition: g_trigger.cpp:47
void G_EventSpawnSound(playermask_t playerMask, const Edict &ent, const vec3_t origin, const char *sound)
Spawns a sound (that will be spatialized on the client side)
Definition: g_events.cpp:40
void G_TriggerAddToList(Edict *self, Edict *activator)
Adds the activator to the list of recognized edicts for this trigger_touch edict. ...
Definition: g_trigger.cpp:67
#define G_IsDead(ent)
Definition: g_actor.h:34
void addStun(int stu)
Definition: g_edict.h:305
Brings new objects into the world.
entity_type_t type
Definition: g_edict.h:81
void setChild(Edict *child)
Definition: g_edict.h:192
functions to handle the storage and lifecycle of all edicts in the game module.
bool G_UseEdict(Edict *ent, Edict *activator)
Call the 'use' function for the given edict and all its group members.
Definition: g_utils.cpp:117
const char * getActorSound(int gender, actorSound_t soundType) const
Returns the actor sounds for a given category.
Definition: chr_shared.cpp:54
vec_t vec3_t[3]
Definition: ufotypes.h:39
linkedList_t * next
Definition: list.h:32
const char * classname
Definition: g_edict.h:67
Edict * _owner
Definition: g_edict.h:65
#define Q_streq(a, b)
Definition: shared.h:136
static bool Touch_RescueTrigger(Edict *self, Edict *activator)
Rescue trigger.
Definition: g_trigger.cpp:345
Definition: g_edict.h:45
char * mapEndCommand
Definition: g_local.h:86
bool G_TriggerRemoveFromList(Edict *self, Edict *activator)
Removes an activator from the list of recognized edicts.
Definition: g_trigger.cpp:87
void G_FreeEdict(Edict *ent)
Marks the edict as free.
Definition: g_utils.cpp:41
const char * model
Definition: g_edict.h:74
const char * particle
Definition: g_edict.h:128
Interface for g_client.cpp.
level_locals_t level
Definition: g_main.cpp:38
int G_ApplyProtection(const Edict *target, const byte dmgWeight, int damage)
Reduces damage by armour and natural protection.
Definition: g_combat.cpp:362
#define PRINT_HUD
Definition: defines.h:107