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 "../../../../client.h"
26 #include "../../../cl_actor.h"
27 #include "../../../cl_hud.h"
28 #include "../../../cl_parse.h"
29 #include "../../../cl_particle.h"
30 #include "../../../../renderer/r_mesh_anim.h"
31 #include "e_event_actordie.h"
32 
33 /**
34  * @brief Some events will be delayed if they are executed in the context of a dying actor. That's why we set
35  * the @c parsedDeath value to @c true here.
36  */
CL_ActorDieTime(const struct eventRegister_s * self,dbuffer * msg,eventTiming_t * eventTiming)37 int CL_ActorDieTime (const struct eventRegister_s* self, dbuffer* msg, eventTiming_t* eventTiming)
38 {
39 	eventTiming->parsedDeath = true;
40 	if (eventTiming->parsedShot && eventTiming->impactTime > cl.time)
41 		return eventTiming->impactTime;
42 	return eventTiming->nextTime;
43 }
44 
45 /**
46  * @brief Kills an actor (all that is needed is the local entity state set to STATE_DEAD).
47  * @note Also changes the animation to a random death sequence and appends the dead animation
48  * @param[in] msg The netchannel message
49  * @param[in] self Pointer to the event structure that is currently executed
50  */
CL_ActorDie(const eventRegister_t * self,dbuffer * msg)51 void CL_ActorDie (const eventRegister_t* self, dbuffer* msg)
52 {
53 	int entnum, state, playerNum, attacker;
54 
55 	NET_ReadFormat(msg, self->formatString, &entnum, &state, &playerNum, &attacker);
56 
57 	/* get les */
58 	le_t* le = LE_Get(entnum);
59 
60 	if (!le)
61 		LE_NotFoundError(entnum);
62 
63 	if (!LE_IsLivingActor(le))
64 		Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, LE is not an actor (type: %i)", le->type);
65 
66 	if (!LE_IsStunned(le) && LE_IsDead(le))
67 		Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, actor already dead");
68 
69 	LE_Lock(le);
70 
71 	/* set relevant vars */
72 	le->resetFloor();
73 	le->state = state;
74 
75 	/* count spotted aliens */
76 	cl.numEnemiesSpotted = CL_CountVisibleEnemies();
77 	Cvar_SetValue("mn_numaliensspotted", cl.numEnemiesSpotted);
78 
79 	/* play animation */
80 	LE_SetThink(le, nullptr);
81 	R_AnimChange(&le->as, le->model1, va("death%i", LE_GetAnimationIndexForDeath(le)));
82 	R_AnimAppend(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le)));
83 
84 	/* Print some info about the death or stun. */
85 	if (le->team == cls.team) {
86 		if (playerNum != le->pnum) {
87 			const char* playerName = CL_PlayerGetName(playerNum);
88 			char tmpbuf[128];
89 			Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s lost a soldier\n"), playerName);
90 			HUD_DisplayMessage(tmpbuf);
91 		} else {
92 			const character_t* chr = CL_ActorGetChr(le);
93 			if (chr) {
94 				char tmpbuf[128];
95 				if (LE_IsStunned(le)) {
96 					Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was stunned\n"), chr->name);
97 				} else if (!attacker) {
98 					Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s has died\n"), chr->name);
99 				} else {
100 					Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was killed\n"), chr->name);
101 				}
102 				HUD_DisplayMessage(tmpbuf);
103 			}
104 		}
105 	} else {
106 		switch (le->team) {
107 		case (TEAM_CIVILIAN):
108 			if (LE_IsStunned(le))
109 				HUD_DisplayMessage(_("A civilian was stunned."));
110 			else if (!attacker)
111 				HUD_DisplayMessage(_("A civilian has died."));
112 			else
113 				HUD_DisplayMessage(_("A civilian was killed."));
114 			break;
115 		case (TEAM_ALIEN):
116 			if (LE_IsStunned(le))
117 				HUD_DisplayMessage(_("An alien was stunned."));
118 			else if (!attacker)
119 				HUD_DisplayMessage(_("An alien has died."));
120 			else
121 				HUD_DisplayMessage(_("An alien was killed."));
122 			break;
123 		case (TEAM_PHALANX):
124 			if (LE_IsStunned(le))
125 				HUD_DisplayMessage(_("A soldier was stunned."));
126 			else if (!attacker)
127 				HUD_DisplayMessage(_("A soldier has died."));
128 			else
129 				HUD_DisplayMessage(_("A soldier was killed."));
130 			break;
131 		default:
132 			if (LE_IsStunned(le))
133 				HUD_DisplayMessage(va(_("A member of team %i was stunned."), le->team));
134 			else if (!attacker)
135 				HUD_DisplayMessage(va(_("A member of team %i has died."), le->team));
136 			else
137 				HUD_DisplayMessage(va(_("A member of team %i was killed."), le->team));
138 			break;
139 		}
140 	}
141 
142 	/**
143 	 * @todo CHRSH_IsTeamDefRobot: spawn smoke particles for robots
144 	 */
145 
146 	CL_ActorPlaySound(le, SND_DEATH);
147 
148 	le->aabb.setMaxs(player_dead_maxs);
149 	if (!LE_IsStunned(le))
150 		le->contents = CONTENTS_DEADACTOR;
151 	if (le->ptl) {
152 		CL_ParticleFree(le->ptl);
153 		le->ptl = nullptr;
154 	}
155 	CL_ActorRemoveFromTeamList(le);
156 
157 	/* update pathing as we maybe can walk onto the dead actor now */
158 	CL_ActorConditionalMoveCalc(selActor);
159 	LE_Unlock(le);
160 }
161