UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_func.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 #include "g_func.h"
30 #include "g_actor.h"
31 #include "g_ai.h"
32 #include "g_match.h"
33 #include "g_move.h"
34 #include "g_spawn.h"
35 #include "g_trigger.h"
36 #include "g_utils.h"
37 #include "g_vis.h"
38 
46 static bool Touch_Breakable (Edict* self, Edict* activator)
47 {
49  if (G_IsActor(activator))
50  G_TriggerAddToList(self, activator);
51 
52  return false;
53 }
54 
55 static bool Destroy_Breakable (Edict* self)
56 {
57  vec3_t origin;
58  const char* model = self->model;
59 
60  self->absBox.getCenter(origin);
61 
62  const char* breakSound = nullptr;
63  switch (self->material) {
64  case MAT_GLASS:
65  breakSound = "misc/breakglass+";
66  break;
67  case MAT_METAL:
68  breakSound = "misc/breakmetal+";
69  break;
70  case MAT_ELECTRICAL:
71  breakSound = "misc/breakelectric+";
72  break;
73  case MAT_WOOD:
74  breakSound = "misc/breakwood+";
75  break;
76  case MAT_MAX:
77  break;
78  }
79 
80  /* the HP value is used to decide whether this was a triggered call or a
81  * call during a fight - a triggered call will be handled differently in
82  * terms of timing and the related particle effects in the client code */
83  if (self->HP == 0)
84  G_EventModelExplodeTriggered(*self, breakSound);
85  else
86  G_EventModelExplode(*self, breakSound);
87 
88  if (self->particle)
89  G_SpawnParticle(origin, self->spawnflags, self->particle);
90 
91  G_TouchEdicts(self, 10.0f);
92  linkedList_t* touchedList = self->touchedList;
93 
94  /* destroy the door trigger */
95  if (self->child()) {
96  Edict* trigger = self->child();
97  /* Remove all activators and reset client actions before removing the trigger */
98  linkedList_t* list = trigger->touchedList;
99  while (list) {
100  linkedList_t* next = list->next;
101  Edict* activator = static_cast<Edict*>(list->data);
102  G_TriggerRemoveFromList(trigger, activator);
103  if (trigger->reset != nullptr)
104  trigger->reset(trigger, activator);
105  list = next;
106  }
107  G_FreeEdict(trigger);
108  }
109 
110  /* now we can destroy the edict completely */
111  G_FreeEdict(self);
112 
113  AABB oldAABB;
114  gi.GetInlineModelAABB(model, oldAABB);
115  GridBox rerouteOldBox;
116  rerouteOldBox.set(oldAABB);
117  G_RecalcRouting(model, rerouteOldBox);
118 
119  LIST_Foreach(touchedList, Edict, activator) {
120  if (G_IsActor(activator))
121  G_ActorFall(activator);
122  }
123 
124  return true;
125 }
126 
127 static bool Use_Breakable (Edict* self, Edict* activator)
128 {
129  self->HP = 0;
130  return Destroy_Breakable(self);
131 }
132 
145 {
146  ent->classname = "breakable";
147  ent->type = ET_BREAKABLE;
148 
149  ent->flags |= FL_DESTROYABLE;
150 
151  /* set an inline model */
152  gi.SetModel(ent, ent->model);
153  ent->solid = SOLID_BSP;
154  gi.LinkEdict(ent);
155 
156  char boxStr[AABB_STRING];
157  ent->entBox.asIntString(boxStr, sizeof(boxStr));
158  Com_DPrintf(DEBUG_GAME, "func_breakable: model (%s) num: %i %s origin: %i %i %i\n",
159  ent->model, ent->mapNum, boxStr,
160  (int)ent->origin[0], (int)ent->origin[1], (int)ent->origin[2]);
161 
162  ent->destroy = Destroy_Breakable;
163  ent->use = Use_Breakable;
165 }
166 
167 /*
168 =============================================================================
169 DOOR FUNCTIONS
170 =============================================================================
171 */
172 
180 static void Door_SlidingUse (Edict* door)
181 {
182  const bool open = door->doorState == STATE_OPENED;
183 
184  /* get the movement angle vector - a negative speed value will close the door*/
185  vec3_t moveAngles;
186  GET_SLIDING_DOOR_SHIFT_VECTOR(door->dir, open ? 1 : -1, moveAngles);
187 
188  /* get the direction vector from the movement angles that were set on the entity */
189  vec3_t moveDir;
190  AngleVectors(moveAngles, moveDir, nullptr, nullptr);
191  VectorAbs(moveDir);
192 
193  /* calculate the distance from the movement angles and the entity size. This is the
194  * distance the door has to slide to fully open or close */
195  const int distance = DotProduct(moveDir, door->size);
196 
197  /* the door is moved in one step on the server side - lerping is not needed here - so we
198  * perform the scalar multiplication with the distance the door must move in order to
199  * fully close/open */
200  vec3_t distanceVec;
201  VectorMul(distance, moveAngles, distanceVec);
202 
203  /* set the updated position. The bounding boxes that are used for tracing must be
204  * shifted when the door state changes. As the mins and maxs of the aabb are absolute
205  * world coordinates in the map we have to translate the position by the above
206  * calculated movement vector */
207  VectorAdd(door->origin, distanceVec, door->origin); /* calc new model position */
208 // gi.SetInlineModelOrientation(door->model, door->origin, door->angles); /* move the model out of the way */
209 }
210 
217 static bool Door_Use (Edict* door, Edict* activator)
218 {
219  int opening = 1;
220 
221  if (door->doorState == STATE_CLOSED) {
222  door->doorState = STATE_OPENED;
223  opening = 1;
224  } else if (door->doorState == STATE_OPENED) {
225  door->doorState = STATE_CLOSED;
226  opening = -1;
227  } else {
228  return false;
229  }
230 
231  /* remember the old location */
232  AABB oldAABB;
233  gi.GetInlineModelAABB(door->model, oldAABB);
234  GridBox rerouteOldBox;
235  rerouteOldBox.set(oldAABB);
236 
237  /* change rotation and relink */
238  if (door->type == ET_DOOR) {
239  if (door->dir & DOOR_OPEN_REVERSE)
240  opening *= -1;
241  door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE * opening;
242  } else if (door->type == ET_DOOR_SLIDING) {
243  Door_SlidingUse(door);
244  }
245  gi.LinkEdict(door);
246 
247  /* maybe the server called this because the door starts opened */
248  if (G_MatchIsRunning()) {
249  /* let everybody know, that the door moves */
250  if (door->doorState == STATE_OPENED)
251  G_EventDoorOpen(*door);
252  else
253  G_EventDoorClose(*door);
254  if (Q_strvalid(door->noise)) {
255  const playermask_t playerMask = G_GetClosePlayerMask(door->origin, UNIT_SIZE * 10);
256  G_EventSpawnSound(playerMask, *door, door->origin, door->noise);
257  }
258  }
259 
260  /* Update model orientation */
261  gi.SetInlineModelOrientation(door->model, door->origin, door->angles);
262  AABB newAabb;
263  gi.GetInlineModelAABB(door->model, newAabb);
264  GridBox rerouteNewBox;
265  rerouteNewBox.set(newAabb);
266  Com_DPrintf(DEBUG_GAME, "Server processed door movement.\n");
267 
268  /* Update path finding table for the new location of the model */
269  G_RecalcRouting(door->model, rerouteOldBox); /* Update path finding table */
270  G_RecalcRouting(door->model, rerouteNewBox);
271 
272  if (activator && G_IsLivingActor(activator)) {
273  /* Check if the player appears/perishes, seen from other teams. */
274  G_CheckVis(activator);
275 
276  /* Calc new vis for the activator. */
277  G_CheckVisTeamAll(activator->getTeam(), 0, activator);
278  }
279 
280  return true;
281 }
282 
290 static bool Touch_DoorTrigger (Edict* self, Edict* activator)
291 {
292  if (!self->owner())
293  return false;
294  if (!self->owner()->inuse)
295  return false;
296  if (!G_IsActor(activator))
297  return false;
298 
299  Actor* actor = makeActor(activator);
300  if (G_IsAI(actor)) {
301  /* let the ai interact with the door */
302  if (self->flags & FL_GROUPSLAVE)
303  self = self->groupMaster;
304  if (AI_CheckUsingDoor(actor, self->owner()))
305  G_ActorUseDoor(actor, self->owner());
306  /* we don't want the client action stuff to be send for ai actors */
307  return false;
308  }
309 
310  /* prepare for client action */
311  G_ActorSetClientAction(actor, self->owner());
312  return true;
313 }
314 
320 static void Reset_DoorTrigger (Edict* self, Edict* activator)
321 {
322  if (activator->clientAction == self->owner())
323  G_ActorSetClientAction(activator, nullptr);
324 }
325 
326 #define REVERSE 0x00000200
327 
335 void SP_func_door (Edict* ent)
336 {
337  ent->classname = "door";
338  ent->type = ET_DOOR;
339  if (!ent->noise)
340  ent->noise = "doors/open_close";
341 
342  /* set an inline model */
343  gi.SetModel(ent, ent->model);
344  ent->solid = SOLID_BSP;
345  gi.LinkEdict(ent);
346  ent->doorState = STATE_CLOSED;
347  ent->dir = YAW;
348 
349  if (ent->spawnflags & REVERSE)
350  ent->dir |= DOOR_OPEN_REVERSE;
351 
352  if (ent->HP)
353  ent->flags |= FL_DESTROYABLE;
354  ent->flags |= FL_CLIENTACTION;
355 
356  /* spawn the trigger entity */
357  Edict* other = G_TriggerSpawn(ent);
358  other->setTouch(Touch_DoorTrigger);
359  other->reset = Reset_DoorTrigger;
360  ent->setChild(other);
361 
363  if (!ent->speed)
364  ent->speed = 10;
365  ent->use = Door_Use;
366 
367  /* the door should start opened */
368  if (ent->spawnflags & FL_TRIGGERED)
369  G_UseEdict(ent, nullptr);
370 
371  ent->destroy = Destroy_Breakable;
372 }
373 
375 {
376  ent->classname = "doorsliding";
377  ent->type = ET_DOOR_SLIDING;
378  if (!ent->noise)
379  ent->noise = "doors/slide";
380 
381  /* set an inline model */
382  gi.SetModel(ent, ent->model);
383  ent->solid = SOLID_BSP;
384  gi.LinkEdict(ent);
385 
386  if (ent->spawnflags & REVERSE)
387  ent->dir |= DOOR_OPEN_REVERSE;
388 
389  if (ent->HP)
390  ent->flags |= FL_DESTROYABLE;
391 
392  ent->doorState = STATE_CLOSED;
393  if (!ent->speed)
394  ent->speed = 10;
395  ent->use = Door_Use;
396 
397  ent->destroy = Destroy_Breakable;
398 }
399 
406 {
407  ent->classname = "rotating";
408  ent->type = ET_ROTATING;
409 
410  /* set an inline model */
411  gi.SetModel(ent, ent->model);
412  ent->solid = SOLID_BSP;
413  gi.LinkEdict(ent);
414 
415  /* the lower, the faster */
416  if (!ent->speed)
417  ent->speed = 50;
418 
419  if (ent->HP)
420  ent->flags |= FL_DESTROYABLE;
421 
422  ent->destroy = Destroy_Breakable;
423 }
void set(const pos3_t mini, const pos3_t maxi)
Definition: mathlib.h:148
void SP_func_rotating(Edict *ent)
Spawns a rotating solid inline brush model.
Definition: g_func.cpp:405
void G_RecalcRouting(const char *model, const GridBox &box)
Definition: g_utils.cpp:452
solid_t solid
Definition: g_edict.h:58
static bool Touch_Breakable(Edict *self, Edict *activator)
If an actor was standing on the breakable that is going to get destroyed, we have to let him fall to ...
Definition: g_func.cpp:46
Edict * child()
Definition: g_edict.h:237
#define REVERSE
Definition: g_func.cpp:326
#define VectorAbs(a)
Definition: vector.h:81
void G_CheckVis(Edict *check, const vischeckflags_t visFlags)
Check if the edict appears/perishes for the other teams. If they appear for other teams...
Definition: g_vis.cpp:409
Artificial Intelligence functions.
void SP_func_breakable(Edict *ent)
func_breakable (0.3 0.3 0.3) ? Used for breakable objects.
Definition: g_func.cpp:144
linkedList_t * touchedList
Definition: g_edict.h:157
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
voidpf uLong int origin
Definition: ioapi.h:45
vec3_t angles
Definition: g_edict.h:54
unsigned int playermask_t
Definition: g_events.h:34
Definition: aabb.h:42
int doorState
Definition: g_edict.h:158
#define STATE_OPENED
Definition: defines.h:87
static bool Use_Breakable(Edict *self, Edict *activator)
Definition: g_func.cpp:127
bool(* use)(Edict *self, Edict *activator)
Definition: g_edict.h:154
Trigger functions.
AABB entBox
Definition: g_edict.h:60
#define FL_TRIGGERED
Trigger the edict at spawn.
Definition: g_local.h:300
static void Reset_DoorTrigger(Edict *self, Edict *activator)
Left the door trigger zone - reset the client action.
Definition: g_func.cpp:320
int mapNum
Definition: g_edict.h:73
bool AI_CheckUsingDoor(const Edict *ent, const Edict *door)
Checks whether the AI controlled actor wants to use a door.
Definition: g_ai.cpp:396
byte dir
Definition: g_edict.h:86
#define G_IsActor(ent)
Definition: g_local.h:127
static bool Destroy_Breakable(Edict *self)
Definition: g_func.cpp:55
void G_TouchEdicts(Edict *trigger, float extend)
Call after linking a new trigger in or destroying a bmodel during gameplay to force all entities it c...
Definition: g_utils.cpp:625
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
int speed
Definition: g_edict.h:124
const char * noise
Definition: g_edict.h:132
#define FL_GROUPSLAVE
not the first on the team
Definition: g_local.h:292
static bool Door_Use(Edict *door, Edict *activator)
Opens/closes a door.
Definition: g_func.cpp:217
#define Q_strvalid(string)
Definition: shared.h:141
vec3_t size
Definition: g_edict.h:62
#define YAW
Definition: mathlib.h:55
int spawnflags
Definition: g_edict.h:118
playermask_t G_GetClosePlayerMask(const vec3_t origin, float radius)
Assembles a player mask for those players that have a living team member close to the given location...
Definition: g_utils.cpp:288
int getTeam() const
Definition: g_edict.h:269
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time)
Definition: g_match.cpp:320
void setTouch(bool(*touch_)(Edict *self, Edict *activator))
Definition: g_edict.h:321
static void Door_SlidingUse(Edict *door)
Slides a door.
Definition: g_func.cpp:180
Match related functions.
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Create the rotation matrix in order to rotate something.
Definition: mathlib.cpp:631
#define VectorMul(scalar, b, dest)
Definition: vector.h:48
func_* edicts
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
game_import_t gi
Definition: g_main.cpp:39
#define DOOR_ROTATION_ANGLE
Definition: q_shared.h:187
void G_EventDoorClose(const Edict &door)
Definition: g_events.cpp:683
void G_ActorUseDoor(Actor *actor, Edict *door)
Make the actor use (as in open/close) a door edict.
Definition: g_actor.cpp:55
static bool Touch_DoorTrigger(Edict *self, Edict *activator)
Trigger to open the door we are standing in front of it.
Definition: g_func.cpp:290
#define STATE_CLOSED
Definition: defines.h:88
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
void G_EventModelExplodeTriggered(const Edict &ent, const char *sound)
Definition: g_events.cpp:689
Edict * owner()
Definition: g_edict.h:240
void G_ActorSetClientAction(Edict *actor, Edict *ent)
Handles the client actions (interaction with the world)
Definition: g_actor.cpp:82
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
void(* reset)(Edict *self, Edict *activator)
Definition: g_edict.h:148
Edict * clientAction
Definition: g_edict.h:113
int flags
Definition: g_edict.h:169
#define GET_SLIDING_DOOR_SHIFT_VECTOR(dir, speed, vecout)
Definition: q_shared.h:294
#define UNIT_SIZE
Definition: defines.h:121
void SP_func_door_sliding(Edict *ent)
Definition: g_func.cpp:374
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
void G_EventDoorOpen(const Edict &door)
Definition: g_events.cpp:677
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
int G_CheckVisTeamAll(const int team, const vischeckflags_t visFlags, const Edict *ent)
Do G_CheckVisTeam for all entities ent is the one that is looking at the others.
Definition: g_vis.cpp:376
void asIntString(char *str, size_t len)
Prints a representation of the box.
Definition: aabb.h:167
#define VectorAdd(a, b, dest)
Definition: vector.h:47
void G_EventModelExplode(const Edict &ent, const char *sound)
Definition: g_events.cpp:697
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_IsAI(ent)
Definition: g_local.h:141
#define FL_DESTROYABLE
If an edict is destroyable (like ET_BREAKABLE, ET_DOOR [if health set] or maybe a ET_MISSION [if heal...
Definition: g_local.h:287
Brings new objects into the world.
entity_type_t type
Definition: g_edict.h:81
void G_ActorSetTU(Edict *ent, int tus)
Definition: g_actor.cpp:267
void setChild(Edict *child)
Definition: g_edict.h:192
vec3_t origin
Definition: g_edict.h:53
bool(* destroy)(Edict *self)
Definition: g_edict.h:155
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
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
vec_t vec3_t[3]
Definition: ufotypes.h:39
linkedList_t * next
Definition: list.h:32
void SP_func_door(Edict *ent)
func_door (0 .5 .8) ? "health" if set, door is destroyable
Definition: g_func.cpp:335
const char * classname
Definition: g_edict.h:67
#define AABB_STRING
Definition: aabb.h:40
Definition: g_edict.h:45
bool G_TriggerRemoveFromList(Edict *self, Edict *activator)
Removes an activator from the list of recognized edicts.
Definition: g_trigger.cpp:87
#define TU_DOOR_ACTION
Definition: defines.h:78
#define DOOR_OPEN_REVERSE
Definition: q_shared.h:293
void G_FreeEdict(Edict *ent)
Marks the edict as free.
Definition: g_utils.cpp:41
const char * model
Definition: g_edict.h:74
#define DEBUG_GAME
Definition: defines.h:61
void G_ActorFall(Edict *ent)
Let an actor fall down if e.g. the func_breakable the actor was standing on was destroyed.
Definition: g_move.cpp:118
int HP
Definition: g_edict.h:89