UFO: Alien Invasion
Loading...
Searching...
No Matches
g_mission.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_mission.h"
30#include "g_actor.h"
31#include "g_client.h"
32#include "g_edicts.h"
33#include "g_inventory.h"
34#include "g_match.h"
35#include "g_spawn.h"
36#include "g_utils.h"
37
38void G_MissionAddVictoryMessage (const char* message)
39{
40 gi.ConfigString(CS_VICTORY_CONDITIONS, "%s\n", message);
41}
42
43static inline const char* G_MissionGetTeamString (const int team) {
44 return (team == TEAM_PHALANX ? "PHALANX" : (team == TEAM_ALIEN ? "The alien" : va("Team %i's", team)));
45}
46
53bool G_MissionTouch (Edict* self, Edict* activator)
54{
55 if (!G_IsLivingActor(activator))
56 return false;
57
58 Actor* actor = makeActor(activator);
59 const char* const actorTeam = G_MissionGetTeamString(actor->getTeam());
60 if (!G_IsCivilian(actor) && self->isOpponent(actor)) {
61 if (!self->item && self->count) {
62 if (self->targetname) {
63 gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking the %s!"), actorTeam, self->targetname);
64 } else {
65 const char* const teamName = G_MissionGetTeamString(self->getTeam());
66 gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking %s target zone!"),
67 actorTeam, teamName);
68 }
69
70 /* reset king of the hill counter */
71 self->count = 0;
72 }
73 return false;
74 }
75 if (self->count)
76 return false;
77
78 if (self->isSameTeamAs(actor)) {
79 if (!self->item) {
80 linkedList_t* touched = self->touchedList;
81 while (touched) {
82 const Edict* const ent = static_cast<Edict*>(touched->data);
83 if (!self->isSameTeamAs(ent) && !G_IsCivilian(ent) && !G_IsDead(ent)) {
84 return false;
85 }
86 touched = touched->next;
87 }
88 self->count = level.actualRound;
89 if (self->targetname) {
90 gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied the %s!"), actorTeam, self->targetname);
91 } else {
92 gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied their target zone!"), actorTeam);
93 }
94 return true;
95 }
96 }
97
98 /* search the item in the activator's inventory */
99 /* ignore items linked from any temp container the actor must have this in his hands */
100 const Container* cont = nullptr;
101 while ((cont = actor->chr.inv.getNextCont(cont))) {
102 Item* item = nullptr;
103 while ((item = cont->getNextItem(item))) {
104 const objDef_t* od = item->def();
105 /* check whether we found the searched item in the actor's inventory */
106 if (!Q_streq(od->id, self->item))
107 continue;
108
109 /* drop the weapon - even if out of TUs */
110 G_ActorInvMove(actor, cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false);
111 if (self->targetname) {
112 gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed at the %s."), item->def()->name, self->targetname);
113 } else {
114 gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed."), item->def()->name);
115 }
116 self->count = level.actualRound;
117 return true;
118 }
119 }
120
121 return false;
122}
123
124void G_MissionReset (Edict* self, Edict* activator)
125{
126 /* Don't reset the mission timer for 'bring item' missions G_MissionThink will handle that */
127 if (!self->time || self->item)
128 return;
129 linkedList_t* touched = self->touchedList;
130 while (touched) {
131 const Edict* const ent = static_cast<Edict*>(touched->data);
132 if (self->isSameTeamAs(ent) && !(G_IsDead(ent) || ent == activator)) {
133 return;
134 }
135 touched = touched->next;
136 }
137 if (activator->getTeam() == self->getTeam()) {
138 const char* const actTeam = G_MissionGetTeamString(activator->getTeam());
139 if (self->targetname)
140 gi.BroadcastPrintf(PRINT_HUD, _("%s forces have left the %s!"), actTeam, self->targetname);
141 else
142 gi.BroadcastPrintf(PRINT_HUD, _("%s forces have left their target zone!"), actTeam);
143 }
144 /* All team actors are gone, reset counter */
145 self->count = 0;
146}
147
151bool G_MissionUse (Edict* self, Edict* activator)
152{
153 Edict* target = G_EdictsFindTargetEntity(self->target);
154 if (!target) {
155 gi.DPrintf("Target '%s' wasn't found for misc_mission\n", self->target);
156 G_FreeEdict(self);
157 return false;
158 }
159
160 if (target->destroy) {
161 /* set this to zero to determine that this is a triggered destroy call */
162 target->HP = 0;
163 target->destroy(target);
164 /* freed when the level changes */
165 self->target = nullptr;
166 self->use = nullptr;
167 } else if (target->use)
168 target->use(target, activator);
169
170 return true;
171}
172
173static bool G_MissionIsTouched (Edict* self) {
174 linkedList_t* touched = self->touchedList;
175 while (touched) {
176 const Edict* const ent = static_cast<Edict*>(touched->data);
177 if (self->isSameTeamAs(ent) && !G_IsDead(ent))
178 return true;
179 touched = touched->next;
180 }
181 return false;
182}
183
189{
190 if (!G_MatchIsRunning())
191 return;
192
193 /* when every player has joined the match - spawn the mission target
194 * particle (if given) to mark the trigger */
195 if (self->particle) {
196 self->link = G_SpawnParticle(self->origin, self->spawnflags, self->particle);
197
198 /* This is automatically freed on map shutdown */
199 self->particle = nullptr;
200 }
201
202 Edict* chain = self->groupMaster;
203 if (!chain)
204 chain = self;
205 while (chain) {
206 if (chain->type == ET_MISSION) {
207 if (chain->item) {
208 const Item* ic;
209 G_GetFloorItems(chain);
210 ic = chain->getFloor();
211 if (!ic) {
212 /* reset the counter if there is no item */
213 chain->count = 0;
214 return;
215 }
216 for (; ic; ic = ic->getNext()) {
217 const objDef_t* od = ic->def();
218 assert(od);
219 /* not the item we are looking for */
220 if (Q_streq(od->id, chain->item))
221 break;
222 }
223 if (!ic) {
224 /* reset the counter if it's not the searched item */
225 chain->count = 0;
226 return;
227 }
228 }
229 if (chain->time) {
230 /* Check that the target zone is still occupied (last defender might have died) */
231 if (!chain->item && !G_MissionIsTouched(chain)) {
232 chain->count = 0;
233 }
234 const int endTime = level.actualRound - chain->count;
235 const int spawnIndex = (chain->getTeam() + level.teamOfs) % MAX_TEAMS;
236 const int currentIndex = (level.activeTeam + level.teamOfs) % MAX_TEAMS;
237 /* not every edict in the group chain has
238 * been occupied long enough */
239 if (!chain->count || endTime < chain->time ||
240 (endTime == chain->time && spawnIndex < currentIndex))
241 return;
242 }
243 if (chain->target && !chain->time && !chain->item) {
244 if (!G_MissionIsTouched(chain))
245 return;
246 }
247 }
248 chain = chain->groupChain;
249 }
250
251 const bool endMission = self->target == nullptr;
252
253 /* store team before the edict is released */
254 const int team = self->getTeam();
255 chain = self->groupMaster;
256 if (!chain)
257 chain = self;
258 while (chain) {
259 if (chain->type == ET_MISSION) {
260 if (chain->item != nullptr) {
261 Edict* item = G_GetEdictFromPos(chain->pos, ET_ITEM);
262 if (item != nullptr) {
263 if (!G_InventoryRemoveItemByID(chain->item, item, CID_FLOOR)) {
264 Com_Printf("Could not remove item '%s' from floor edict %i\n", chain->item, item->getIdNum());
265 } else if (!item->getFloor()) {
266 G_EventPerish(*item);
267 G_FreeEdict(item);
268 }
269 }
270 }
271 if (chain->link != nullptr) {
272 Edict* particle = G_GetEdictFromPos(chain->pos, ET_PARTICLE);
273 if (particle != nullptr) {
274 G_AppearPerishEvent(G_VisToPM(particle->visflags), false, *particle, nullptr);
275 G_FreeEdict(particle);
276 }
277 chain->link = nullptr;
278 }
279
280 /* Display mission message */
281 if (G_ValidMessage(chain)) {
282 const char* msg = chain->message;
283 if (msg[0] == '_')
284 ++msg;
285 gi.BroadcastPrintf(PRINT_HUD, "%s", msg);
286 }
287 }
288
289 Edict* ent = chain->groupChain;
290 /* free the group chain (G_MissionUse() will free the edict if it fails) */
291 if (G_UseEdict(chain, nullptr))
292 G_FreeEdict(chain);
293 chain = ent;
294 }
295
296 if (endMission)
297 G_MatchEndTrigger(team, level.activeTeam == TEAM_ALIEN ? 10 : 3);
298}
#define _(String)
Definition cl_shared.h:44
#define INVDEF(containerID)
Definition cl_shared.h:48
An Edict of type Actor.
Definition g_edict.h:348
Item * getNextItem(const Item *prev) const
const invDef_t * def() const
teammask_t visflags
Definition g_edict.h:82
bool isSameTeamAs(const Edict *other) const
Definition g_edict.h:278
const Edict * link
Definition g_edict.h:80
character_t chr
Definition g_edict.h:116
int getIdNum() const
Definition g_edict.h:231
vec3_t origin
Definition g_edict.h:53
const char * particle
Definition g_edict.h:128
pos3_t pos
Definition g_edict.h:55
const char * target
Definition g_edict.h:125
int HP
Definition g_edict.h:89
Edict * groupChain
Definition g_edict.h:167
bool(* use)(Edict *self, Edict *activator)
Definition g_edict.h:154
int getTeam() const
Definition g_edict.h:269
bool(* destroy)(Edict *self)
Definition g_edict.h:155
bool isOpponent(const Actor *actor) const
Check if given actor is an enemy.
Definition g_edicts.cpp:383
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
linkedList_t * touchedList
Definition g_edict.h:157
entity_type_t type
Definition g_edict.h:81
const char * message
Definition g_edict.h:130
const char * item
Definition g_edict.h:127
Item * getFloor() const
Definition g_edict.h:262
Edict * groupMaster
Definition g_edict.h:168
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * def(void) const
Definition inv_shared.h:469
Item * getNext() const
Definition inv_shared.h:451
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define PRINT_HUD
Definition defines.h:107
#define MAX_TEAMS
Definition defines.h:98
#define NONE
Definition defines.h:68
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition g_actor.cpp:43
bool G_ActorInvMove(Actor *actor, const invDef_t *fromContType, Item *fItem, const invDef_t *toContType, int tx, int ty, bool checkaction)
Moves an item inside an inventory. Floors are handled special.
Definition g_actor.cpp:506
#define G_IsDead(ent)
Definition g_actor.h:34
void G_AppearPerishEvent(playermask_t playerMask, bool appear, Edict &check, const Edict *ent)
Send the appear or perish event to the affected clients.
Definition g_client.cpp:245
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition g_client.cpp:186
Interface for g_client.cpp.
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_EventPerish(const Edict &ent)
Send an event to all clients that are seeing the given edict, that it just has disappeared.
Definition g_events.cpp:158
bool G_InventoryRemoveItemByID(const char *itemID, Edict *ent, containerIndex_t container)
Removes one particular item from a given container.
Edict * G_GetFloorItems(Edict *ent)
Prepares a list of items on the floor at given entity position.
level_locals_t level
Definition g_main.cpp:38
game_import_t gi
Definition g_main.cpp:39
#define G_IsCivilian(ent)
Definition g_local.h:148
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
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time).
Definition g_match.cpp:320
Match related functions.
static const char * G_MissionGetTeamString(const int team)
Definition g_mission.cpp:43
bool G_MissionTouch(Edict *self, Edict *activator)
Mission trigger.
Definition g_mission.cpp:53
static bool G_MissionIsTouched(Edict *self)
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.
Edict * G_SpawnParticle(const vec3_t origin, int spawnflags, const char *particle)
Definition g_spawn.cpp:551
Brings new objects into the world.
#define G_ValidMessage(ent)
Definition g_spawn.h:33
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
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.
#define CID_FLOOR
Definition inv_shared.h:55
#define TEAM_PHALANX
Definition q_shared.h:62
#define TEAM_ALIEN
Definition q_shared.h:63
@ ET_MISSION
Definition q_shared.h:162
@ ET_PARTICLE
Definition q_shared.h:164
@ ET_ITEM
Definition q_shared.h:149
#define CS_VICTORY_CONDITIONS
Definition q_shared.h:323
#define Q_streq(a, b)
Definition shared.h:136
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
Inventory inv
Definition chr_shared.h:411
void * data
Definition list.h:31
linkedList_t * next
Definition list.h:32
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
const char * id
Definition inv_shared.h:268
const char * name
Definition inv_shared.h:267