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"
26 #include "g_actor.h"
27 #include "g_client.h"
28 #include "g_edicts.h"
29 #include "g_health.h"
30 #include "g_inventory.h"
31 #include "g_utils.h"
32 #include "g_vis.h"
33 
34 /**
35  * @brief Checks whether a point is "visible" from the edicts position
36  * @sa FrustumVis
37  */
G_FrustumVis(const Edict * from,const vec3_t point)38 bool G_FrustumVis (const Edict* from, const vec3_t point)
39 {
40 	if (G_IsActiveCamera(from)) {
41 		/* it's a 360 degree camera */
42 		if (from->camera.rotate)
43 			return true;
44 	}
45 	return FrustumVis(from->origin, from->dir, point);
46 }
47 
48 /**
49  * @brief tests the visibility between two points
50  * @param[in] from The point to check visibility from
51  * @param[in] to The point to check visibility to
52  * @return true if the points are invisible from each other (trace hit something), false otherwise.
53  */
G_LineVis(const vec3_t from,const vec3_t to)54 static bool G_LineVis (const vec3_t from, const vec3_t to)
55 {
56 	return G_TestLineWithEnts(from, to);
57 }
58 
59 /**
60  * @brief calculate how much check is "visible" from @c from
61  * @param[in] from The world coordinate to check from
62  * @param[in] ent The source edict of the check
63  * @param[in] check The edict to check how good (or if at all) it is visible
64  * @param[in] full Perform a full check in different directions. If this is
65  * @c false the actor is fully visible if one vis check returned @c true. With
66  * @c true this function can also return a value != 0.0 and != 1.0. Try to only
67  * use @c true if you really need the full check. Full checks are of course
68  * more expensive.
69  * @return a value between 0.0 and 1.0 which reflects the visibility from 0
70  * to 100 percent
71  * @note This call isn't cheap - try to do this only if you really need the
72  * visibility check or the statement whether one particular actor see another
73  * particular actor.
74  * @sa CL_ActorVis
75  */
G_ActorVis(const vec3_t from,const Edict * ent,const Edict * check,bool full)76 float G_ActorVis (const vec3_t from, const Edict* ent, const Edict* check, bool full)
77 {
78 	vec3_t test, dir;
79 	float delta;
80 	int i, n;
81 	const float distance = VectorDist(check->origin, ent->origin);
82 
83 	/* units that are very close are visible in the smoke */
84 	if (distance > UNIT_SIZE * 1.5f) {
85 		vec3_t eyeEnt;
86 		Edict* e = nullptr;
87 
88 		G_ActorGetEyeVector(ent, eyeEnt);
89 
90 		while ((e = G_EdictsGetNextInUse(e))) {
91 			if (G_IsSmoke(e)) {
92 				if (RayIntersectAABB(eyeEnt, check->absmin, e->absmin, e->absmax)
93 				 || RayIntersectAABB(eyeEnt, check->absmax, e->absmin, e->absmax)) {
94 					return ACTOR_VIS_0;
95 				}
96 			}
97 		}
98 	}
99 
100 	/* start on eye height */
101 	VectorCopy(check->origin, test);
102 	if (G_IsDead(check)) {
103 		test[2] += PLAYER_DEAD;
104 		delta = 0;
105 	} else if (G_IsCrouched(check)) {
106 		test[2] += PLAYER_CROUCH - 2;
107 		delta = (PLAYER_CROUCH - PLAYER_MIN) / 2 - 2;
108 	} else {
109 		test[2] += PLAYER_STAND;
110 		delta = (PLAYER_STAND - PLAYER_MIN) / 2 - 2;
111 	}
112 
113 	/* side shifting -> better checks */
114 	dir[0] = from[1] - check->origin[1];
115 	dir[1] = check->origin[0] - from[0];
116 	dir[2] = 0;
117 	VectorNormalizeFast(dir);
118 	VectorMA(test, -7, dir, test);
119 
120 	/* do 3 tests */
121 	n = 0;
122 	for (i = 0; i < 3; i++) {
123 		if (!G_LineVis(from, test)) {
124 			if (full)
125 				n++;
126 			else
127 				return ACTOR_VIS_100;
128 		}
129 
130 		/* look further down or stop */
131 		if (!delta) {
132 			if (n > 0)
133 				return ACTOR_VIS_100;
134 			else
135 				return ACTOR_VIS_0;
136 		}
137 		VectorMA(test, 7, dir, test);
138 		test[2] -= delta;
139 	}
140 
141 	/* return factor */
142 	switch (n) {
143 	case 0:
144 		return ACTOR_VIS_0;
145 	case 1:
146 		return ACTOR_VIS_10;
147 	case 2:
148 		return ACTOR_VIS_50;
149 	default:
150 		return ACTOR_VIS_100;
151 	}
152 }
153 
G_VisCheckDist(const Edict * const ent)154 int G_VisCheckDist (const Edict* const ent)
155 {
156 	if (G_IsActiveCamera(ent))
157 		return MAX_SPOT_DIST_CAMERA;
158 
159 	if (G_IsActor(ent))
160 		return MAX_SPOT_DIST * G_ActorGetInjuryPenalty(ent, MODIFIER_SIGHT);
161 
162 	return MAX_SPOT_DIST;
163 }
164 
165 /**
166  * @brief test if check is visible by from
167  * @param[in] team Living team members are always visible. If this is a negative
168  * number we inverse the team rules (see comments included). In combination with VT_NOFRUSTUM
169  * we can check whether there is any edict (that is no in our team) that can see @c check
170  * @param[in] from is from team @c team and must be a living actor
171  * @param[in] check The edict we want to get the visibility for
172  * @param[in] flags @c VT_NOFRUSTUM, ...
173  */
G_Vis(const int team,const Edict * from,const Edict * check,const vischeckflags_t flags)174 bool G_Vis (const int team, const Edict* from, const Edict* check, const vischeckflags_t flags)
175 {
176 	vec3_t eye;
177 
178 	/* if any of them isn't in use, then they're not visible */
179 	if (!from->inuse || !check->inuse)
180 		return false;
181 
182 	/* only actors and 2x2 units can see anything */
183 	if (!G_IsLivingActor(from) && !G_IsActiveCamera(from))
184 		return false;
185 
186 	/* living team members are always visible */
187 	if (team >= 0 && check->team == team && !G_IsDead(check))
188 		return true;
189 
190 	/* standard team rules */
191 	if (team >= 0 && from->team != team)
192 		return false;
193 
194 	/* inverse team rules */
195 	if (team < 0 && check->team == -team)
196 		return false;
197 
198 	/* check for same pos */
199 	if (VectorCompare(from->pos, check->pos))
200 		return true;
201 
202 	if (!G_IsVisibleOnBattlefield(check))
203 		return false;
204 
205 	/* view distance check */
206 	const int spotDist = G_VisCheckDist(from);
207 	if (VectorDistSqr(from->origin, check->origin) > spotDist * spotDist)
208 		return false;
209 
210 	/* view frustum check */
211 	if (!(flags & VT_NOFRUSTUM) && !G_FrustumVis(from, check->origin))
212 		return false;
213 
214 	/* get viewers eye height */
215 	G_ActorGetEyeVector(from, eye);
216 
217 	/* line trace check */
218 	switch (check->type) {
219 	case ET_ACTOR:
220 	case ET_ACTOR2x2:
221 		return G_ActorVis(eye, from, check, false) > ACTOR_VIS_0;
222 	case ET_ITEM:
223 	case ET_CAMERA:
224 	case ET_PARTICLE:
225 		return !G_LineVis(eye, check->origin);
226 	default:
227 		return false;
228 	}
229 }
230 
231 /**
232  * @brief test if @c check is visible by team (or if visibility changed?)
233  * @sa G_CheckVisTeam
234  * @param[in] team the team the edict may become visible for or perish from
235  * their view
236  * @param[in] check the edict we are talking about - which may become visible
237  * or perish
238  * @param[in] flags if you want to check whether the edict may also perish from
239  * other players view, you should use the @c VT_PERISHCHK bits
240  * @note If the edict is already visible and flags doesn't contain the
241  * bits of @c VT_PERISHCHK, no further checks are performed - only the
242  * @c VS_YES bits are returned
243  * @return VS_CHANGE is added to the bit mask if the edict flipped its visibility
244  * (invisible to visible or vice versa) VS_YES means the edict is visible for the
245  * given team
246  */
G_TestVis(const int team,Edict * check,const vischeckflags_t flags)247 int G_TestVis (const int team, Edict* check, const vischeckflags_t flags)
248 {
249 	Edict* from = nullptr;
250 	/* store old flag */
251 	const int old = G_IsVisibleForTeam(check, team) ? VS_CHANGE : 0;
252 
253 	if (g_aidebug->integer)
254 		return VS_YES | !old;
255 
256 	if (!(flags & VT_PERISHCHK) && old)
257 		return VS_YES;
258 
259 	/* test if check is visible */
260 	while ((from = G_EdictsGetNextInUse(from)))
261 		if (G_Vis(team, from, check, flags))
262 			/* visible */
263 			return VS_YES | !old;
264 
265 	/* just return the old state */
266 	return old;
267 }
268 
G_VisShouldStop(const Edict * ent)269 static bool G_VisShouldStop (const Edict* ent)
270 {
271 	return G_IsLivingActor(ent) && !G_IsCivilian(ent);
272 }
273 
274 /**
275  * @param[in] team The team looking at the edict (or not)
276  * @param[in] check The edict to check the visibility for
277  * @param[in] visFlags The flags for the vis check
278  * @param[in] playerMask The mask for the players to send the appear/perish events to. If this is @c 0 the events
279  * are not sent - we only update the visflags of the edict
280  * @param[in] ent The edict that was responsible for letting the check edict appear and is looking
281  */
G_DoTestVis(const int team,Edict * check,const vischeckflags_t visFlags,playermask_t playerMask,const Edict * ent)282 static int G_DoTestVis (const int team, Edict* check, const vischeckflags_t visFlags, playermask_t playerMask, const Edict* ent)
283 {
284 	int status = 0;
285 	const int vis = G_TestVis(team, check, visFlags);
286 
287 	/* visibility has changed ... */
288 	if (vis & VS_CHANGE) {
289 		/* swap the vis mask for the given team */
290 		const bool appear = (vis & VS_YES) == VS_YES;
291 		if (playerMask == 0) {
292 			G_VisFlagsSwap(*check, G_TeamToVisMask(team));
293 		} else {
294 			G_AppearPerishEvent(playerMask, appear, *check, ent);
295 		}
296 
297 		/* ... to visible */
298 		if (vis & VS_YES) {
299 			status |= VIS_APPEAR;
300 			if (G_VisShouldStop(check))
301 				status |= VIS_STOP;
302 		} else {
303 			status |= VIS_PERISH;
304 		}
305 	} else if (vis == 0 && (visFlags & VT_NEW)) {
306 		if (G_IsActor(check)) {
307 			G_EventActorAdd(playerMask, *check);
308 		}
309 	}
310 	return status;
311 }
312 
313 /**
314  * @brief Sets visible edict on player spawn
315  * @sa G_ClientStartMatch
316  * @sa G_CheckVisTeam
317  * @sa G_AppearPerishEvent
318  */
G_CheckVisPlayer(Player & player,const vischeckflags_t visFlags)319 void G_CheckVisPlayer (Player &player, const vischeckflags_t visFlags)
320 {
321 	Edict* ent = nullptr;
322 
323 	/* check visibility */
324 	while ((ent = G_EdictsGetNextInUse(ent))) {
325 		/* check if he's visible */
326 		G_DoTestVis(player.getTeam(), ent, visFlags, G_PlayerToPM(player), nullptr);
327 	}
328 }
329 
330 /**
331  * @brief Checks whether an edict appear/perishes for a specific team - also
332  * updates the visflags each edict carries
333  * @sa G_TestVis
334  * @param[in] team Team to check the vis for
335  * @param[in] check The edict that you want to check (and which maybe will appear
336  * or perish for the given team). If this is nullptr every edict will be checked.
337  * @param visFlags Modifiers for the checks
338  * @param[in] ent The edict that is (maybe) seeing other edicts
339  * @return If an actor get visible who's no civilian VIS_STOP is added to the
340  * bit mask, VIS_APPEAR means, that the actor is becoming visible for the queried
341  * team, VIS_PERISH means that the actor (the edict) is getting invisible
342  * @note the edict may not only be actors, but also items of course
343  * @sa G_TeamToPM
344  * @sa G_TestVis
345  * @sa G_AppearPerishEvent
346  * @sa G_CheckVisPlayer
347  * @sa G_CheckVisTeamAll
348  * @note If something appears, the needed information for those clients that are affected
349  * are also send in @c G_AppearPerishEvent
350  */
G_CheckVisTeam(const int team,Edict * check,const vischeckflags_t visFlags,const Edict * ent)351 static int G_CheckVisTeam (const int team, Edict* check, const vischeckflags_t visFlags, const Edict* ent)
352 {
353 	int status = 0;
354 
355 	/* check visibility */
356 	if (check->inuse) {
357 		/* check if he's visible */
358 		status |= G_DoTestVis(team, check, visFlags, G_TeamToPM(team), ent);
359 	}
360 
361 	return status;
362 }
363 
364 /**
365  * @brief Do @c G_CheckVisTeam for all entities
366  * ent is the one that is looking at the others
367  */
G_CheckVisTeamAll(const int team,const vischeckflags_t visFlags,const Edict * ent)368 int G_CheckVisTeamAll (const int team, const vischeckflags_t visFlags, const Edict* ent)
369 {
370 	Edict* chk = nullptr;
371 	int status = 0;
372 
373 	while ((chk = G_EdictsGetNextInUse(chk))) {
374 		status |= G_CheckVisTeam(team, chk, visFlags, ent);
375 	}
376 	return status;
377 }
378 
379 /**
380  * @brief Make everything visible to anyone who can't already see it
381  */
G_VisMakeEverythingVisible(void)382 void G_VisMakeEverythingVisible (void)
383 {
384 	Edict* ent = nullptr;
385 	while ((ent = G_EdictsGetNextInUse(ent))) {
386 		const int playerMask = G_VisToPM(ent->visflags);
387 		G_AppearPerishEvent(~playerMask, true, *ent, nullptr);
388 		if (G_IsActor(ent))
389 			G_SendInventory(~G_TeamToPM(ent->team), *ent);
390 	}
391 }
392 
393 /**
394  * @brief Check if the edict appears/perishes for the other teams. If they appear
395  * for other teams, the needed information for those clients are also send in
396  * @c G_CheckVisTeam resp. @c G_AppearPerishEvent
397  * @param[in] check The edict that is maybe seen by others. If nullptr, all edicts are checked
398  * @param visFlags Modifiers for the checks
399  * @sa G_CheckVisTeam
400  */
G_CheckVis(Edict * check,const vischeckflags_t visFlags)401 void G_CheckVis (Edict* check, const vischeckflags_t visFlags)
402 {
403 	int team;
404 
405 	for (team = 0; team < MAX_TEAMS; team++)
406 		if (level.num_alive[team]) {
407 			if (!check)	/* no special entity given, so check them all */
408 				G_CheckVisTeamAll(team, visFlags, nullptr);
409 			else
410 				G_CheckVisTeam(team, check, visFlags, nullptr);
411 		}
412 }
413 
414 /**
415  * @brief Reset the visflags for all edicts in the global list for the
416  * given team - and only for the given team
417  */
G_VisFlagsClear(int team)418 void G_VisFlagsClear (int team)
419 {
420 	Edict* ent = nullptr;
421 	const teammask_t teamMask = ~G_TeamToVisMask(team);
422 	while ((ent = G_EdictsGetNextInUse(ent))) {
423 		ent->visflags &= teamMask;
424 	}
425 }
426 
G_VisFlagsAdd(Edict & ent,teammask_t teamMask)427 void G_VisFlagsAdd (Edict &ent, teammask_t teamMask)
428 {
429 	ent.visflags |= teamMask;
430 }
431 
G_VisFlagsReset(Edict & ent)432 void G_VisFlagsReset (Edict &ent)
433 {
434 	ent.visflags = 0;
435 }
436 
G_VisFlagsSwap(Edict & ent,teammask_t teamMask)437 void G_VisFlagsSwap (Edict &ent, teammask_t teamMask)
438 {
439 	ent.visflags ^= teamMask;
440 }
441