UFO: Alien Invasion
Loading...
Searching...
No Matches
g_trigger.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Original file from Quake 2 v3.21: quake2-2.31/game/g_spawn.c
10Copyright (C) 1997-2001 Id Software, Inc.
11
12This program is free software; you can redistribute it and/or
13modify it under the terms of the GNU General Public License
14as published by the Free Software Foundation; either version 2
15of the License, or (at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, 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
47bool 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
67void 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
87bool 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
131static 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
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";
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
212bool 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
280static 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
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
306static 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
336 ent->setChild(nullptr);
337
338 gi.LinkEdict(ent);
339}
340
345static 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
359static 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;
393 ent->setChild(nullptr);
394
395 gi.LinkEdict(ent);
396}
@ SND_HURT
Definition chr_shared.h:220
#define _(String)
Definition cl_shared.h:44
Definition aabb.h:42
void expandXY(const float byVal)
expand the box in four directions, but clip them to the maximum boundaries
Definition aabb.h:232
void getCenter(vec3_t center) const
Calculates the center of the bounding box.
Definition aabb.h:155
void set(const AABB &other)
Copies the values from the given aabb.
Definition aabb.h:60
An Edict of type Actor.
Definition g_edict.h:348
void setInRescueZone(bool inRescueZone_)
Set the rescue zone flag.
Definition g_edict.h:414
bool isSameTeamAs(const Edict *other) const
Definition g_edict.h:278
character_t chr
Definition g_edict.h:116
int flags
Definition g_edict.h:169
byte dmgtype
Definition g_edict.h:139
void addStun(int stu)
Definition g_edict.h:305
const char * classname
Definition g_edict.h:67
const char * particle
Definition g_edict.h:128
Edict * owner()
Definition g_edict.h:240
void(* think)(Edict *self)
Definition g_edict.h:150
const char * target
Definition g_edict.h:125
bool(* use)(Edict *self, Edict *activator)
Definition g_edict.h:154
void setTouch(bool(*touch_)(Edict *self, Edict *activator))
Definition g_edict.h:321
bool inuse
Definition g_edict.h:47
int getTeam() const
Definition g_edict.h:269
AABB entBox
Definition g_edict.h:60
Edict * _owner
Definition g_edict.h:65
void setChild(Edict *child)
Definition g_edict.h:192
Player & getPlayer() const
Definition g_edict.h:265
void(* reset)(Edict *self, Edict *activator)
Definition g_edict.h:148
AABB absBox
Definition g_edict.h:61
int dmg
Definition g_edict.h:138
int spawnflags
Definition g_edict.h:118
linkedList_t * touchedList
Definition g_edict.h:157
solid_t solid
Definition g_edict.h:58
entity_type_t type
Definition g_edict.h:81
const char * model
Definition g_edict.h:74
const char * nextmap
Definition g_edict.h:129
#define PRINT_HUD
Definition defines.h:107
#define UNIT_SIZE
Definition defines.h:121
void G_ActorSetClientAction(Edict *actor, Edict *ent)
Handles the client actions (interaction with the world).
Definition g_actor.cpp:82
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition g_actor.cpp:43
#define G_IsDead(ent)
Definition g_actor.h:34
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition g_client.cpp:206
Interface for g_client.cpp.
int G_ApplyProtection(const Edict *target, const byte dmgWeight, int damage)
Reduces damage by armour and natural protection.
Definition g_combat.cpp:362
void G_CheckDeathOrKnockout(Actor *target, Actor *attacker, const fireDef_t *fd, int damage)
Definition g_combat.cpp:499
All parts of the main game logic that are combat related.
Actor * makeActor(Edict *ent)
Convert an Edict pointer into an Actor pointer.
Definition g_edicts.cpp:327
Edict * G_EdictsFindTargetEntity(const char *target)
Searches the edict that has the given target as targetname set.
Definition g_edicts.cpp:290
functions to handle the storage and lifecycle of all edicts in the game module.
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
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
#define PM_ALL
Definition g_events.h:36
#define G_PlayerToPM(player)
Definition g_events.h:37
void G_DamageActor(Edict *target, const int damage, const vec3_t impact)
Deals damage and causes wounds.
Definition g_health.cpp:52
#define TAG_LEVEL
Definition g_local.h:59
level_locals_t level
Definition g_main.cpp:38
#define G_IsActor(ent)
Definition g_local.h:127
#define G_MemFree(ptr)
Definition g_local.h:65
#define TAG_GAME
Definition g_local.h:58
game_import_t gi
Definition g_main.cpp:39
#define G_TagMalloc(size, tag)
Definition g_local.h:64
#define G_IsMultiPlayer()
Definition g_local.h:145
#define FL_CLIENTACTION
Edict flag to indicate, that the edict can be used in the context of a client action.
Definition g_local.h:298
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
Match related functions.
Edict * G_Spawn(const char *classname)
Either finds a free edict, or allocates a new one.
Definition g_spawn.cpp:403
Edict * G_SpawnParticle(const vec3_t origin, int spawnflags, const char *particle)
Definition g_spawn.cpp:551
Brings new objects into the world.
static void Reset_TouchTrigger(Edict *self, Edict *activator)
void SP_trigger_touch(Edict *ent)
Touch trigger to call the use function of the attached target.
void SP_trigger_nextmap(Edict *ent)
Edict * G_TriggerSpawn(Edict *owner)
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
static void Reset_RescueTrigger(Edict *self, Edict *activator)
static bool Touch_NextMapTrigger(Edict *self, Edict *activator)
Next map trigger that is going to get active once all opponents are killed.
#define TRIGGER_TOUCH_ONCE
static bool Touch_RescueTrigger(Edict *self, Edict *activator)
Rescue trigger.
void SP_trigger_hurt(Edict *ent)
Trigger for grid fields if they are under fire.
void Think_NextMapTrigger(Edict *self)
Register this think function once you would like to end the match This think function will register t...
bool G_TriggerIsInList(Edict *self, Edict *activator)
Checks whether the activator of this trigger_touch was already recognized.
Definition g_trigger.cpp:47
static bool Touch_TouchTrigger(Edict *self, Edict *activator)
Touch trigger.
bool G_TriggerRemoveFromList(Edict *self, Edict *activator)
Removes an activator from the list of recognized edicts.
Definition g_trigger.cpp:87
bool Touch_HurtTrigger(Edict *self, Edict *activator)
Hurt trigger.
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...
Trigger functions.
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
void G_FreeEdict(Edict *ent)
Marks the edict as free.
Definition g_utils.cpp:41
Misc utility functions for game module.
@ SOLID_TRIGGER
Definition game.h:155
#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
@ ET_TRIGGER_NEXTMAP
Definition q_shared.h:155
@ ET_TRIGGER_RESCUE
Definition q_shared.h:154
@ ET_TRIGGER_HURT
Definition q_shared.h:152
@ ET_TRIGGER_TOUCH
Definition q_shared.h:153
@ ET_TRIGGER
Definition q_shared.h:151
#define Q_streq(a, b)
Definition shared.h:136
#define MAX_VAR
Definition shared.h:36
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const teamDef_t * teamDef
Definition chr_shared.h:413
void * data
Definition list.h:31
linkedList_t * next
Definition list.h:32
const char * getActorSound(int gender, actorSound_t soundType) const
pos_t pos3_t[3]
Definition ufotypes.h:58
vec_t vec3_t[3]
Definition ufotypes.h:39