UFO: Alien Invasion
Loading...
Searching...
No Matches
g_spawn.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#include "g_spawn.h"
30#include "g_ai.h"
31#include "g_client.h"
32#include "g_edicts.h"
33#include "g_func.h"
34#include "g_inventory.h"
35#include "g_mission.h"
36#include "g_reaction.h"
37#include "g_trigger.h"
38#include "g_utils.h"
39#include "g_vis.h"
40#include "../shared/parse.h"
42
43/* fields are needed for spawning from the entity string */
44#define FFL_SPAWNTEMP 1
45#define FFL_NOSPAWN 2
46
47#define G_ValidDescription(ent) ((ent)->description && ((ent)->description[0] == '_' || strstr((ent)->description, "*msgid:") != nullptr))
48
53typedef struct spawn_temp_s {
54 /* world vars */
58
60
61static void SP_light(Edict* ent);
62static void SP_dummy(Edict* ent);
63static void SP_player_start(Edict* ent);
64static void SP_human_start(Edict* ent);
65static void SP_alien_start(Edict* ent);
66static void SP_civilian_start(Edict* ent);
67static void SP_worldspawn(Edict* ent);
68static void SP_2x2_start(Edict* ent);
69static void SP_civilian_target(Edict* ent);
70static void SP_misc_model(Edict* ent);
71static void SP_misc_item(Edict* ent);
72static void SP_misc_mission(Edict* ent);
73static void SP_misc_message(Edict* ent);
74static void SP_misc_smoke(Edict* ent);
75static void SP_misc_fire(Edict* ent);
76static void SP_misc_camera(Edict* ent);
77static void SP_misc_smokestun(Edict* ent);
78
79typedef struct spawn_s {
80 const char* name;
81 void (*spawn) (Edict* ent);
82} spawn_t;
83
84static const spawn_t spawns[] = {
85 {"worldspawn", SP_worldspawn},
86 {"light", SP_light},
87 {"misc_item", SP_misc_item},
88 {"misc_sound", SP_dummy},
89 {"misc_model", SP_misc_model},
90 {"misc_particle", SP_dummy},
91 {"misc_mission", SP_misc_mission},
92 {"info_player_start", SP_player_start},
93 {"info_human_start", SP_human_start},
94 {"info_alien_start", SP_alien_start},
95 {"info_civilian_start", SP_civilian_start},
96 {"info_civilian_target", SP_civilian_target},
97 {"info_2x2_start", SP_2x2_start},
98 {"info_null", SP_dummy},
99 {"func_breakable", SP_func_breakable},
100 {"func_door", SP_func_door},
101 {"func_door_sliding", SP_func_door_sliding},
102 {"func_rotating", SP_func_rotating},
103 {"trigger_nextmap", SP_trigger_nextmap},
104 {"trigger_hurt", SP_trigger_hurt},
105 {"trigger_touch", SP_trigger_touch},
106 {"trigger_rescue", SP_trigger_rescue},
107 {"misc_message", SP_misc_message},
108 {"misc_smoke", SP_misc_smoke},
109 {"misc_fire", SP_misc_fire},
110 {"misc_smokestun", SP_misc_smokestun},
111 {"misc_camera", SP_misc_camera},
112
113 {nullptr, nullptr}
114};
115
119static void ED_CallSpawn (Edict* ent)
120{
121 if (!ent->classname)
122 return;
123
124 /* check normal spawn functions */
125 for (const spawn_t* s = spawns; s->name; s++) {
126 /* found it */
127 if (Q_streq(s->name, ent->classname)) {
128 s->spawn(ent);
129 return;
130 }
131 }
132
133 ent->inuse = false;
134}
135
141static char* ED_NewString (const char* string)
142{
143 const size_t l = strlen(string) + 1;
144 char* newb = (char*)G_TagMalloc(l, TAG_LEVEL);
145 char* new_p = newb;
146
147 for (int i = 0; i < l; i++) {
148 /* check for special chars and convert them */
149 if (string[i] == '\\' && i < l - 1) {
150 i++;
151 if (string[i] == 'n')
152 *new_p++ = '\n';
153 else
154 *new_p++ = '\\';
155 } else
156 *new_p++ = string[i];
157 }
158
159 return newb;
160}
161
165static void ED_ParseField (const char* key, const char* value, Edict* ent)
166{
167 KeyValuePair kvp(key, value);
168
169 if (kvp.isKey("classname"))
170 ent->classname = ED_NewString(value);
171 else if (kvp.isKey("model"))
172 ent->model = ED_NewString(value);
173 else if (kvp.isKey("spawnflags"))
174 ent->spawnflags = kvp.asInt();
175 else if (kvp.isKey("speed"))
176 ent->speed = kvp.asInt();
177 else if (kvp.isKey("dir"))
178 ent->dir = kvp.asInt();
179 else if (kvp.isKey("active"))
180 ent->active = kvp.asBool();
181 else if (kvp.isKey("target"))
182 ent->target = ED_NewString(value);
183 else if (kvp.isKey("targetname"))
184 ent->targetname = ED_NewString(value);
185 else if (kvp.isKey("item"))
186 ent->item = ED_NewString(value);
187 else if (kvp.isKey("noise"))
188 ent->noise = ED_NewString(value);
189 else if (kvp.isKey("particle"))
190 ent->particle = ED_NewString(value);
191 else if (kvp.isKey("nextmap"))
192 ent->nextmap = ED_NewString(value);
193 else if (kvp.isKey("frame"))
194 ent->frame = kvp.asInt();
195 else if (kvp.isKey("team"))
196 ent->setTeam(kvp.asInt());
197 else if (kvp.isKey("group"))
198 ent->group = ED_NewString(value);
199 else if (kvp.isKey("size"))
200 ent->fieldSize = kvp.asInt();
201 else if (kvp.isKey("count"))
202 ent->count = kvp.asInt();
203 else if (kvp.isKey("time"))
204 ent->time = kvp.asInt();
205 else if (kvp.isKey("health"))
206 ent->HP = kvp.asInt();
207 else if (kvp.isKey("radius"))
208 ent->radius = kvp.asInt();
209 else if (kvp.isKey("sounds"))
210 ent->sounds = kvp.asInt();
211 else if (kvp.isKey("material"))
212 ent->material = static_cast<edictMaterial_t>(kvp.asInt()); // enum !!
213 else if (kvp.isKey("light"))
214 ; // ignore
217 else if (kvp.isKey("maxteams"))
218 ; // ignore
219 else if (kvp.isKey("maxlevel"))
220 ; // ignore
221 else if (kvp.isKey("dmg"))
222 ent->dmg = kvp.asInt();
223 else if (kvp.isKey("origin"))
224 kvp.asVec3(ent->origin);
225 else if (kvp.isKey("angles"))
226 kvp.asVec3(ent->angles);
227 else if (kvp.isKey("angle"))
228 ent->angle = kvp.asFloat();
229 else if (kvp.isKey("message"))
230 ent->message = ED_NewString(value);
231 else if (kvp.isKey("desc"))
232 ent->description = ED_NewString(value);
233
234 else if (kvp.isKey("norandomspawn"))
235 spawnTemp.noRandomSpawn = kvp.asInt();
236 else if (kvp.isKey("noequipment"))
237 spawnTemp.noEquipment = kvp.asInt();
238}
239
245static const char* ED_ParseEdict (const char* data, Edict* ent)
246{
247 char keyname[MAX_VAR];
248
249 bool reset = true;
251
252 /* go through all the dictionary pairs */
253 while (1) {
254 /* parse key */
255 const char* c = Com_Parse(&data);
256 if (c[0] == '}')
257 break;
258 if (!data)
259 gi.Error("ED_ParseEntity: EOF without closing brace");
260
261 Q_strncpyz(keyname, c, sizeof(keyname));
262
263 /* parse value */
264 c = Com_Parse(&data);
265 if (!data)
266 gi.Error("ED_ParseEntity: EOF without closing brace");
267
268 if (c[0] == '}')
269 gi.Error("ED_ParseEntity: closing brace without data");
270
271 reset = false;
272
273 /* keynames with a leading underscore are used for utility comments,
274 * and are immediately discarded by ufo */
275 if (keyname[0] == '_')
276 continue;
277
278 ED_ParseField(keyname, c, ent);
279 }
280
281 if (reset)
282 ent->nativeReset();
283
284 return data;
285}
286
292static void G_FindEdictGroups (void)
293{
294 Edict* ent = G_EdictsGetFirst(); /* the first edict is always a world edict that can be skipped */
295
296 while ((ent = G_EdictsGetNextInUse(ent))) {
297 /* no group at all */
298 if (!ent->group)
299 continue;
300 /* already marked as slave in another group */
301 if (ent->flags & FL_GROUPSLAVE)
302 continue;
303 Edict* chain = ent;
304 ent->groupMaster = ent;
305 Edict* groupMember = ent;
306 /* search only the remainder of the entities */
307 while ((groupMember = G_EdictsGetNextInUse(groupMember))) {
308 /* no group at all */
309 if (!groupMember->group)
310 continue;
311 /* already marked as slave in another group */
312 if (groupMember->flags & FL_GROUPSLAVE)
313 continue;
314 /* same group as the master? */
315 if (Q_streq(ent->group, groupMember->group)) {
316 chain->groupChain = groupMember;
317 groupMember->groupMaster = ent;
318 chain = groupMember;
319 groupMember->flags |= FL_GROUPSLAVE;
320 }
321 }
322 }
323}
324
331void G_SpawnEntities (const char* mapname, bool day, const char* entities)
332{
334
335 OBJZERO(level);
336 level.pathingMap = (pathing_t*)G_TagMalloc(sizeof(*level.pathingMap), TAG_LEVEL);
337
338 G_EdictsInit();
339
340 /* initialize reactionFire data */
342
343 Q_strncpyz(level.mapname, mapname, sizeof(level.mapname));
344 level.day = day;
345
347
348 level.activeTeam = TEAM_NO_ACTIVE;
349 level.actualRound = 1;
350 level.hurtAliens = sv_hurtaliens->integer;
351
352 /* parse ents */
353 int entnum = 0;
354 while (1) {
355 /* parse the opening brace */
356 const char* token = Com_Parse(&entities);
357 if (!entities)
358 break;
359 if (token[0] != '{')
360 gi.Error("ED_LoadFromFile: found %s when expecting {", token);
361
362 Edict* ent = G_Spawn();
363
365
366 ent->mapNum = entnum++;
367
368 /* Set the position of the entity */
369 VecToPos(ent->origin, ent->pos);
370
371 /* Call this entity's specific initializer (sets ent->type) */
372 ED_CallSpawn(ent);
373
374 /* if this entity is an bbox (e.g. actor), then center its origin based on its position */
375 if (ent->solid == SOLID_BBOX)
376 ent->calcOrigin();
377 }
378
379 /* spawn ai players, if needed */
380 if (level.num_spawnpoints[TEAM_CIVILIAN]) {
381 if (AI_CreatePlayer(TEAM_CIVILIAN) == nullptr)
382 gi.DPrintf("Could not create civilian\n");
383 }
384
385 if ((G_IsSinglePlayer() || ai_multiplayeraliens->integer) && level.num_spawnpoints[TEAM_ALIEN]) {
386 if (AI_CreatePlayer(TEAM_ALIEN) == nullptr)
387 gi.DPrintf("Could not create alien\n");
388 }
389
390 Com_Printf("Used inventory slots after ai spawn: %i\n", game.invi.GetUsedSlots());
391
393}
394
403Edict* G_Spawn (const char* classname)
404{
405 Edict* ent = G_EdictsGetNewEdict();
406
407 if (!ent)
408 gi.Error("G_Spawn: no free edicts");
409
410 ent->inuse = true;
411 ent->number = G_EdictsGetNumber(ent);
412 if (classname)
413 ent->classname = classname;
414 else
415 ent->classname = "noclass";
417 ent->setActive(); /* only used by camera */
418 return ent;
419}
420
421static void Think_SmokeAndFire (Edict* self)
422{
423 const int endRound = self->time + self->count;
424 const int spawnIndex = (self->getTeam() + level.teamOfs) % MAX_TEAMS;
425 const int currentIndex = (level.activeTeam + level.teamOfs) % MAX_TEAMS;
426 if (endRound < level.actualRound || (endRound == level.actualRound && spawnIndex <= currentIndex)) {
427 const bool checkVis = self->type == ET_SMOKE;
430 G_FreeEdict(self);
431 if (checkVis)
432 G_CheckVis(nullptr);
433 }
434}
435
436static void G_SpawnFieldPart (const entity_type_t fieldtype, const vec3_t vec, const char* particle, int rounds, int damage)
437{
438 pos3_t pos;
439 VecToPos(vec, pos); /* calculate grid position */
440
441 Edict* ent = G_GetEdictFromPos(pos, fieldtype);
442 if (ent == nullptr) {
443 pos_t z = gi.GridFall(ACTOR_SIZE_NORMAL, pos);
444 if (std::abs(pos[2] - z) > 1)
445 return;
446
447 pos[2] = z;
448 ent = G_Spawn();
449 VectorCopy(pos, ent->pos);
450 ent->calcOrigin(); /* although vec is supposed to be the origin, calc origin from pos. That's safer. */
451 ent->dmg = damage;
452 ent->particle = particle;
454 switch (fieldtype) {
455 case ET_SMOKE:
456 SP_misc_smoke(ent);
457 break;
458 case ET_FIRE:
459 SP_misc_fire(ent);
460 break;
461 case ET_SMOKESTUN:
463 break;
464 default:
465 break;
466 }
467 }
468
469 ent->count = rounds;
470}
471
482static void G_SpawnFieldGroup (const entity_type_t fieldtype, const vec3_t vec, const char* particle, int rounds, int damage, vec_t radius)
483{
484 G_SpawnFieldPart(fieldtype, vec, particle, rounds, damage);
485
486 /* for all cells in a square of +/- radius */
487 for (vec_t x = vec[0] - radius; x <= vec[0] + radius; x += UNIT_SIZE) {
488 for (vec_t y = vec[1] - radius; y <= vec[1] + radius; y += UNIT_SIZE) {
489 vec3_t end;
490 VectorSet(end, x, y, vec[2]);
491
492 /* cut off the edges of the square to resemble a circle */
493 if (VectorDist(end, vec) > radius)
494 continue;
495 if (!gi.isOnMap(end))
496 continue;
497 const trace_t tr = G_Trace(Line(vec, end), nullptr, MASK_SMOKE_AND_FIRE);
498 /* trace didn't reach the target - something was hit before */
499 if (tr.fraction < 1.0f || (tr.contentFlags & CONTENTS_WATER)) {
500 continue;
501 }
502 G_SpawnFieldPart(fieldtype, end, particle, rounds, damage);
503 }
504 }
505}
506
516void G_SpawnSmokeField (const vec3_t vec, const char* particle, int rounds, int damage, vec_t radius)
517{
518 G_SpawnFieldGroup(ET_SMOKE, vec, particle, rounds, damage, radius);
519}
520
521void G_SpawnFireField (const vec3_t vec, const char* particle, int rounds, int damage, vec_t radius)
522{
523 G_SpawnFieldGroup(ET_FIRE, vec, particle, rounds, damage, radius);
524}
525
526void G_SpawnStunSmokeField (const vec3_t vec, const char* particle, int rounds, int damage, vec_t radius)
527{
528 G_SpawnFieldGroup(ET_SMOKESTUN, vec, particle, rounds, damage, radius);
529}
530
536{
537 Edict* floorItem = G_Spawn("item");
538 floorItem->type = ET_ITEM;
539 /* make sure that the item is always on a field that even the smallest actor can reach */
540 floorItem->fieldSize = ACTOR_SIZE_NORMAL;
541 VectorCopy(pos, floorItem->pos);
542 floorItem->pos[2] = gi.GridFall(floorItem->fieldSize, floorItem->pos);
543 floorItem->calcOrigin();
544 return floorItem;
545}
546
551Edict* G_SpawnParticle (const vec3_t origin, int spawnflags, const char* particle)
552{
553 Edict* ent = G_Spawn("particle");
554 ent->type = ET_PARTICLE;
555 VectorCopy(origin, ent->origin);
556
557 /* Set the position of the entity */
558 VecToPos(ent->origin, ent->pos);
559
560 ent->particle = particle;
561 ent->spawnflags = spawnflags;
562
563 G_CheckVis(ent);
564
565 return ent;
566}
567
571static void G_ActorSpawn (Edict* ent)
572{
573 /* set properties */
574 level.num_spawnpoints[ent->getTeam()]++;
575 ent->classname = "actor";
576 ent->type = ET_ACTORSPAWN;
578
579 /* Fall to ground */
580 if (ent->pos[2] >= PATHFINDING_HEIGHT)
581 ent->pos[2] = PATHFINDING_HEIGHT - 1;
582 vec3_t vec;
583 PosToVec(ent->pos, vec);
584 while (ent->pos[2] > 0 && !gi.isOnMap(vec)) {
585 --ent->pos[2];
586 PosToVec(ent->pos, vec);
587 }
588 ent->pos[2] = gi.GridFall(ent->fieldSize, ent->pos);
589 if (ent->pos[2] >= PATHFINDING_HEIGHT)
590 gi.DPrintf("G_ActorSpawn: Warning: z level is out of bounds: %i\n", ent->pos[2]);
591
592 ent->calcOrigin();
593
594 /* link it for collision detection */
595 ent->dir = AngleToDir(ent->angle);
596 assert(ent->dir < CORE_DIRECTIONS);
597 ent->solid = SOLID_BBOX;
598
599 /* Set bounding box. Maybe this is already set in one of the spawn functions? */
600 if (ent->entBox.getMaxX() == 0)
602 if (ent->entBox.getMinX() == 0)
604}
605
609static void G_Actor2x2Spawn (Edict* ent)
610{
611 /* set properties */
612 level.num_2x2spawnpoints[ent->getTeam()]++;
613 ent->classname = "ugv";
614 ent->type = ET_ACTOR2x2SPAWN;
616
617 /* Spawning has already calculated the pos from the origin ( = center of the cell). Perfect for normal size actors.
618 * For 2x2 actors, the origin(of the info_ box) is in the middle of the four cells. Using VecToPos on that origin
619 * results in the upper right cell being the pos of the actor. But we want the lower left cell to be the pos of the
620 * 2x2 actor because routing and pathfinding rely on that. So compensate for that. */
621 ent->pos[0]--;
622 ent->pos[1]--;
623
624 /* Fall to ground */
625 if (ent->pos[2] >= PATHFINDING_HEIGHT)
626 ent->pos[2] = PATHFINDING_HEIGHT - 1;
627 ent->pos[2] = gi.GridFall(ent->fieldSize, ent->pos);
628 if (ent->pos[2] >= PATHFINDING_HEIGHT)
629 gi.DPrintf("G_Actor2x2Spawn: Warning: z level is out of bounds: %i\n", ent->pos[2]);
630 ent->calcOrigin();
631
632 /* link it for collision detection */
633 ent->dir = AngleToDir(ent->angle);
634 assert(ent->dir < CORE_DIRECTIONS);
635 ent->solid = SOLID_BBOX;
636
637 /* Set bounding box. Maybe this is already set in one of the spawn functions? */
638 if (ent->entBox.getMaxX() == 0)
640 if (ent->entBox.getMinX() == 0)
642}
643
647static void SP_light (Edict* ent)
648{
649 /* lights aren't client-server communicated items */
650 /* they are completely client side */
651 G_FreeEdict(ent);
652}
653
660static void SP_player_start (Edict* ent)
661{
662 /* only used in multi player */
663 if (G_IsSinglePlayer()) {
664 G_FreeEdict(ent);
665 return;
666 }
667
669 /* maybe there are already the max soldiers allowed per team connected */
670 if (sv_maxsoldiersperteam->integer > level.num_spawnpoints[ent->getTeam()]) {
671 G_ActorSpawn(ent);
672 } else
673 G_FreeEdict(ent);
674}
675
680static void SP_human_start (Edict* ent)
681{
682 /* only used in single player */
683 if (G_IsMultiPlayer()) {
684 G_FreeEdict(ent);
685 return;
686 }
687
688 ent->setTeam(TEAM_PHALANX);
689 G_ActorSpawn(ent);
690}
691
692
697static void SP_2x2_start (Edict* ent)
698{
699 /* no 2x2 unit in multiplayer */
700 if (G_IsMultiPlayer()) {
701 G_FreeEdict(ent);
702 return;
703 }
704
705 if (!ent->getTeam())
706 ent->setTeam(TEAM_PHALANX);
707
708 /* these units are bigger */
710 ent->entBox.setMins(-(PLAYER_WIDTH * 2), -(PLAYER_WIDTH * 2), PLAYER_MIN);
711
712 /* spawn singleplayer 2x2 unit */
713 G_Actor2x2Spawn(ent);
714}
715
720static void SP_alien_start (Edict* ent)
721{
722 /* deactivateable in multiplayer */
723 if (G_IsMultiPlayer() && !ai_multiplayeraliens->integer) {
724 G_FreeEdict(ent);
725 return;
726 }
727 ent->setTeam(TEAM_ALIEN);
728
729 G_ActorSpawn(ent);
730}
731
732
737static void SP_civilian_start (Edict* ent)
738{
739 /* deactivateable in multiplayer */
740 if (G_IsMultiPlayer() && !ai_numcivilians->integer) {
741 G_FreeEdict(ent);
742 return;
743 }
745 ent->count = 100; /* current waypoint */
746 G_ActorSpawn(ent);
747}
748
756static void SP_civilian_target (Edict* ent)
757{
758 /* target point for which team */
760 ent->classname = "civtarget";
761 ent->type = ET_CIVILIANTARGET;
762 ent->fieldSize = ACTOR_SIZE_NORMAL; /* to let the grid fall function work */
763
764 /* add the edict to the list of known waypoints */
766
767 /* fall to ground */
768 if (ent->pos[2] >= PATHFINDING_HEIGHT)
769 ent->pos[2] = PATHFINDING_HEIGHT - 1;
770 ent->pos[2] = gi.GridFall(ent->fieldSize, ent->pos);
771 ent->calcOrigin();
772}
773
777static void SP_misc_mission (Edict* ent)
778{
779 ent->classname = "misc_mission";
780 ent->type = ET_MISSION;
781 ent->solid = SOLID_TRIGGER;
782
783 /* maybe this was set to something else for multiplayer */
784 if (!ent->getTeam())
785 ent->setTeam(TEAM_PHALANX);
786
787 if (!ent->time && !ent->target && !ent->item) {
788 G_FreeEdict(ent);
789 gi.DPrintf("misc_mission given with no objective\n");
790 return;
791 }
792
793 /* think function values */
794 ent->think = G_MissionThink;
795 ent->nextthink = 1;
796
797 if (ent->radius < 1) {
798 ent->radius = UNIT_SIZE * 3;
799 }
800 ent->entBox.setMaxs(ent->radius, ent->radius, PLAYER_STAND);
801 ent->entBox.setMins(-ent->radius, -ent->radius, PLAYER_MIN);
802
803 if (G_ValidDescription(ent))
805
807 ent->reset = G_MissionReset;
808 if (ent->target) {
809 ent->use = G_MissionUse;
810 /* Bomb/key/etc target will be freed when used - don't group it! */
811 if (ent->item)
812 ent->group = nullptr;
813 }
814
815 gi.LinkEdict(ent);
816}
817
825{
826 vec3_t center;
827 AABB shiftedBox(ent->absBox);
828 shiftedBox.getCenter(center);
829
830 pos3_t mins, maxs, origin;
831 VecToPos(shiftedBox.mins, mins);
832 VecToPos(shiftedBox.maxs, maxs);
833 VecToPos(center, origin);
834
835 const int xDelta = std::max(1, maxs[0] - mins[0]);
836 const int yDelta = std::max(1, maxs[1] - mins[1]);
837
838 int size = xDelta * yDelta;
840 ent->forbiddenListSize = size;
841
842 for (int i = 0; i < xDelta; i++) {
843 for (int j = 0; j < yDelta; j++) {
844 const pos_t x = mins[0] + i;
845 const pos_t y = mins[1] + j;
846 const pos_t z = origin[2];
847 VectorSet(ent->forbiddenListPos[i], x, y, z);
848 }
849 }
850}
851
852#define MISC_MODEL_SOLID (1 << 8)
856static void SP_misc_model (Edict* ent)
857{
858 if (ent->spawnflags & MISC_MODEL_SOLID) {
859 if (ent->model && ent->model[0] != '\0') {
860 AABB modelAabb;
861 if (gi.LoadModelAABB(ent->model, ent->frame, modelAabb)) {
862 ent->classname = "model";
863 ent->entBox.set(modelAabb);
864 ent->type = ET_SOLID;
865 ent->solid = SOLID_BBOX;
868 gi.LinkEdict(ent);
870 } else {
871 gi.DPrintf("Could not get mins/maxs for model '%s'\n", ent->model);
872 G_FreeEdict(ent);
873 }
874 } else {
875 gi.DPrintf("server_solid misc_model with no model given\n");
876 G_FreeEdict(ent);
877 }
878 } else {
879 /* handled client side */
880 G_FreeEdict(ent);
881 }
882}
883
887static void SP_misc_item (Edict* ent)
888{
889 if (!ent->item) {
890 gi.DPrintf("No item defined in misc_item\n");
891 G_FreeEdict(ent);
892 return;
893 }
894
895 G_AddItemToFloor(ent->pos, ent->item);
896
897 /* now we can free the original edict */
898 G_FreeEdict(ent);
899}
900
901static bool Message_Use (Edict* self, Edict* activator)
902{
903 if (!activator || !G_IsActor(activator)) {
904 return false;
905 } else {
906 if (G_ValidMessage(self)) {
907 Player& player = activator->getPlayer();
908 const char* msg = self->message;
909 /* remove gettext marker */
910 if (msg[0] == '_')
911 msg++;
912 G_ClientPrintf(player, PRINT_HUD, "%s", msg);
913 }
914
915 if (self->spawnflags & 1)
916 G_FreeEdict(self);
917
918 return false;
919 }
920}
921
922static void G_SpawnField (Edict* ent, const char* classname, entity_type_t type, solid_t solid)
923{
924 ent->classname = classname;
925 ent->type = type;
927 ent->solid = solid;
928 ent->entBox.setMaxs(UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2);
929 ent->entBox.setMins(-UNIT_SIZE / 2, -UNIT_SIZE / 2, -UNIT_HEIGHT / 2);
930 ent->calcOrigin();
932 ent->nextthink = 1;
933 ent->time = level.actualRound;
934 ent->setTeam(level.activeTeam);
935
936 gi.LinkEdict(ent);
937
938 vec3_t particleOrigin;
939 VectorCopy(ent->origin, particleOrigin);
940 particleOrigin[2] -= GROUND_DELTA;
941 ent->particleLink = G_SpawnParticle(particleOrigin, ent->spawnflags, ent->particle);
942}
943
944static void SP_misc_smoke (Edict* ent)
945{
946 G_SpawnField(ent, "smoke", ET_SMOKE, SOLID_TRIGGER);
947 ent->dmgtype = gi.csi->damStunGas;
949 G_CheckVis(nullptr);
950}
951
952static void SP_misc_fire (Edict* ent)
953{
954 G_SpawnField(ent, "fire", ET_FIRE, SOLID_TRIGGER);
955 ent->dmgtype = gi.csi->damIncendiary;
957}
958
959static void SP_misc_smokestun (Edict* ent)
960{
961 G_SpawnField(ent, "stunsmoke", ET_SMOKESTUN, SOLID_TRIGGER);
962 ent->dmgtype = gi.csi->damStunGas;
964}
965
966static void SP_misc_message (Edict* ent)
967{
968 if (!ent->message) {
969 G_FreeEdict(ent);
970 return;
971 }
972
973 if (!G_ValidMessage(ent))
974 gi.DPrintf("No translation marker for misc_message set\n");
975 ent->use = Message_Use;
976 ent->classname = "misc_message";
977 ent->type = ET_MESSAGE;
978 ent->solid = SOLID_NOT;
979}
980
981#define CAMERA_ROTATE (1 << 8)
982
983static void SP_misc_camera (Edict* ent)
984{
985 /* only used in single player */
986 if (G_IsMultiPlayer()) {
987 G_FreeEdict(ent);
988 return;
989 }
990
991 const bool rotate = ent->spawnflags & CAMERA_ROTATE;
992 G_InitCamera(ent, CAMERA_STATIONARY, ent->angle, rotate);
993}
994
998static void SP_dummy (Edict* ent)
999{
1000 /* particles aren't client-server communicated items
1001 * they are completely client side */
1002 G_FreeEdict(ent);
1003}
1004
1013static void SP_worldspawn (Edict* ent)
1014{
1015 ent->solid = SOLID_BSP;
1016 /* since the world doesn't use G_Spawn() */
1017 ent->inuse = true;
1018 ent->classname = "worldspawn";
1019
1020 level.noEquipment = spawnTemp.noEquipment;
1021 level.noRandomSpawn = spawnTemp.noRandomSpawn;
1022
1023 gi.ConfigString(CS_MAXCLIENTS, "%i", sv_maxclients->integer);
1024
1025 /* only used in multi player */
1026 if (G_IsMultiPlayer()) {
1027 gi.ConfigString(CS_MAXSOLDIERSPERTEAM, "%i", sv_maxsoldiersperteam->integer);
1028 gi.ConfigString(CS_MAXSOLDIERSPERPLAYER, "%i", sv_maxsoldiersperplayer->integer);
1029 gi.ConfigString(CS_ENABLEMORALE, "%i", sv_enablemorale->integer);
1030 gi.ConfigString(CS_MAXTEAMS, "%s", sv_maxteams->string);
1031 }
1032}
entity_t entities[MAX_MAP_ENTITIES]
Definition bspfile.cpp:395
unsigned int key
Definition cl_input.cpp:64
static void SP_light(const localEntityParse_t *entData)
Definition cl_spawn.cpp:259
static const spawn_t spawns[]
Definition cl_spawn.cpp:275
static void SP_worldspawn(const localEntityParse_t *entData)
Definition cl_spawn.cpp:114
static void SP_misc_model(const localEntityParse_t *entData)
Definition cl_spawn.cpp:203
Definition aabb.h:42
vec3_t maxs
Definition aabb.h:258
void setMaxs(const vec3_t maxi)
Definition aabb.h:71
vec3_t mins
Definition aabb.h:257
void getCenter(vec3_t center) const
Calculates the center of the bounding box.
Definition aabb.h:155
float getMinX() const
Definition aabb.h:119
float getMaxX() const
Definition aabb.h:131
void set(const AABB &other)
Copies the values from the given aabb.
Definition aabb.h:60
void setMins(const vec3_t mini)
Definition aabb.h:68
teammask_t visflags
Definition g_edict.h:82
Edict * particleLink
Definition g_edict.h:79
int flags
Definition g_edict.h:169
byte dmgtype
Definition g_edict.h:139
const char * classname
Definition g_edict.h:67
bool active
Definition g_edict.h:177
vec3_t origin
Definition g_edict.h:53
const char * particle
Definition g_edict.h:128
pos3_t pos
Definition g_edict.h:55
float angle
Definition g_edict.h:120
actorSizeEnum_t fieldSize
Definition g_edict.h:141
void(* think)(Edict *self)
Definition g_edict.h:150
const char * target
Definition g_edict.h:125
int HP
Definition g_edict.h:89
const char * noise
Definition g_edict.h:132
byte dir
Definition g_edict.h:86
Edict * groupChain
Definition g_edict.h:167
vec3_t angles
Definition g_edict.h:54
pos3_t * forbiddenListPos
Definition g_edict.h:173
void setActive()
Definition g_edict.h:198
void setTeam(int team_)
Definition g_edict.h:189
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
void calcOrigin()
Calculate the edict's origin vector from it's grid position.
Definition g_edict.h:216
float nextthink
Definition g_edict.h:149
Player & getPlayer() const
Definition g_edict.h:265
int mapNum
Definition g_edict.h:73
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 sounds
Definition g_edict.h:137
int spawnflags
Definition g_edict.h:118
const char * targetname
Definition g_edict.h:126
int time
Definition g_edict.h:136
int count
Definition g_edict.h:135
solid_t solid
Definition g_edict.h:58
int radius
Definition g_edict.h:123
char * group
Definition g_edict.h:104
entity_type_t type
Definition g_edict.h:81
const char * message
Definition g_edict.h:130
void nativeReset()
Definition g_edict.h:195
int number
Definition g_edict.h:51
const char * item
Definition g_edict.h:127
Edict * groupMaster
Definition g_edict.h:168
int speed
Definition g_edict.h:124
edictMaterial_t material
Definition g_edict.h:133
int forbiddenListSize
Definition g_edict.h:175
int frame
Definition g_edict.h:102
const char * description
Definition g_edict.h:131
const char * model
Definition g_edict.h:74
const char * nextmap
Definition g_edict.h:129
A pair of strings representing a key and a value The value string can be trimmed and rendered in the ...
void asVec3(vec3_t vec) const
float asFloat() const
int asInt() const
bool asBool() const
bool isKey(const char *name) const
Definition line.h:31
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
cvar_t * sv_maxclients
Definition g_main.cpp:43
static transfer_t tr
#define PRINT_HUD
Definition defines.h:107
#define MAX_TEAMS
Definition defines.h:98
#define UNIT_HEIGHT
Definition defines.h:122
#define UNIT_SIZE
Definition defines.h:121
#define ACTOR_SIZE_2x2
Definition defines.h:303
#define ACTOR_SIZE_NORMAL
Definition defines.h:302
#define MASK_SMOKE_AND_FIRE
Definition defines.h:276
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition defines.h:294
#define GROUND_DELTA
Definition defines.h:115
#define CONTENTS_WATER
Definition defines.h:226
void G_AddToWayPointList(Edict *ent)
Definition g_ai.cpp:1537
Player * AI_CreatePlayer(int team)
Spawn civilians and aliens.
Definition g_ai.cpp:1944
Artificial Intelligence functions.
void G_InitCamera(Edict *ent, camera_type_t cameraType, float angle, bool rotate)
Definition g_camera.cpp:50
void G_ResetClientData(void)
Called after every player has joined.
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition g_client.cpp:206
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition g_client.cpp:186
Interface for g_client.cpp.
Edict * G_EdictsGetFirst(void)
Returns the first entity.
Definition g_edicts.cpp:98
Edict * G_EdictsGetNewEdict(void)
Find an entity that is not in use.
Definition g_edicts.cpp:141
int G_EdictsGetNumber(const Edict *ent)
Get an entity's ID number.
Definition g_edicts.cpp:60
Edict * G_EdictsGetNextInUse(Edict *lastEnt)
Iterate through the entities that are in use.
Definition g_edicts.cpp:166
void G_EdictsInit(void)
Reset the entity pointers for eg. a new game.
Definition g_edicts.cpp:48
functions to handle the storage and lifecycle of all edicts in the game module.
void G_EventEdictPerish(playermask_t playerMask, const Edict &ent)
Send disappear event.
Definition g_events.cpp:624
void SP_func_breakable(Edict *ent)
func_breakable (0.3 0.3 0.3) ? Used for breakable objects.
Definition g_func.cpp:144
void SP_func_door(Edict *ent)
func_door (0 .5 .8) ? "health" if set, door is destroyable
Definition g_func.cpp:335
void SP_func_door_sliding(Edict *ent)
Definition g_func.cpp:374
void SP_func_rotating(Edict *ent)
Spawns a rotating solid inline brush model.
Definition g_func.cpp:405
func_* edicts
bool G_AddItemToFloor(const pos3_t pos, const char *itemID)
Adds a new item to an existing or new floor container edict at the given grid location.
#define TAG_LEVEL
Definition g_local.h:59
cvar_t * sv_hurtaliens
Definition g_main.cpp:62
#define G_FreeTags(tag)
Definition g_local.h:63
game_locals_t game
Definition g_main.cpp:37
level_locals_t level
Definition g_main.cpp:38
cvar_t * ai_numcivilians
Definition g_main.cpp:73
#define G_IsActor(ent)
Definition g_local.h:127
#define FL_GROUPSLAVE
not the first on the team
Definition g_local.h:294
game_import_t gi
Definition g_main.cpp:39
#define G_TagMalloc(size, tag)
Definition g_local.h:64
cvar_t * sv_enablemorale
Definition g_main.cpp:55
#define G_IsMultiPlayer()
Definition g_local.h:145
#define G_IsSinglePlayer()
Definition g_local.h:146
cvar_t * sv_maxsoldiersperplayer
Definition g_main.cpp:54
cvar_t * ai_multiplayeraliens
Definition g_main.cpp:74
edictMaterial_t
e.g. used for breakable objects
Definition g_local.h:267
cvar_t * sv_maxsoldiersperteam
Definition g_main.cpp:53
cvar_t * sv_maxteams
Definition g_main.cpp:59
bool G_MissionTouch(Edict *self, Edict *activator)
Mission trigger.
Definition g_mission.cpp:53
bool G_MissionUse(Edict *self, Edict *activator)
Mission trigger use function.
void G_MissionThink(Edict *self)
void G_MissionReset(Edict *self, Edict *activator)
void G_MissionAddVictoryMessage(const char *message)
Definition g_mission.cpp:38
Mission related code - king of the hill and so on.
void G_ReactionFireTargetsInit(void)
free function to initialize the reaction fire table for all entities.
Reaction fire system.
static void SP_light(Edict *ent)
light (0 1 0) (-8 -8 -8) (8 8 8)
Definition g_spawn.cpp:647
static void SP_2x2_start(Edict *ent)
info_2x2_start (1 1 0) (-32 -32 -24) (32 32 32) Starting point for a 2x2 unit.
Definition g_spawn.cpp:697
static void G_Actor2x2Spawn(Edict *ent)
Spawn a singleplayer 2x2 unit.
Definition g_spawn.cpp:609
Edict * G_Spawn(const char *classname)
Either finds a free edict, or allocates a new one.
Definition g_spawn.cpp:403
static void SP_civilian_target(Edict *ent)
info_civilian_start (0 1 1) (-16 -16 -24) (16 16 32) Way point for a civilian.
Definition g_spawn.cpp:756
void G_SpawnSmokeField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Spawns a smoke field that is available for some rounds.
Definition g_spawn.cpp:516
static void SP_worldspawn(Edict *ent)
Spawns the world entity.
Definition g_spawn.cpp:1013
void G_SpawnFireField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Definition g_spawn.cpp:521
static bool Message_Use(Edict *self, Edict *activator)
Definition g_spawn.cpp:901
static void SP_human_start(Edict *ent)
info_human_start (1 0 0) (-16 -16 -24) (16 16 32) Starting point for a single player human.
Definition g_spawn.cpp:680
#define G_ValidDescription(ent)
Definition g_spawn.cpp:47
static void SP_misc_item(Edict *ent)
Spawns an item to the ground container.
Definition g_spawn.cpp:887
static void ED_CallSpawn(Edict *ent)
Finds the spawn function for the entity and calls it.
Definition g_spawn.cpp:119
static void G_BuildForbiddenListForEntity(Edict *ent)
Create a list of the grid positions the edict occupies and store it with the edict.
Definition g_spawn.cpp:824
static void SP_misc_model(Edict *ent)
Spawns a misc_model if there is a solid state.
Definition g_spawn.cpp:856
static void SP_dummy(Edict *ent)
a dummy to get rid of local entities
Definition g_spawn.cpp:998
static void SP_misc_message(Edict *ent)
Definition g_spawn.cpp:966
static void SP_player_start(Edict *ent)
info_player_start (1 0 0) (-16 -16 -24) (16 16 32) Starting point for a player. "team" the number of ...
Definition g_spawn.cpp:660
static void SP_misc_mission(Edict *ent)
Initializes the human/phalanx mission entity.
Definition g_spawn.cpp:777
Edict * G_SpawnFloor(const pos3_t pos)
Spawns a new entity at the floor.
Definition g_spawn.cpp:535
static spawn_temp_t spawnTemp
Definition g_spawn.cpp:59
#define MISC_MODEL_SOLID
Definition g_spawn.cpp:852
static void G_SpawnField(Edict *ent, const char *classname, entity_type_t type, solid_t solid)
Definition g_spawn.cpp:922
static void G_ActorSpawn(Edict *ent)
Spawn point for a 1x1 unit.
Definition g_spawn.cpp:571
#define CAMERA_ROTATE
Definition g_spawn.cpp:981
static void ED_ParseField(const char *key, const char *value, Edict *ent)
Takes a key/value pair and sets the binary values in an edict.
Definition g_spawn.cpp:165
void G_SpawnStunSmokeField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Definition g_spawn.cpp:526
static void G_FindEdictGroups(void)
Chain together all entities with a matching team field. All but the first will have the FL_GROUPSLAVE...
Definition g_spawn.cpp:292
Edict * G_SpawnParticle(const vec3_t origin, int spawnflags, const char *particle)
Definition g_spawn.cpp:551
static const char * ED_ParseEdict(const char *data, Edict *ent)
Parses an edict out of the given string, returning the new position.
Definition g_spawn.cpp:245
static void SP_civilian_start(Edict *ent)
info_civilian_start (0 1 1) (-16 -16 -24) (16 16 32) Starting point for a civilian.
Definition g_spawn.cpp:737
static void SP_alien_start(Edict *ent)
info_alien_start (1 0 0) (-16 -16 -24) (16 16 32) Starting point for a single player alien.
Definition g_spawn.cpp:720
static void Think_SmokeAndFire(Edict *self)
Definition g_spawn.cpp:421
static void SP_misc_fire(Edict *ent)
Definition g_spawn.cpp:952
static void G_SpawnFieldGroup(const entity_type_t fieldtype, const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Spawns a field that is available for some rounds.
Definition g_spawn.cpp:482
static void G_SpawnFieldPart(const entity_type_t fieldtype, const vec3_t vec, const char *particle, int rounds, int damage)
Definition g_spawn.cpp:436
static void SP_misc_smokestun(Edict *ent)
Definition g_spawn.cpp:959
static void SP_misc_smoke(Edict *ent)
Definition g_spawn.cpp:944
void G_SpawnEntities(const char *mapname, bool day, const char *entities)
Creates a server's entity / program execution context by parsing textual entity definitions out of an...
Definition g_spawn.cpp:331
static void SP_misc_camera(Edict *ent)
Definition g_spawn.cpp:983
static char * ED_NewString(const char *string)
Allocated memory for the given string in the level context (TAG_LEVEL).
Definition g_spawn.cpp:141
Brings new objects into the world.
#define G_ValidMessage(ent)
Definition g_spawn.h:33
void SP_trigger_touch(Edict *ent)
Touch trigger to call the use function of the attached target.
void SP_trigger_nextmap(Edict *ent)
void SP_trigger_hurt(Edict *ent)
Trigger for grid fields if they are under fire.
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.
Edict * G_GetEdictFromPos(const pos3_t pos, const entity_type_t type)
Searches an edict of the given type at the given grid location.
Definition g_utils.cpp:59
trace_t G_Trace(const Line &trLine, const Edict *passent, int contentmask)
collision detection - this version is more accurate and includes entity tests
Definition g_utils.cpp:265
uint32_t G_GetLevelFlagsFromPos(const pos3_t pos)
Calculates the level flags for a given grid position.
Definition g_utils.cpp:650
void G_FreeEdict(Edict *ent)
Marks the edict as free.
Definition g_utils.cpp:41
Misc utility functions for game module.
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
solid_t
edict->solid values
Definition game.h:153
@ SOLID_TRIGGER
Definition game.h:155
@ SOLID_BBOX
Definition game.h:156
@ SOLID_NOT
Definition game.h:154
@ SOLID_BSP
Definition game.h:157
voidpf void uLong size
Definition ioapi.h:42
voidpf uLong int origin
Definition ioapi.h:45
Info string handling.
int AngleToDir(int angle)
Returns the index of array directionAngles[DIRECTIONS] whose value is the closest to angle.
Definition mathlib.cpp:130
#define PosToVec(p, v)
Pos boundary size is +/- 128 - to get into the positive area we add the possible max negative value a...
Definition mathlib.h:110
#define CORE_DIRECTIONS
Definition mathlib.h:88
#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
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition parse.cpp:107
Shared parsing functions.
#define TEAM_PHALANX
Definition q_shared.h:62
@ CAMERA_STATIONARY
Definition q_shared.h:180
#define CS_MAXSOLDIERSPERPLAYER
Definition q_shared.h:314
#define TEAM_ALIEN
Definition q_shared.h:63
#define CS_MAXSOLDIERSPERTEAM
Definition q_shared.h:313
entity_type_t
Definition q_shared.h:145
@ ET_SOLID
Definition q_shared.h:166
@ ET_CIVILIANTARGET
Definition q_shared.h:161
@ ET_MESSAGE
Definition q_shared.h:167
@ ET_MISSION
Definition q_shared.h:162
@ ET_PARTICLE
Definition q_shared.h:164
@ ET_SMOKE
Definition q_shared.h:168
@ ET_FIRE
Definition q_shared.h:169
@ ET_ACTORSPAWN
Definition q_shared.h:147
@ ET_SMOKESTUN
Definition q_shared.h:170
@ ET_ACTOR2x2SPAWN
Definition q_shared.h:159
@ ET_ITEM
Definition q_shared.h:149
#define CS_MAXTEAMS
Definition q_shared.h:316
#define CS_ENABLEMORALE
Definition q_shared.h:315
#define CS_MAXCLIENTS
Definition q_shared.h:311
#define TEAM_CIVILIAN
Definition q_shared.h:61
#define TEAM_NO_ACTIVE
Definition q_shared.h:60
#define PLAYER_MIN
Definition q_sizes.h:9
#define PLAYER_STAND
Definition q_sizes.h:6
#define PLAYER_WIDTH
Definition q_sizes.h:10
#define PLAYER2x2_WIDTH
Definition q_sizes.h:23
QGL_EXTERN void(APIENTRY *qglActiveTexture)(GLenum texture)
QGL_EXTERN GLsizei const GLvoid * data
Definition r_gl.h:89
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#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
this is only used to hold entity field values that can be set from the editor, but aren't actually pr...
Definition g_spawn.cpp:53
int noRandomSpawn
Definition g_spawn.cpp:55
int noEquipment
Definition g_spawn.cpp:56
pos_t pos3_t[3]
Definition ufotypes.h:58
byte pos_t
Definition ufotypes.h:57
float vec_t
Definition ufotypes.h:37
vec_t vec3_t[3]
Definition ufotypes.h:39
#define VectorDist(a, b)
Definition vector.h:69
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorSet(v, x, y, z)
Definition vector.h:59