1 /**
2  * @file
3  */
4 
5 /*
6 Copyright (C) 2002-2013 UFO: Alien Invasion.
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 See the GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 
23 */
24 
25 #include "g_local.h"	/* also includes g_event.h */
26 #include "g_actor.h"
27 #include "g_client.h"
28 #include "g_edicts.h"
29 #include "g_inventory.h"
30 
31 /**
32  * @brief Spawns a sound (that will be spatialized on the client side)
33  * @param playerMask A bit mask. One bit for each affected player
34  * @param instant Whether the sound should be played instantly rather than queued
35  * @param ent The edict that is causing the sound
36  * @param origin The origin of the sound
37  * @param sound The sound file, path relative to sounds/. If there is a + at the end the client will
38  * choose a random sound. See the event function for more information.
39  * of the path, a random sound will be taken.
40  */
G_EventSpawnSound(playermask_t playerMask,const Edict & ent,const vec3_t origin,const char * sound)41 void G_EventSpawnSound (playermask_t playerMask, const Edict &ent, const vec3_t origin, const char* sound)
42 {
43 	G_EventAdd(playerMask, EV_SOUND, ent.number);
44 
45 	/* use the entity origin unless it is a bmodel or explicitly specified */
46 	if (!origin) {
47 		if (ent.solid == SOLID_BSP) {
48 			vec3_t origin_v;
49 			VectorCenterFromMinsMaxs(ent.mins, ent.maxs, origin_v);
50 			VectorAdd(ent.origin, origin_v, origin_v);
51 			gi.WritePos(origin);
52 		} else {
53 			gi.WritePos(vec3_origin);
54 		}
55 	} else {
56 		gi.WritePos(origin);
57 	}
58 	gi.WriteByte(0xFF);
59 	gi.WriteString(sound);
60 	G_EventEnd();
61 }
62 
G_EventSpawnFootstepSound(const Edict & ent,const char * sound)63 void G_EventSpawnFootstepSound (const Edict &ent, const char* sound)
64 {
65 	const playermask_t playerMask = ~G_VisToPM(ent.visflags);
66 	gi.QueueEvent(playerMask, EV_SOUND, ent.number);
67 	gi.QueueWritePos(ent.origin);
68 	gi.QueueWriteByte(ent.moveinfo.steps);
69 	gi.QueueWriteString(sound);
70 }
71 
72 /**
73  * @brief Send the turn event for the given entity
74  * @param ent The entity to send the turn event for
75  * @note Every player that can see this ent will reveive the turn event data
76  * @note Make sure that the direction to turn into is already set
77  */
G_EventActorTurn(const Edict & ent)78 void G_EventActorTurn (const Edict &ent)
79 {
80 	G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_TURN, ent.number);
81 	gi.WriteByte(ent.dir);
82 	G_EventEnd();
83 }
84 
85 /**
86  * @brief Announce the actor die event for the clients that are seeing the actor
87  * @param[in] ent The actor that is dying
88  */
G_EventActorDie(const Edict & ent,bool attacker)89 void G_EventActorDie (const Edict &ent, bool attacker)
90 {
91 	G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_DIE, ent.number);
92 	gi.WriteShort(ent.state);
93 	gi.WriteByte(ent.pnum);
94 	gi.WriteByte(attacker);
95 	G_EventEnd();
96 }
97 
98 /**
99  * @brief Announce the actor die event for the clients that are seeing the actor
100  * @param[in] ent The actor that was healed and woke up again
101  */
G_EventActorRevitalise(const Edict & ent)102 void G_EventActorRevitalise (const Edict &ent)
103 {
104 	G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_REVITALISED, ent.number);
105 	gi.WriteShort(ent.state);
106 	G_EventEnd();
107 }
108 
109 /**
110  * @brief Will inform the player about the real TU reservation
111  * @param ent The actors edict.
112  */
G_EventActorSendReservations(const Edict & ent)113 void G_EventActorSendReservations (const Edict &ent)
114 {
115 	G_EventAdd(G_PlayerToPM(ent.getPlayer()), EV_ACTOR_RESERVATIONCHANGE, ent.number);
116 	const chrReservations_t &reservedTUs = ent.chr.reservedTus;
117 	gi.WriteShort(reservedTUs.reaction);
118 	gi.WriteShort(reservedTUs.shot);
119 	gi.WriteShort(reservedTUs.crouch);
120 
121 	G_EventEnd();
122 }
123 
124 /**
125  * @brief Tell the client to remove the item from the container
126  * @param[in] ent Pointer to entity having given inventory.
127  * @param[in] playerMask The player mask to determine which clients should receive the event (e.g. @c G_VisToPM(ent->visflags))
128  * @param[in] containerId Id of the container the item is in.
129  * @param[in] x Position of item in container.
130  * @param[in] y Position of item in container.
131  */
G_EventInventoryDelete(const Edict & ent,playermask_t playerMask,const containerIndex_t containerId,int x,int y)132 void G_EventInventoryDelete (const Edict &ent, playermask_t playerMask, const containerIndex_t containerId, int x, int y)
133 {
134 	G_EventAdd(playerMask, EV_INV_DEL, ent.number);
135 	gi.WriteByte(containerId);
136 	gi.WriteByte(x);
137 	gi.WriteByte(y);
138 	G_EventEnd();
139 }
140 
141 /**
142  * @brief Tell the client to add the item from the container
143  * @param[in] ent Pointer to entity having given inventory.
144  * @param[in] playerMask The player mask to determine which clients should receive the event (e.g. @c G_VisToPM(ent->visflags))
145  * @param[in] itemAmount How many items to add.
146  * @note This event must be followed by a @c G_WriteItem call
147  */
G_EventInventoryAdd(const Edict & ent,playermask_t playerMask,int itemAmount)148 void G_EventInventoryAdd (const Edict &ent, playermask_t playerMask, int itemAmount)
149 {
150 	G_EventAdd(playerMask, EV_INV_ADD, ent.number);
151 	gi.WriteShort(itemAmount);
152 	/* do not end the pending events here - this is just a header, the items are following */
153 }
154 
155 /**
156  * @brief Send an event to all clients that are seeing the given edict, that it just has disappeared
157  * @param ent The edict that disappeared
158  */
G_EventPerish(const Edict & ent)159 void G_EventPerish (const Edict &ent)
160 {
161 	G_EventEdictPerish(G_VisToPM(ent.visflags), ent);
162 }
163 
164 /**
165  * @brief Unregister an edict at the client
166  * @param ent The edict to unregister
167  */
G_EventDestroyEdict(const Edict & ent)168 void G_EventDestroyEdict (const Edict &ent)
169 {
170 	assert(ent.inuse);
171 	G_EventAdd(PM_ALL, EV_ENT_DESTROY, ent.number);
172 	G_EventEnd();
173 }
174 
175 /**
176  * @brief Change the amount of available ammo for the given entity
177  * @param ent The entity to change the amount of ammo for
178  * @param ammo The ammo to change
179  * @param amount The new amount of the left ammo
180  * @param shootType The shooting type to determine which container to use
181  */
G_EventInventoryAmmo(const Edict & ent,const objDef_t * ammo,int amount,shoot_types_t shootType)182 void G_EventInventoryAmmo (const Edict &ent, const objDef_t* ammo, int amount, shoot_types_t shootType)
183 {
184 	G_EventAdd(G_VisToPM(ent.visflags), EV_INV_AMMO, ent.number);
185 	gi.WriteByte(amount);
186 	gi.WriteByte(ammo->idx);
187 	if (IS_SHOT_RIGHT(shootType))
188 		gi.WriteByte(CID_RIGHT);
189 	else
190 		gi.WriteByte(CID_LEFT);
191 	/* x and y value */
192 	gi.WriteByte(0);
193 	gi.WriteByte(0);
194 	G_EventEnd();
195 }
196 
197 /**
198  * @brief Start the shooting event
199  * @param ent The entity that starts the shooting
200  * @param teamMask the vis mask of the teams to determine the clients from this event is send to
201  * @param shootType The type of the shoot
202  * @param at The grid position to target to
203  */
G_EventStartShoot(const Edict & ent,teammask_t teamMask,shoot_types_t shootType,const pos3_t at)204 void G_EventStartShoot (const Edict &ent, teammask_t teamMask, shoot_types_t shootType, const pos3_t at)
205 {
206 	G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_START_SHOOT, ent.number);
207 	gi.WriteByte(shootType);
208 	gi.WriteGPos(ent.pos);
209 	gi.WriteGPos(at);
210 	G_EventEnd();
211 }
212 
213 /**
214  * @brief Start the shooting event
215  * @param ent The entity that starts the shooting
216  * @param teamMask the vis mask of the teams to determine the clients from this event is send to
217  */
G_EventEndShoot(const Edict & ent,teammask_t teamMask)218 void G_EventEndShoot (const Edict &ent, teammask_t teamMask)
219 {
220 	G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_END_SHOOT, ent.number);
221 	G_EventEnd();
222 }
223 
224 /**
225  * @brief Start the shooting event for hidden actors
226  * @param teamMask the vis mask to determine the clients from this event is send to
227  * @param fd The firedefinition to use for the shoot
228  * @param firstShoot Is this the first shoot
229  */
G_EventShootHidden(teammask_t teamMask,const fireDef_t * fd,bool firstShoot)230 void G_EventShootHidden (teammask_t teamMask, const fireDef_t* fd, bool firstShoot)
231 {
232 	G_EventAdd(~G_VisToPM(teamMask), EV_ACTOR_SHOOT_HIDDEN, -1);
233 	gi.WriteByte(firstShoot);
234 	gi.WriteShort(fd->obj->idx);
235 	gi.WriteByte(fd->weapFdsIdx);
236 	gi.WriteByte(fd->fdIdx);
237 	G_EventEnd();
238 }
239 
240 /**
241  * @brief Do the shooting
242  * @param ent The entity that is doing the shooting
243  * @param teamMask the vis mask to determine the clients from this event is send to
244  * @param fd The firedefinition to use for the shoot
245  * @param firstShoot Is this the first shoot
246  * @param shootType The type of the shoot
247  * @param flags Define some flags in a bitmask: @c SF_BODY, @c SF_IMPACT, @c SF_BOUNCING and @c SF_BOUNCING
248  * @param trace The trace what was used to determine whether this shot has hit something
249  * @param from The position the entity shoots from
250  * @param impact The impact world vector for the shot
251  */
G_EventShoot(const Edict & ent,teammask_t teamMask,const fireDef_t * fd,bool firstShoot,shoot_types_t shootType,int flags,const trace_t * trace,const vec3_t from,const vec3_t impact)252 void G_EventShoot (const Edict &ent, teammask_t teamMask, const fireDef_t* fd, bool firstShoot, shoot_types_t shootType, int flags, const trace_t* trace, const vec3_t from, const vec3_t impact)
253 {
254 	const Edict* targetEdict = G_EdictsGetByNum(trace->entNum);	/* the ent possibly hit by the trace */
255 
256 	G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_SHOOT, ent.number);
257 	if (targetEdict && G_IsBreakable(targetEdict))
258 		gi.WriteShort(targetEdict->number);
259 	else
260 		gi.WriteShort(SKIP_LOCAL_ENTITY);
261 	gi.WriteByte(firstShoot ? 1 : 0);
262 	gi.WriteShort(fd->obj->idx);
263 	gi.WriteByte(fd->weapFdsIdx);
264 	gi.WriteByte(fd->fdIdx);
265 	gi.WriteByte(shootType);
266 	gi.WriteByte(flags);
267 	gi.WriteByte(trace->contentFlags);
268 	gi.WritePos(from);
269 	gi.WritePos(impact);
270 	gi.WriteDir(trace->plane.normal);
271 	G_EventEnd();
272 }
273 
G_EventReactionFireChange(const Edict & ent)274 void G_EventReactionFireChange (const Edict &ent)
275 {
276 	const FiremodeSettings &fireMode = ent.chr.RFmode;
277 	const objDef_t* od = fireMode.getWeapon();
278 
279 	G_EventAdd(G_PlayerToPM(ent.getPlayer()), EV_ACTOR_REACTIONFIRECHANGE, ent.number);
280 	gi.WriteByte(fireMode.getFmIdx());
281 	gi.WriteByte(fireMode.getHand());
282 	gi.WriteShort(od ? od->idx : NONE);
283 
284 	G_EventEnd();
285 }
286 
G_EventReactionFireAddTarget(const Edict & shooter,const Edict & target,int tus,int step)287 void G_EventReactionFireAddTarget (const Edict &shooter, const Edict &target, int tus, int step)
288 {
289 	gi.QueueEvent(G_PlayerToPM(shooter.getPlayer()), EV_ACTOR_REACTIONFIREADDTARGET, shooter.number);
290 	gi.QueueWriteShort(target.number);
291 	gi.QueueWriteByte(tus);
292 	gi.QueueWriteByte(step);
293 }
294 
G_EventReactionFireRemoveTarget(const Edict & shooter,const Edict & target,int step)295 void G_EventReactionFireRemoveTarget (const Edict &shooter, const Edict &target, int step)
296 {
297 	gi.QueueEvent(G_PlayerToPM(shooter.getPlayer()), EV_ACTOR_REACTIONFIREREMOVETARGET, shooter.number);
298 	gi.QueueWriteShort(target.number);
299 	gi.QueueWriteByte(step);
300 }
301 
G_EventReactionFireTargetUpdate(const Edict & shooter,const Edict & target,int tus,int step)302 void G_EventReactionFireTargetUpdate (const Edict &shooter, const Edict &target, int tus, int step)
303 {
304 	gi.QueueEvent(G_PlayerToPM(shooter.getPlayer()), EV_ACTOR_REACTIONFIRETARGETUPDATE, shooter.number);
305 	gi.QueueWriteShort(target.number);
306 	gi.QueueWriteByte(tus);
307 	gi.QueueWriteByte(step);
308 }
309 
310 /**
311  * @brief Spawn a new particle for the client
312  * @param[in] playerMask A bit mask. One bit for each affected player
313  * @param[in] name The id of the particle (see ptl_*.ufo script files in base/ufos)
314  * @param[in] levelFlags Show at which levels
315  * @param[in] s starting/location vector
316  * @param[in] v velocity vector
317  * @param[in] a acceleration vector
318  */
G_EventParticleSpawn(playermask_t playerMask,const char * name,int levelFlags,const vec3_t s,const vec3_t v,const vec3_t a)319 void G_EventParticleSpawn (playermask_t playerMask, const char* name, int levelFlags, const vec3_t s, const vec3_t v, const vec3_t a)
320 {
321 	G_EventAdd(playerMask, EV_PARTICLE_SPAWN, -1);
322 	gi.WriteByte(levelFlags);
323 	gi.WritePos(s);
324 	gi.WritePos(v);
325 	gi.WritePos(a);
326 	gi.WriteString(name);
327 	G_EventEnd();
328 }
329 
G_EventActorFall(const Edict & ent)330 void G_EventActorFall (const Edict &ent)
331 {
332 	G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_MOVE, ent.number);
333 	gi.WriteByte(1);
334 	gi.WriteByte(ent.pos[0]);
335 	gi.WriteByte(ent.pos[1]);
336 	gi.WriteByte(ent.pos[2]);
337 	gi.WriteByte(makeDV(DIRECTION_FALL, ent.pos[2]));
338 	gi.WriteShort(GRAVITY);
339 	gi.WriteShort(0);
340 	G_EventEnd();
341 }
342 
343 /**
344  * @brief Informs the client that an interaction with the world is possible
345  * @note It's assumed that the clientAction is already set
346  * @param[in] ent The edict that can execute the action (an actor)
347  */
G_EventSetClientAction(const Edict & ent)348 void G_EventSetClientAction (const Edict &ent)
349 {
350 	assert(ent.clientAction);
351 	assert(ent.clientAction->flags & FL_CLIENTACTION);
352 
353 	/* tell the hud to show the door buttons */
354 	G_EventAdd(G_TeamToPM(ent.team), EV_CLIENT_ACTION, ent.number);
355 	gi.WriteShort(ent.clientAction->number);
356 	G_EventEnd();
357 }
358 
359 /**
360  * @brief Reset the client actions for the given entity
361  * @param[in] ent The entity to reset the client action for
362  * @note This event is send to the player this edict belongs to
363  */
G_EventResetClientAction(const Edict & ent)364 void G_EventResetClientAction (const Edict &ent)
365 {
366 	const int playerMask = G_PlayerToPM(ent.getPlayer());
367 	G_EventAdd(playerMask, EV_RESET_CLIENT_ACTION, ent.number);
368 	G_EventEnd();
369 }
370 
G_EventActorStats(const Edict & ent,playermask_t playerMask)371 void G_EventActorStats (const Edict &ent, playermask_t playerMask)
372 {
373 	G_EventAdd(playerMask, EV_ACTOR_STATS, ent.number);
374 	gi.WriteByte(ent.TU);
375 	gi.WriteShort(ent.HP);
376 	gi.WriteByte(ent.STUN);
377 	gi.WriteByte(ent.morale);
378 	G_EventEnd();
379 }
380 
381 /**
382  * @brief Send info about an actor's wounds to the client.
383  * @param[in] ent The actor whose wound status we are sending.
384  * @param[in] bodyPart The body part index we are sending wound info about.
385  * @note This event is sent to the player this actor belongs to
386  */
G_EventActorWound(const Edict & ent,const int bodyPart)387 void G_EventActorWound (const Edict &ent, const int bodyPart)
388 {
389 	const int mask = G_PlayerToPM(ent.getPlayer());
390 	G_EventAdd(mask, EV_ACTOR_WOUND, ent.number);
391 	gi.WriteByte(bodyPart);
392 	const woundInfo_t &wounds = ent.chr.wounds;
393 	gi.WriteByte(wounds.woundLevel[bodyPart]);
394 	gi.WriteByte(wounds.treatmentLevel[bodyPart]);
395 	G_EventEnd();
396 }
397 
398 /**
399  * @brief End of turn event for the current active team
400  * @note This event is send to every connected client
401  */
G_EventEndRound(void)402 void G_EventEndRound (void)
403 {
404 	G_EventAdd(PM_ALL, EV_ENDROUND, -1);
405 	gi.WriteByte(level.activeTeam);
406 	G_EventEnd();
407 }
408 
G_EventInventoryReload(const Edict & ent,playermask_t playerMask,const Item * item,const invDef_t * invDef,const Item * ic)409 void G_EventInventoryReload (const Edict &ent, playermask_t playerMask, const Item* item, const invDef_t* invDef, const Item* ic)
410 {
411 	G_EventAdd(playerMask, EV_INV_RELOAD, ent.number);
412 	gi.WriteByte(item->def()->ammo);
413 	gi.WriteByte(item->ammoDef()->idx);
414 	gi.WriteByte(invDef->id);
415 	gi.WriteByte(ic->getX());
416 	gi.WriteByte(ic->getY());
417 	G_EventEnd();
418 }
419 
420 /**
421  * @param[in] teamMask the vis mask to determine the clients from this event is send to
422  * @param[in] fd The firedefinition to use
423  * @param[in] dt Delta time
424  * @param[in] flags bitmask of the following values: @c SF_BODY, @c SF_IMPACT, @c SF_BOUNCING and @c SF_BOUNCED
425  * @param[in] position The current position
426  * @param[in] velocity The velocity of the throw
427  */
G_EventThrow(teammask_t teamMask,const fireDef_t * fd,float dt,byte flags,const vec3_t position,const vec3_t velocity)428 void G_EventThrow (teammask_t teamMask, const fireDef_t* fd, float dt, byte flags, const vec3_t position, const vec3_t velocity)
429 {
430 	G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_THROW, -1);
431 	gi.WriteShort(dt * 1000);
432 	gi.WriteShort(fd->obj->idx);
433 	gi.WriteByte(fd->weapFdsIdx);
434 	gi.WriteByte(fd->fdIdx);
435 	gi.WriteByte(flags);
436 	gi.WritePos(position);
437 	gi.WritePos(velocity);
438 	G_EventEnd();
439 }
440 
441 /**
442  * @brief Send the bounding box info to the client.
443  * @param[in] ent The edict to send the bounding box for
444  */
G_EventSendEdict(const Edict & ent)445 void G_EventSendEdict (const Edict &ent)
446 {
447 	G_EventAdd(PM_ALL, EV_ADD_EDICT, ent.number);
448 	gi.WriteByte(ent.type);
449 	gi.WritePos(ent.absmin);
450 	gi.WritePos(ent.absmax);
451 	G_EventEnd();
452 }
453 
G_EventSendState(playermask_t playerMask,const Edict & ent)454 void G_EventSendState (playermask_t playerMask, const Edict &ent)
455 {
456 	G_EventActorStateChange(playerMask & G_TeamToPM(ent.team), ent);
457 
458 	G_EventAdd(playerMask & ~G_TeamToPM(ent.team), EV_ACTOR_STATECHANGE, ent.number);
459 	gi.WriteShort(ent.state & STATE_PUBLIC);
460 	G_EventEnd();
461 }
462 
463 /**
464  * @brief Centers the view for all clients that are seeing the given edict on the world position of the edict
465  * @param[in] ent The edict to use the position from
466  */
G_EventCenterView(const Edict & ent)467 void G_EventCenterView (const Edict &ent)
468 {
469 	G_EventCenterViewAt(G_VisToPM(ent.visflags), ent.pos);
470 }
471 
472 /**
473  * @brief Centers the view for all clients that are seeing the given edict on the world position of the edict
474  * @param playerMask The clients that should see the edict
475  * @param pos The position to center the view
476  */
G_EventCenterViewAt(playermask_t playerMask,const pos3_t pos)477 void G_EventCenterViewAt (playermask_t playerMask, const pos3_t pos)
478 {
479 	G_EventAdd(playerMask, EV_CENTERVIEW, -1);
480 	gi.WriteGPos(pos);
481 	G_EventEnd();
482 }
483 
484 /**
485  * @brief Centers the view for all clients that are seeing the given edict on the world position of the edict
486  * @param[in] playerMask The clients that should see the edict
487  * @param[in] pos The position to center the view
488  */
G_EventMoveCameraTo(playermask_t playerMask,const pos3_t pos)489 void G_EventMoveCameraTo (playermask_t playerMask, const pos3_t pos)
490 {
491 	G_EventAdd(playerMask, EV_MOVECAMERA, -1);
492 	gi.WriteGPos(pos);
493 	G_EventEnd();
494 }
495 
496 /**
497  * @sa CL_ActorAdd
498  */
G_EventActorAdd(playermask_t playerMask,const Edict & ent)499 void G_EventActorAdd (playermask_t playerMask, const Edict &ent)
500 {
501 	G_EventAdd(playerMask, EV_ACTOR_ADD, ent.number);
502 	gi.WriteByte(ent.team);
503 	gi.WriteByte(ent.chr.teamDef ? ent.chr.teamDef->idx : NONE);
504 	gi.WriteByte(ent.chr.gender);
505 	gi.WriteByte(ent.pnum);
506 	gi.WriteGPos(ent.pos);
507 	gi.WriteShort(ent.state & STATE_PUBLIC);
508 	gi.WriteByte(ent.fieldSize);
509 	G_EventEnd();
510 }
511 
512 /**
513  * Send a particle spawn event to the client
514  * @param[in] playerMask The clients that should see the particle
515  * @param[in] ent The particle to spawn
516  */
G_EventSendParticle(playermask_t playerMask,const Edict & ent)517 void G_EventSendParticle (playermask_t playerMask, const Edict &ent)
518 {
519 	G_EventAdd(playerMask, EV_PARTICLE_APPEAR, ent.number);
520 	gi.WriteShort(ent.spawnflags);
521 	gi.WritePos(ent.origin);
522 	gi.WriteString(ent.particle);
523 	G_EventEnd();
524 }
525 
526 /**
527  * @brief Send an appear event to the client.
528  * @param playerMask The players to send the event to
529  * @param ent The camera that should appear to the players included in the given mask.
530  */
G_EventCameraAppear(playermask_t playerMask,const Edict & ent)531 void G_EventCameraAppear (playermask_t playerMask, const Edict &ent)
532 {
533 	G_EventAdd(playerMask, EV_CAMERA_APPEAR, ent.number);
534 	gi.WritePos(ent.origin);
535 	gi.WriteByte(ent.team);
536 	gi.WriteByte(ent.dir);
537 	gi.WriteByte(ent.camera.cameraType);
538 	/* strip the higher bits - only send levelflags */
539 	gi.WriteByte(ent.spawnflags & 0xFF);
540 	gi.WriteByte(ent.camera.rotate);
541 	G_EventEnd();
542 }
543 
544 /**
545  * @brief Send an appear event to the client.
546  * @param playerMask The players to send the event to
547  * @param ent The edict that should appear to the players included in the given mask.
548  * @note Each following event that is relying on the fact that this edict must already
549  * be known in the client, must also adopt the client side parsing of the event times.
550  */
G_EventEdictAppear(playermask_t playerMask,const Edict & ent)551 void G_EventEdictAppear (playermask_t playerMask, const Edict &ent)
552 {
553 	G_EventAdd(playerMask, EV_ENT_APPEAR, ent.number);
554 	gi.WriteByte(ent.type);
555 	gi.WriteGPos(ent.pos);
556 	G_EventEnd();
557 }
558 
G_EventActorAppear(playermask_t playerMask,const Edict & check,const Edict * ent)559 void G_EventActorAppear (playermask_t playerMask, const Edict &check, const Edict* ent)
560 {
561 	const int mask = G_TeamToPM(check.team) & playerMask;
562 
563 	/* parsed in CL_ActorAppear */
564 	G_EventAdd(playerMask, EV_ACTOR_APPEAR, check.number);
565 	gi.WriteShort(ent && ent->number > 0 ? ent->number : SKIP_LOCAL_ENTITY);
566 	gi.WriteByte(check.team);
567 	gi.WriteByte(check.chr.teamDef ? check.chr.teamDef->idx : NONE);
568 	gi.WriteByte(check.chr.gender);
569 	gi.WriteShort(check.chr.ucn);
570 	gi.WriteByte(check.pnum);
571 	gi.WriteGPos(check.pos);
572 	gi.WriteByte(check.dir);
573 	if (check.getRightHandItem()) {
574 		gi.WriteShort(check.getRightHandItem()->def()->idx);
575 	} else {
576 		gi.WriteShort(NONE);
577 	}
578 
579 	if (check.getLeftHandItem()) {
580 		gi.WriteShort(check.getLeftHandItem()->def()->idx);
581 	} else {
582 		gi.WriteShort(NONE);
583 	}
584 
585 	if (check.body == 0 || check.head == 0) {
586 		gi.Error("invalid body and/or head model indices");
587 	}
588 	gi.WriteShort(check.body);
589 	gi.WriteShort(check.head);
590 	gi.WriteByte(check.chr.bodySkin);
591 	gi.WriteByte(check.chr.headSkin);
592 	/* strip the server private states */
593 	gi.WriteShort(check.state & STATE_PUBLIC);
594 	gi.WriteByte(check.fieldSize);
595 	/* get the max values for TU and morale */
596 	gi.WriteByte(G_ActorCalculateMaxTU(&check));
597 	gi.WriteByte(std::min(MAX_SKILL, GET_MORALE(check.chr.score.skills[ABILITY_MIND])));
598 	gi.WriteShort(check.chr.maxHP);
599 	G_EventEnd();
600 
601 	if (mask) {
602 		G_EventActorStateChange(mask, check);
603 		G_SendInventory(mask, check);
604 	}
605 }
606 
607 /**
608  * @brief Send disappear event
609  * @param[in] playerMask The bitmask to determine the clients this event is send to
610  * @param[in,out] ent The edict that perished
611  */
G_EventEdictPerish(playermask_t playerMask,const Edict & ent)612 void G_EventEdictPerish (playermask_t playerMask, const Edict &ent)
613 {
614 	assert(ent.inuse);
615 	G_EventAdd(playerMask, EV_ENT_PERISH, ent.number);
616 	gi.WriteByte(ent.type);
617 	G_EventEnd();
618 }
619 
G_EventActorStateChange(playermask_t playerMask,const Edict & ent)620 void G_EventActorStateChange (playermask_t playerMask, const Edict &ent)
621 {
622 	G_EventAdd(playerMask, EV_ACTOR_STATECHANGE, ent.number);
623 	gi.WriteShort(ent.state);
624 	G_EventEnd();
625 }
626 
G_EventAddBrushModel(playermask_t playerMask,const Edict & ent)627 void G_EventAddBrushModel (playermask_t playerMask, const Edict &ent)
628 {
629 	G_EventAdd(playerMask, EV_ADD_BRUSH_MODEL, ent.number);
630 	gi.WriteByte(ent.type);
631 	gi.WriteShort(ent.modelindex);
632 	/* strip the higher bits - only send levelflags */
633 	gi.WriteByte(ent.spawnflags & 0xFF);
634 	gi.WritePos(ent.origin);
635 	gi.WritePos(ent.angles);
636 	gi.WriteShort(ent.speed);
637 	gi.WriteByte(ent.angle);
638 	gi.WriteByte(ent.dir);
639 	G_EventEnd();
640 }
641 
G_EventEndRoundAnnounce(const Player & player)642 void G_EventEndRoundAnnounce (const Player &player)
643 {
644 	G_EventAdd(PM_ALL, EV_ENDROUNDANNOUNCE, -1);
645 	gi.WriteByte(player.getNum());
646 	gi.WriteByte(player.getTeam());
647 	G_EventEnd();
648 }
649 
G_EventStart(const Player & player,bool teamplay)650 void G_EventStart (const Player &player, bool teamplay)
651 {
652 	G_EventAdd(G_PlayerToPM(player), EV_START | EVENT_INSTANTLY, -1);
653 	gi.WriteByte(teamplay);
654 	G_EventEnd();
655 }
656 
G_EventReset(const Player & player,int activeTeam)657 void G_EventReset (const Player &player, int activeTeam)
658 {
659 	G_EventAdd(G_PlayerToPM(player), EV_RESET | EVENT_INSTANTLY, -1);
660 	gi.WriteByte(player.getTeam());
661 	gi.WriteByte(activeTeam);
662 	G_EventEnd();
663 }
664 
G_EventDoorOpen(const Edict & door)665 void G_EventDoorOpen (const Edict &door)
666 {
667 	G_EventAdd(PM_ALL, EV_DOOR_OPEN, door.number);
668 	G_EventEnd();
669 }
670 
G_EventDoorClose(const Edict & door)671 void G_EventDoorClose (const Edict &door)
672 {
673 	G_EventAdd(PM_ALL, EV_DOOR_CLOSE, door.number);
674 	G_EventEnd();
675 }
676 
G_EventModelExplodeTriggered(const Edict & ent,const char * sound)677 void G_EventModelExplodeTriggered (const Edict &ent, const char* sound)
678 {
679 	assert(ent.inuse);
680 	G_EventAdd(PM_ALL, EV_MODEL_EXPLODE_TRIGGERED, ent.number);
681 	gi.WriteString(sound);
682 	G_EventEnd();
683 }
684 
G_EventModelExplode(const Edict & ent,const char * sound)685 void G_EventModelExplode (const Edict &ent, const char* sound)
686 {
687 	assert(ent.inuse);
688 	G_EventAdd(PM_ALL, EV_MODEL_EXPLODE, ent.number);
689 	gi.WriteString(sound);
690 	G_EventEnd();
691 }
692 
G_EventAdd(playermask_t playerMask,int eType,int entnum)693 void G_EventAdd (playermask_t playerMask, int eType, int entnum)
694 {
695 	G_EventEnd();
696 	gi.AddEvent(playerMask, eType, entnum);
697 }
698 
G_EventEnd(void)699 void G_EventEnd (void)
700 {
701 	if (gi.GetEvent() == EV_ACTOR_MOVE) {
702 		/* mark the end of the steps */
703 		gi.WriteLong(0);
704 		const Edict* ent = gi.GetEventEdict();
705 		assert(ent);
706 		gi.WriteGPos(ent->pos);
707 	}
708 	gi.EndEvents();
709 }
710