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_actor.h"
26 #include "g_client.h"
27 #include "g_edicts.h"
28 #include "g_health.h"
29 #include "g_inventory.h"
30 #include "g_reaction.h"
31 #include "g_spawn.h"
32 #include "g_utils.h"
33 #include "g_vis.h"
34 
35 /* however we manipulate TUs, this value must never be exceeded */
36 #define MAX_TU				(ROUTING_NOT_REACHABLE - 1)
37 
38 /**
39  * @brief Checks whether the given edict is a living actor
40  * @param[in] ent The edict to perform the check for
41  * @sa LE_IsLivingActor
42  */
G_IsLivingActor(const Edict * ent)43 bool G_IsLivingActor (const Edict* ent)
44 {
45 	return G_IsActor(ent) && (G_IsStunned(ent) || !G_IsDead(ent));
46 }
47 
48 /**
49  * @brief Make the actor use (as in open/close) a door edict
50  * @note Will also check whether the door is still reachable (this might have
51  * changed due to the rotation) after the usage
52  * @param actor The actor that is using the door
53  * @param door The door that should be opened/closed
54  */
G_ActorUseDoor(Edict * actor,Edict * door)55 void G_ActorUseDoor (Edict* actor, Edict* door)
56 {
57 	/* check whether it's part of an edict group but not the master */
58 	if (door->flags & FL_GROUPSLAVE)
59 		door = door->groupMaster;
60 
61 	if (!G_ClientUseEdict(actor->getPlayer(), actor, door))
62 		return;
63 
64 	/* end this loop here, for the AI this is a) not interesting,
65 	 * and b) could result in endless loops */
66 	if (G_IsAI(actor))
67 		return;
68 
69 	Edict* closeActor = nullptr;
70 	while ((closeActor = G_FindRadius(closeActor, door->origin, UNIT_SIZE * 3))) {
71 		/* check whether the door is still reachable (this might have
72 		 * changed due to the rotation) or whether an actor can reach it now */
73 		G_TouchTriggers(closeActor);
74 	}
75 }
76 
77 /**
78  * @brief Checks whether the given actor is currently standing in a rescue zone
79  * @param[in] actor The actor to check
80  * @return @c true if the actor is standing in a rescue zone, @c false otherwise.
81  */
G_ActorIsInRescueZone(const Edict * actor)82 bool G_ActorIsInRescueZone (const Edict* actor)
83 {
84 	return actor->inRescueZone;
85 }
86 
87 /**
88  * @brief Set the rescue zone data
89  * @param[out] actor The actor to set the rescue zone flag for
90  * @param[in] inRescueZone @c true if the actor is in the rescue zone, @c false otherwise
91  */
G_ActorSetInRescueZone(Edict * actor,bool inRescueZone)92 void G_ActorSetInRescueZone (Edict* actor, bool inRescueZone)
93 {
94 	if (inRescueZone == G_ActorIsInRescueZone(actor))
95 		return;
96 
97 	actor->inRescueZone = inRescueZone;
98 }
99 
100 /**
101  * @brief Handles the client actions (interaction with the world)
102  * @param actor The actors' edict
103  * @param ent The edict that can be used by the actor
104  */
G_ActorSetClientAction(Edict * actor,Edict * ent)105 void G_ActorSetClientAction (Edict* actor, Edict* ent)
106 {
107 	if (actor->clientAction == ent)
108 		return;
109 
110 	assert(ent == nullptr || (ent->flags & FL_CLIENTACTION));
111 	actor->clientAction = ent;
112 	if (ent == nullptr) {
113 		G_EventResetClientAction(*actor);
114 	} else {
115 		G_EventSetClientAction(*actor);
116 	}
117 }
118 
119 /**
120  * @brief Calculates the amount of all currently reserved TUs
121  * @param ent The actor to calculate the reserved TUs for
122  * @return The amount of reserved TUs for the given actor edict
123  */
G_ActorGetReservedTUs(const Edict * ent)124 int G_ActorGetReservedTUs (const Edict* ent)
125 {
126 	const chrReservations_t* res = &ent->chr.reservedTus;
127 	return res->reaction + res->shot + res->crouch;
128 }
129 
130 /**
131  * @brief Calculates the amount of usable TUs. This is without the reserved TUs.
132  * @param[in] ent The actor to calculate the amount of usable TUs for. If @c ent is @c nullptr, we
133  * return zero here
134  * @return The amount of usable TUs for the given actor edict
135  */
G_ActorUsableTUs(const Edict * ent)136 int G_ActorUsableTUs (const Edict* ent)
137 {
138 	if (!ent)
139 		return 0;
140 
141 	return ent->TU - G_ActorGetReservedTUs(ent);
142 }
143 
144 /**
145  * @brief Calculates the amount of TUs that are needed for the current selected reaction fire mode.
146  * @note It's assumed that there is a sane fire mode selected for reaction fire
147  * @param[in] ent The actors edict
148  * @return The amount of TUs that are needed for the current selected reaction fire mode.
149  */
G_ActorGetTUForReactionFire(const Edict * ent)150 int G_ActorGetTUForReactionFire (const Edict* ent)
151 {
152 	const FiremodeSettings *fm = &ent->chr.RFmode;
153 
154 	const Item* weapon = ent->getHandItem(fm->getHand());
155 	assert(weapon);
156 	assert(weapon->def());
157 
158 	const fireDef_t* fd = weapon->getFiredefs();
159 	assert(fd);
160 	return G_ActorGetModifiedTimeForFiredef(ent, &fd[fm->getFmIdx()], false);
161 }
162 
163 /**
164  * @brief Reserves TUs for different actor actions
165  * @param[in,out] ent The actor to reserve TUs for. Might not be @c nullptr.
166  * @param[in] resReaction TUs for reaction fire
167  * @param[in] resShot TUs for shooting
168  * @param[in] resCrouch TUs for going into crouch mode
169  */
G_ActorReserveTUs(Edict * ent,int resReaction,int resShot,int resCrouch)170 void G_ActorReserveTUs (Edict* ent, int resReaction, int resShot, int resCrouch)
171 {
172 	if (ent->TU >= resReaction + resShot + resCrouch) {
173 		ent->chr.reservedTus.reaction = resReaction;
174 		ent->chr.reservedTus.shot = resShot;
175 		ent->chr.reservedTus.crouch = resCrouch;
176 	}
177 
178 	G_EventActorSendReservations(*ent);
179 }
180 
181 /**
182  * @brief Turns an actor around
183  * @param[in] ent the actor (edict) we are talking about
184  * @param[in] dir the direction to turn the edict into, might be an action
185  * @return Bitmask of visible (VIS_*) values
186  * @sa G_CheckVisTeamAll
187  */
G_ActorDoTurn(Edict * ent,byte dir)188 int G_ActorDoTurn (Edict* ent, byte dir)
189 {
190 	float angleDiv;
191 	const byte* rot;
192 	int i, num;
193 	int status;
194 
195 	assert(ent->dir < CORE_DIRECTIONS);
196 	assert(dir < PATHFINDING_DIRECTIONS);
197 
198 	/*
199 	 * If dir is at least CORE_DIRECTIONS but less than FLYING_DIRECTIONS,
200 	 * then the direction is an action.
201 	 */
202 	/** @todo If performing an action, ensure the actor is facing the direction
203 	 *  needed to perform the action if needed (climbing ladders).
204 	 */
205 	if (dir >= CORE_DIRECTIONS && dir < FLYING_DIRECTIONS)
206 		return 0;
207 
208 	/* Clamp dir between 0 and CORE_DIRECTIONS - 1. */
209 	dir &= (CORE_DIRECTIONS - 1);
210 	assert(dir < CORE_DIRECTIONS);
211 
212 	/* Return if no rotation needs to be done. */
213 	if (ent->dir == dir)
214 		return 0;
215 
216 	/* calculate angle difference */
217 	angleDiv = directionAngles[dir] - directionAngles[ent->dir];
218 	if (angleDiv > 180.0)
219 		angleDiv -= 360.0;
220 	if (angleDiv < -180.0)
221 		angleDiv += 360.0;
222 
223 	/* prepare rotation - decide whether the actor turns around the left
224 	 * shoulder or the right - this is needed the get the rotation vector
225 	 * that is used below to check in each of the rotation steps
226 	 * (1/8, 22.5 degree) whether something became visible while turning */
227 	if (angleDiv > 0) {
228 		const int angleStep = (360.0 / CORE_DIRECTIONS);
229 		rot = dvleft;
230 		num = (angleDiv + angleStep / 2) / angleStep;
231 	} else {
232 		const int angleStep = (360.0 / CORE_DIRECTIONS);
233 		rot = dvright;
234 		num = (-angleDiv + angleStep / 2) / angleStep;
235 	}
236 
237 	/* do rotation and vis checks */
238 	status = 0;
239 
240 	/* check every angle (1/8 steps - on the way to the end direction) in the rotation
241 	 * whether something becomes visible and stop before reaching the final direction
242 	 * if this happened */
243 	for (i = 0; i < num; i++) {
244 		ent->dir = rot[ent->dir];
245 		assert(ent->dir < CORE_DIRECTIONS);
246 		status |= G_CheckVisTeamAll(ent->team, 0, ent);
247 	}
248 
249 	if (status & VIS_STOP) {
250 		/* send the turn */
251 		G_EventActorTurn(*ent);
252 	}
253 
254 	return status;
255 }
256 
257 /**
258  * @brief Sets correct bounding box for actor (state dependent).
259  * @param[in] ent Pointer to entity for which bounding box is being set.
260  * @note Also re-links the actor edict - because the server must know about the
261  * changed bounding box for tracing to work.
262  */
G_ActorSetMaxs(Edict * ent)263 void G_ActorSetMaxs (Edict* ent)
264 {
265 	if (G_IsCrouched(ent))
266 		VectorSet(ent->maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_CROUCH);
267 	else if (G_IsDead(ent) && !CHRSH_IsTeamDefRobot(ent->chr.teamDef))
268 		VectorSet(ent->maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_DEAD);
269 	else
270 		VectorSet(ent->maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_STAND);
271 
272 	/* Link it. */
273 	gi.LinkEdict(ent);
274 }
275 
G_ActorCalculateMaxTU(const Edict * ent)276 int G_ActorCalculateMaxTU (const Edict* ent)
277 {
278 	const int invWeight = ent->chr.inv.getWeight();
279 	const int currentMaxTU = GET_TU(ent->chr.score.skills[ABILITY_SPEED], GET_ENCUMBRANCE_PENALTY(invWeight,
280 			ent->chr.score.skills[ABILITY_POWER])) * G_ActorGetInjuryPenalty(ent, MODIFIER_TU);
281 
282 	return std::min(currentMaxTU, MAX_TU);
283 }
284 
285 /**
286  * @brief Set time units for the given edict. Based on speed skills
287  * @param ent The actor edict
288  */
G_ActorGiveTimeUnits(Edict * ent)289 void G_ActorGiveTimeUnits (Edict* ent)
290 {
291 	const int tus = G_IsDazed(ent) ? 0 : G_ActorCalculateMaxTU(ent);
292 	G_ActorSetTU(ent, tus);
293 	G_RemoveDazed(ent);
294 }
295 
G_ActorSetTU(Edict * ent,int tus)296 void G_ActorSetTU (Edict* ent, int tus)
297 {
298 	if (tus > 0 && tus < ent->TU) {
299 		if (g_notu != nullptr && g_notu->integer) {
300 			ent->TU = G_ActorCalculateMaxTU(ent);
301 			return;
302 		}
303 	}
304 	ent->TU = std::max(tus, 0);
305 }
306 
G_ActorUseTU(Edict * ent,int tus)307 void G_ActorUseTU (Edict* ent, int tus)
308 {
309 	G_ActorSetTU(ent, ent->TU - tus);
310 }
311 
G_ActorStun(Edict * ent,const Edict * attacker)312 static bool G_ActorStun (Edict* ent, const Edict* attacker)
313 {
314 	/* already dead or stunned? */
315 	if (G_IsDead(ent))
316 		return false;
317 
318 	/* no other state should be set here */
319 	ent->state = STATE_STUN;
320 	G_ActorSetMaxs(ent);
321 	ent->link = attacker;
322 
323 	G_ActorModifyCounters(attacker, ent, -1, 0, 1);
324 
325 	return true;
326 }
327 
G_ActorModifyCounters(const Edict * attacker,const Edict * victim,int deltaAlive,int deltaKills,int deltaStuns)328 void G_ActorModifyCounters (const Edict* attacker, const Edict* victim, int deltaAlive, int deltaKills, int deltaStuns)
329 {
330 	const int spawned = level.num_spawned[victim->team];
331 	const int attackerTeam = (attacker != nullptr ? attacker->team : MAX_TEAMS);
332 	byte* alive = level.num_alive;
333 
334 	alive[victim->team] += deltaAlive;
335 	if (alive[victim->team] > spawned)
336 		gi.Error("alive counter out of sync");
337 
338 	if (deltaStuns != 0) {
339 		byte* stuns = level.num_stuns[attackerTeam];
340 		stuns[victim->team] += deltaStuns;
341 		if (stuns[victim->team] > spawned)
342 			gi.Error("stuns counter out of sync");
343 	}
344 
345 	if (deltaKills != 0) {
346 		byte* kills = level.num_kills[attackerTeam];
347 		kills[victim->team] += deltaKills;
348 		if (kills[victim->team] > spawned)
349 			gi.Error("kills counter out of sync");
350 	}
351 #if 0
352 	{
353 		int i;
354 
355 		Com_Printf("num_spawned: %i\n", spawned);
356 
357 		if (attacker)
358 		for (i = 0; i < MAX_TEAMS; i++) {
359 			int j;
360 
361 			if (i == victim->team) {
362 				Com_Printf("^2num_alive (team %i): %i\n", i, level.num_alive[i]);
363 			} else {
364 				Com_Printf("num_alive (team %i): %i\n", i, level.num_alive[i]);
365 			}
366 
367 			for (j = 0; j < MAX_TEAMS; j++) {
368 				if (j == victim->team) {
369 					Com_Printf("^2num_kills (team %i killed  %i): %i\n", i, j, level.num_kills[i][j]);
370 					Com_Printf("^2num_stuns (team %i stunned %i): %i\n", i, j, level.num_stuns[i][j]);
371 				} else if (i == attacker->team) {
372 					Com_Printf("^3num_kills (team %i killed  %i): %i\n", i, j, level.num_kills[i][j]);
373 					Com_Printf("^3num_stuns (team %i stunned %i): %i\n", i, j, level.num_stuns[i][j]);
374 				} else {
375 					Com_Printf("num_kills (team %i killed  %i): %i\n", i, j, level.num_kills[i][j]);
376 					Com_Printf("num_stuns (team %i stunned %i): %i\n", i, j, level.num_stuns[i][j]);
377 				}
378 			}
379 		}
380 	}
381 #endif
382 }
383 
384 /**
385  * @brief Fills a vector with the eye position of a given actor
386  * @param[in] actor The actor edict to get the eye position in the world from
387  * @param[out] eye The eye vector world position
388  */
G_ActorGetEyeVector(const Edict * actor,vec3_t eye)389 void G_ActorGetEyeVector (const Edict* actor, vec3_t eye)
390 {
391 	VectorCopy(actor->origin, eye);
392 	if (G_IsCrouched(actor) || G_IsPanicked(actor))
393 		eye[2] += EYE_CROUCH;
394 	else
395 		eye[2] += EYE_STAND;
396 }
397 
G_ActorRevitalise(Edict * ent)398 static void G_ActorRevitalise (Edict* ent)
399 {
400 	if (G_IsStunned(ent)) {
401 		G_RemoveStunned(ent);
402 		/** @todo have a look at the morale value of
403 		 * the ent and maybe get into rage or panic? */
404 		G_ActorModifyCounters(ent->link, ent, 1, 0, -1);
405 		G_GetFloorItems(ent);
406 	}
407 	G_ActorSetMaxs(ent);
408 
409 	/* check if the player appears/perishes, seen from other teams */
410 	G_CheckVis(ent);
411 
412 	/* calc new vis for this player */
413 	G_CheckVisTeamAll(ent->team, 0, ent);
414 
415 	G_PrintStats("%s is revitalized.", ent->chr.name);
416 }
417 
G_ActorCheckRevitalise(Edict * ent)418 void G_ActorCheckRevitalise (Edict* ent)
419 {
420 	if (G_IsStunned(ent) && ent->STUN < ent->HP) {
421 		/* check that we could move after we stood up */
422 		Edict* otherActor = nullptr;
423 		while ((otherActor = G_EdictsGetNextInUse(otherActor))) {
424 			if (!VectorCompare(ent->pos, otherActor->pos))
425 				continue;
426 			if (G_IsBlockingMovementActor(otherActor))
427 				return;
428 		}
429 
430 		G_ActorRevitalise(ent);
431 		G_EventActorRevitalise(*ent);
432 		G_EventActorStateChange(~G_VisToPM(ent->visflags), *ent);
433 		G_SendStats(*ent);
434 	}
435 }
436 
G_ActorDie(Edict * ent,const Edict * attacker)437 static bool G_ActorDie (Edict* ent, const Edict* attacker)
438 {
439 	const bool stunned = G_IsStunned(ent);
440 
441 	G_RemoveStunned(ent);
442 
443 	if (G_IsDead(ent))
444 		return false;
445 
446 	G_SetState(ent, 1 + rand() % MAX_DEATH);
447 	G_ActorSetMaxs(ent);
448 
449 	if (stunned) {
450 		G_ActorModifyCounters(attacker, ent, 0, 1, 0);
451 		G_ActorModifyCounters(ent->link, ent, 0, 0, -1);
452 	} else {
453 		G_ActorModifyCounters(attacker, ent, -1, 1, 0);
454 	}
455 
456 	return true;
457 }
458 
459 /**
460  * @brief Reports and handles death or stun of an actor. If the HP of an actor is zero the actor
461  * will die, otherwise the actor will get stunned.
462  * @param[in] ent Pointer to an entity being killed or stunned actor.
463  * @param[in] attacker Pointer to attacker - it must be notified about state of victim.
464  * @todo Discuss whether stunned actor should really drop everything to floor. Maybe
465  * it should drop only what he has in hands? Stunned actor can wake later during mission.
466  */
G_ActorDieOrStun(Edict * ent,Edict * attacker)467 bool G_ActorDieOrStun (Edict* ent, Edict* attacker)
468 {
469 	bool state;
470 
471 	if (ent->HP == 0)
472 		state = G_ActorDie(ent, attacker);
473 	else
474 		state = G_ActorStun(ent, attacker);
475 
476 	/* no state change performed? */
477 	if (!state) {
478 		gi.DPrintf("G_ActorDieOrStun: State wasn't changed\n");
479 		return false;
480 	}
481 
482 	if (!G_IsStunned(ent))
483 		ent->solid = SOLID_NOT;
484 
485 	/* send death */
486 	G_EventActorDie(*ent, attacker != nullptr);
487 	G_EventActorStateChange(~G_VisToPM(ent->visflags), *ent);
488 
489 	/* handle inventory - drop everything but the armour to the floor */
490 	G_InventoryToFloor(ent);
491 	G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false);
492 
493 	/* check if the player appears/perishes, seen from other teams */
494 	G_CheckVis(ent);
495 
496 	/* check if the attacker appears/perishes, seen from other teams */
497 	if (attacker)
498 		G_CheckVis(attacker);
499 
500 	/* calc new vis for this player */
501 	G_CheckVisTeamAll(ent->team, 0, attacker);
502 
503 	/* unlink the floor container */
504 	ent->resetFloor();
505 
506 	G_ReactionFireOnDead(ent);
507 
508 	return true;
509 }
510 
511 /**
512  * @brief Get the content flags from where the actor is currently standing
513  */
G_ActorGetContentFlags(const vec3_t origin)514 int G_ActorGetContentFlags (const vec3_t origin)
515 {
516 	vec3_t pointTrace;
517 
518 	VectorCopy(origin, pointTrace);
519 	pointTrace[2] += PLAYER_MIN;
520 
521 	return gi.PointContents(pointTrace);
522 }
523 
524 /**
525  * @brief Moves an item inside an inventory. Floors are handled special.
526  * @param[in] actor The pointer to the selected/used edict/soldier.
527  * @param[in] fromContType The container (-id) the item should be moved from.
528  * @param[in] fItem The item you want to move.
529  * @param[in] toContType The container (-def) the item should be moved to.
530  * @param[in] tx x position where you want the item to go in the destination container
531  * @param[in] ty y position where you want the item to go in the destination container
532  * @param[in] checkaction Set this to true if you want to check for TUs, otherwise false.
533  * @sa event PA_INVMOVE
534  * @sa AI_ActorThink
535  */
G_ActorInvMove(Edict * actor,const invDef_t * fromContType,Item * fItem,const invDef_t * toContType,int tx,int ty,bool checkaction)536 bool G_ActorInvMove (Edict* actor, const invDef_t* fromContType, Item* fItem, const invDef_t* toContType, int tx, int ty, bool checkaction)
537 {
538 	Edict* floor;
539 	bool newFloor;
540 	Item* tc;
541 	playermask_t mask;
542 	inventory_action_t ia;
543 	Item fromItemBackup, toItemBackup;
544 	int fx, fy;
545 	int originalTU, reservedTU = 0;
546 	Player &player = actor->getPlayer();
547 
548 	assert(fItem);
549 	assert(fItem->def());
550 
551 	/* Store the location/item of 'from' BEFORE actually moving items with moveInInventory. */
552 	fromItemBackup = *fItem;
553 
554 	/* Store the location of 'to' BEFORE actually moving items with moveInInventory
555 	 * so in case we swap ammo the client can be updated correctly */
556 	tc = actor->chr.inv.getItemAtPos(toContType, tx, ty);
557 	if (tc)
558 		toItemBackup = *tc;
559 	else
560 		toItemBackup = *fItem;
561 
562 	/* Get first used bit in item. */
563 	fItem->getFirstShapePosition(&fx, &fy);
564 	fx += fItem->getX();
565 	fy += fItem->getY();
566 
567 	/* Check if action is possible */
568 	/* TUs are 1 here - but this is only a dummy - the real TU check is done in the inventory functions below */
569 	if (checkaction && !G_ActionCheckForCurrentTeam(player, actor, 1))
570 		return false;
571 
572 	if (!actor->chr.inv.canHoldItemWeight(fromContType->id, toContType->id, *fItem, actor->chr.score.skills[ABILITY_POWER])) {
573 		G_ClientPrintf(player, PRINT_HUD, _("This soldier can not carry anything else."));
574 		return false;
575 	}
576 
577 	/* "get floor ready" - searching for existing floor-edict */
578 	floor = G_GetFloorItems(actor);
579 	if (toContType->isFloorDef() && !floor) {
580 		/* We are moving to the floor, but no existing edict for this floor-tile found -> create new one */
581 		floor = G_SpawnFloor(actor->pos);
582 		newFloor = true;
583 	} else if (fromContType->isFloorDef() && !floor) {
584 		/* We are moving from the floor, but no existing edict for this floor-tile found -> this should never be the case. */
585 		gi.DPrintf("G_ClientInvMove: No source-floor found.\n");
586 		return false;
587 	} else {
588 		/* There already exists an edict for this floor-tile. */
589 		newFloor = false;
590 	}
591 
592 	/* search for space */
593 	Item* item2;
594 	if (tx == NONE) {
595 		item2 = actor->chr.inv.getItemAtPos(fromContType, fItem->getX(), fItem->getY());
596 		if (item2)
597 			actor->chr.inv.findSpace(toContType, item2, &tx, &ty, fItem);
598 		if (tx == NONE)
599 			return false;
600 	}
601 
602 	/** @todo what if we don't have enough TUs after subtracting the reserved ones? */
603 	/* Because moveInInventory don't know anything about character_t and it updates actor->TU,
604 	 * we need to save original actor->TU for the sake of checking TU reservations. */
605 	originalTU = actor->TU;
606 	reservedTU = G_ActorGetReservedTUs(actor);
607 	/* Temporary decrease actor->TU to make moveInInventory do what expected. */
608 	G_ActorUseTU(actor, reservedTU);
609 	/* Try to actually move the item and check the return value after restoring valid actor->TU. */
610 	ia = game.i.moveInInventory(&actor->chr.inv, fromContType, fItem, toContType, tx, ty, checkaction ? &actor->TU : nullptr, &item2);
611 	/* Now restore the original actor->TU and decrease it for TU used for inventory move. */
612 	G_ActorSetTU(actor, originalTU - (originalTU - reservedTU - actor->TU));
613 
614 	switch (ia) {
615 	case IA_NONE:
616 		/* No action possible - abort */
617 		return false;
618 	case IA_NOTIME:
619 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not enough TUs!"));
620 		return false;
621 	case IA_NORELOAD:
622 		G_ClientPrintf(player, PRINT_HUD,
623 				_("Can't perform action - weapon already fully loaded with the same ammunition!"));
624 		return false;
625 	default:
626 		/* Continue below. */
627 		break;
628 	}
629 
630 	/* successful inventory change; remove the item in clients */
631 	if (fromContType->isFloorDef()) {
632 		/* We removed an item from the floor - check how the client
633 		 * needs to be updated. */
634 		assert(!newFloor);
635 		if (actor->getFloor()) {
636 			/* There is still something on the floor. */
637 			floor->setFloor(actor);
638 			/* Delay this if swapping ammo, otherwise the le will be removed in the client before we can add back
639 			 * the current ammo because removeNextFrame is set in LE_PlaceItem() if the floor le has no items */
640 			if (ia != IA_RELOAD_SWAP)
641 				G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy);
642 		} else {
643 			/* Floor is empty, remove the edict (from server + client) if we are
644 			 * not moving to it. */
645 			if (!toContType->isFloorDef()) {
646 				G_EventPerish(*floor);
647 				G_FreeEdict(floor);
648 			} else
649 				G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy);
650 		}
651 	} else {
652 		G_EventInventoryDelete(*actor, G_TeamToPM(actor->team), fromContType->id, fx, fy);
653 	}
654 
655 	/* send tu's */
656 	G_SendStats(*actor);
657 
658 	assert(item2);
659 	Item item = *item2;
660 
661 	if (ia == IA_RELOAD || ia == IA_RELOAD_SWAP) {
662 		/* reload */
663 		if (toContType->isFloorDef())
664 			mask = G_VisToPM(floor->visflags);
665 		else
666 			mask = G_TeamToPM(actor->team);
667 
668 		G_EventInventoryReload(toContType->isFloorDef() ? *floor : *actor, mask, &item, toContType, item2);
669 
670 		if (ia == IA_RELOAD) {
671 			return true;
672 		} else { /* ia == IA_RELOAD_SWAP */
673 			item.setAmmoLeft(NONE_AMMO);
674 			item.setAmmoDef(nullptr);
675 			item.setDef(toItemBackup.ammoDef());
676 			item.rotated = fromItemBackup.rotated;
677 			item.setAmount(toItemBackup.getAmount());
678 			toContType = fromContType;
679 			if (toContType->isFloorDef()) {
680 				/* moveInInventory placed the swapped ammo in an available space, check where it was placed
681 				 * so we can place it at the same place in the client, otherwise since fItem hasn't been removed
682 				 * this could end in a different place in the client - will cause an error if trying to use it again */
683 				item2 = actor->chr.inv.findInContainer(toContType->id, &item);
684 				assert(item2);
685 				fromItemBackup = item;
686 				fromItemBackup.setX(item2->getX());
687 				fromItemBackup.setY(item2->getY());
688 			}
689 			tx = fromItemBackup.getX();
690 			ty = fromItemBackup.getY();
691 		}
692 	}
693 
694 	/* We moved an item to the floor - check how the client needs to be updated. */
695 	if (toContType->isFloorDef()) {
696 		/* we have to link the temp floor container to the new floor edict or add
697 		 * the item to an already existing floor edict - the floor container that
698 		 * is already linked might be from a different entity (this might happen
699 		 * in case of a throw by another actor) */
700 		floor->setFloor(actor);
701 
702 		/* A new container was created for the floor. */
703 		if (newFloor) {
704 			/* Send item info to the clients */
705 			G_CheckVis(floor);
706 		} else {
707 			/* use the backup item to use the old amount values, because the clients have to use the same actions
708 			 * on the original amount. Otherwise they would end in a different amount of items as the server (+1) */
709 			G_EventInventoryAdd(*floor, G_VisToPM(floor->visflags), 1);
710 			G_WriteItem(fromItemBackup, toContType->id, tx, ty);
711 			G_EventEnd();
712 			/* Couldn't remove it before because that would remove the le from the client and would cause battlescape to crash
713 			 * when trying to add back the swapped ammo above */
714 			if (ia == IA_RELOAD_SWAP)
715 				G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy);
716 		}
717 	} else {
718 		G_EventInventoryAdd(*actor, G_TeamToPM(actor->team), 1);
719 		G_WriteItem(item, toContType->id, tx, ty);
720 		G_EventEnd();
721 	}
722 
723 	G_ReactionFireSettingsUpdate(actor, actor->chr.RFmode.getFmIdx(), actor->chr.RFmode.getHand(), actor->chr.RFmode.getWeapon());
724 
725 	/* Other players receive weapon info only. */
726 	mask = G_VisToPM(actor->visflags) & ~G_TeamToPM(actor->team);
727 	if (mask) {
728 		if (fromContType->isRightDef() || fromContType->isLeftDef()) {
729 			G_EventInventoryDelete(*actor, mask, fromContType->id, fx, fy);
730 		}
731 		if (toContType->isRightDef() || toContType->isLeftDef()) {
732 			G_EventInventoryAdd(*actor, mask, 1);
733 			G_WriteItem(item, toContType->id, tx, ty);
734 			G_EventEnd();
735 		}
736 	}
737 
738 	return true;
739 }
740 
741 /**
742  * @brief Reload weapon with actor.
743  * @param[in] ent Pointer to an actor reloading weapon.
744  * @param[in] invDef Reloading weapon in right or left hand.
745  * @sa AI_ActorThink
746  */
G_ActorReload(Edict * ent,const invDef_t * invDef)747 void G_ActorReload (Edict* ent, const invDef_t* invDef)
748 {
749 	const objDef_t* weapon;
750 
751 	if (ent->getContainer(invDef->id)) {
752 		weapon = ent->getContainer(invDef->id)->def();
753 	} else if (invDef->isLeftDef() && ent->getRightHandItem()->isHeldTwoHanded()) {
754 		/* Check for two-handed weapon */
755 		invDef = INVDEF(CID_RIGHT);
756 		weapon = ent->getRightHandItem()->def();
757 	} else
758 		return;
759 
760 	assert(weapon);
761 
762 	/* LordHavoc: Check if item is researched when in singleplayer? I don't think this is really a
763 	 * cheat issue as in singleplayer there is no way to inject fake client commands in the virtual
764 	 * network buffer, and in multiplayer everything is researched */
765 
766 	/* search for clips and select the one that is available easily */
767 	/* also try the temp containers */
768 	const invDef_t* bestContainer = nullptr;
769 	Item* ammoItem = nullptr;
770 	int tu = 100;
771 	const Container* cont = nullptr;
772 	while ((cont = ent->chr.inv.getNextCont(cont, true))) {
773 		if (cont->def()->out >= tu)
774 			continue;
775 		/* Once we've found at least one clip, there's no point
776 		 * searching other containers if it would take longer
777 		 * to retrieve the ammo from them than the one
778 		 * we've already found. */
779 		Item* item = nullptr;
780 		while ((item = cont->getNextItem(item))) {
781 			if (item->def()->isLoadableInWeapon(weapon)) {
782 				ammoItem = item;
783 				bestContainer = INVDEF(cont->id);
784 				tu = bestContainer->out;
785 				break;
786 			}
787 		}
788 	}
789 
790 	/* send request */
791 	if (bestContainer)
792 		G_ActorInvMove(ent, bestContainer, ammoItem, invDef, 0, 0, true);
793 }
794 
G_ActorGetModifiedTimeForFiredef(const Edict * const ent,const fireDef_t * const fd,const bool reaction)795 int G_ActorGetModifiedTimeForFiredef (const Edict* const ent, const fireDef_t* const fd, const bool reaction)
796 {
797 	return fd->time * G_ActorGetInjuryPenalty(ent, reaction ? MODIFIER_REACTION : MODIFIER_SHOOTING);
798 }
799