1 /*
2 PLAYER.C
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 Saturday, December 11, 1993 10:25:55 AM
22 
23 Friday, September 30, 1994 5:48:25 PM (Jason)
24 	moved nearly all sounds out of the damage_definition structure and into shapes.
25 Wednesday, October 26, 1994 3:18:59 PM (Jason)
26 	invincible players are now damaged by fusion projectiles.
27 Wednesday, November 30, 1994 6:56:20 PM  (Jason)
28 	oxygen is used up faster by running and by firing.
29 Thursday, January 12, 1995 11:18:18 AM  (Jason')
30 	dead players don�t continue to use up oxygen.
31 Thursday, July 6, 1995 4:53:52 PM
32 	supports multi-player cooperative games. (Ryan)
33 
34 Feb 4, 2000 (Loren Petrich):
35 	Added SMG wielding stuff
36 
37 	Changed halt() to assert(false) for better debugging
38 
39 Feb 18, 2000 (Loren Petrich):
40 	Added support for a chase cam.
41 	Note that mark_player_collections() always loads the player sprites
42 	in expectation of a chase cam; this could be made to conditional on
43 	whether a chase cam will ever be active.
44 
45 Feb 21, 2000 (Loren Petrich):
46 	Changed NO_TELEPORTATION_DESTINATION to SHRT_MAX, an idiot-proof value,
47 	since there are unlikely to be that many polygons in a map.
48 
49 	Added upward and rightward shifts of the chase-cam position
50 
51 Feb 25, 2000 (Loren Petrich):
52 	Moved chase-cam data into preferences data; using accessor in "interface.h"
53 	Made it possible to swim under a liquid if one has the ball
54 
55 Feb 26, 2000 (Loren Petrich):
56 	Fixed level-0 teleportation bug; the hack is to move the destination
57 	down by 1.
58 
59 	Added chase-cam reset feature, for the purpose of doing chase-cam inertia.
60 	The reset is necessary to take into account teleporting or entering a level.
61 
62 Mar 2, 2000 (Loren Petrich):
63 	Moved the chase-cam stuff into ChaseCam.c/h
64 
65 Mar 22, 2000 (Loren Petrich):
66 	Added a function to revive_player() to reset the field of view properly
67 	when reviving
68 
69 May 14, 2000 (Loren Petrich):
70 	Added XML-configuration support for various player features
71 
72 May 22, 2000 (Loren Petrich):
73 	Added XML configurability for the powerup durations
74 
75 May 27, 2000 (Loren Petrich):
76 	Added oxygen depletion and replenishment rates
77 
78 Jun 11, 2000 (Loren Petrich):
79 	Pegging health and oxygen to maximum values when damaged;
80 	takes into account negative damage from healing projectiles.
81 	Also turned "agressor" into "aggressor".
82 
83 Jun 15, 2000 (Loren Petrich):
84 	Added support for Chris Pruett's Pfhortran
85 
86 Jun 28, 2000 (Loren Petrich):
87 	Generalized the invincibility-powerup vulnerability and added XML support for that
88 
89 Jul 1, 2000 (Loren Petrich):
90 	Added Benad's changes
91 
92 Jul 10, 2000 (Loren Petrich):
93 	Changed calculate_player_team() slightly; no more first vassert()
94 
95 Aug 31, 2000 (Loren Petrich):
96 	Added stuff for unpacking and packing
97 
98 Apr 27, 2001 (Loren Petrich):
99 	Made player guided missiles optional
100 
101 Oct 21, 2001 (Woody Zenfell):
102         Made player_shape_definitions available to the rest of the system -
103         in particular, so that SDL network dialog widgets can use it to render
104         player icons.
105 
106 Feb 20, 2002 (Woody Zenfell):
107     Ripped action_queue support out into new ActionQueues class (see ActionQueues.h)
108     Providing pointer gRealActionQueues to help others find the set of queues they are
109     accustomed to using.
110 
111 May 20, 2002 (Woody Zenfell):
112     get_ticks_since_local_player_in_terminal() mechanism
113 
114 Jan 12, 2003 (Woody Zenfell):
115 	Single entry point (reset_action_queues()) to reset all ActionQueues that need to be reset
116 
117 May 22, 2003 (Woody Zenfell):
118 	Fixing damaging polygon types; giving player netgame penalty feedback; announcing player
119 	net disconnects.
120 
121  May 27, 2003 (Woody Zenfell):
122 	I hear dead people.  (netmic, star protocol or newer only)
123 
124  June 14, 2003 (Woody Zenfell):
125 	update_players() now has a predictive mode of execution which takes many fewer actions
126 	(i.e. tries to alter only state like the player's location and facing etc.)
127 
128  May 21, 2004 (Alexei Svitkine):
129 	Made all the MML-settable stuff in this file have a ResetValues method that resets to
130 	old values (which we now save). Had to move some free-standing variables into structs
131 	for this.
132 */
133 
134 #define DONT_REPEAT_DEFINITIONS
135 
136 #include "cseries.h"
137 #include "map.h"
138 #include "player.h"
139 #include "monster_definitions.h"
140 #include "monsters.h"
141 #include "interface.h"
142 #include "SoundManager.h"
143 #include "fades.h"
144 #include "FilmProfile.h"
145 #include "media.h"
146 #include "items.h"
147 #include "weapons.h"
148 #include "game_window.h"
149 #include "computer_interface.h"
150 #include "projectiles.h"
151 #include "network_games.h"
152 #include "network.h"
153 #include "screen.h"
154 #include "shell.h" // for screen_printf()
155 #include "Console.h"
156 #include "ViewControl.h"
157 #include "InfoTree.h"
158 
159 /*
160 //anybody on the receiving pad of a teleport should explode (what happens to invincible guys?)
161 // Really should create a function that initializes the player state.
162 ??new players should teleport in
163 */
164 
165 // LP addition:
166 #include "ChaseCam.h"
167 #include "Packing.h"
168 #include "network.h"
169 
170 // ZZZ additions:
171 #include "ActionQueues.h"
172 
173 // jkvw addition:
174 #include "lua_script.h"
175 
176 #include <string.h>
177 #include <stdio.h>
178 #include <stdlib.h>
179 #include <limits.h>
180 
181 /* ---------- constants */
182 
183 struct player_powerup_durations_definition {
184 	short InvisibilityDuration;
185 	short InvincibilityDuration;
186 	short ExtravisionDuration;
187 	short InfravisionDuration;
188 };
189 
190 // These are variables, because they can be set with an XML parser
191 struct player_powerup_durations_definition player_powerup_durations = {
192 	70*TICKS_PER_SECOND,
193 	50*TICKS_PER_SECOND,
194 	3*TICKS_PER_MINUTE,
195 	3*TICKS_PER_MINUTE
196 };
197 
198 #define kINVISIBILITY_DURATION  player_powerup_durations.InvisibilityDuration
199 #define kINVINCIBILITY_DURATION player_powerup_durations.InvincibilityDuration
200 #define kEXTRAVISION_DURATION   player_powerup_durations.ExtravisionDuration
201 #define kINFRAVISION_DURATION   player_powerup_durations.InfravisionDuration
202 
203 #define MINIMUM_REINCARNATION_DELAY (TICKS_PER_SECOND)
204 #define NORMAL_REINCARNATION_DELAY (10*TICKS_PER_SECOND)
205 #define SUICIDE_REINCARNATION_DELAY (15*TICKS_PER_SECOND)
206 
207 #define DEAD_PLAYER_HEIGHT WORLD_ONE_FOURTH
208 
209 #define OXYGEN_WARNING_LEVEL TICKS_PER_MINUTE
210 #define OXYGEN_WARNING_FREQUENCY (TICKS_PER_MINUTE/4)
211 #define OXYGEN_WARNING_OFFSET (10*TICKS_PER_SECOND)
212 
213 #define LAST_LEVEL 100
214 
215 /* ---------- structures */
216 
217 // ZZZ: moved struct action_queue inside ActionQueues (see ActionQueues.cpp).
218 // ZZZ: moved struct player_shape_information to player.h for sharing
219 
220 struct damage_response_definition
221 {
222 	short type;
223 	short damage_threshhold; /* NONE is none, otherwise bumps fade by one if over threshhold */
224 
225 	short fade;
226 	short sound, death_sound, death_action;
227 };
228 
229 struct player_powerup_definition
230 {
231 	short Powerup_Invincibility;
232 	short Powerup_Invisibility;
233 	short Powerup_Infravision;
234 	short Powerup_Extravision;
235 	short Powerup_TripleEnergy;
236 	short Powerup_DoubleEnergy;
237 	short Powerup_Energy;
238 	short Powerup_Oxygen;
239 };
240 
241 /* ---------- globals */
242 
243 struct player_data *players;
244 struct damage_record team_damage_given[NUMBER_OF_TEAM_COLORS];
245 struct damage_record team_damage_taken[NUMBER_OF_TEAM_COLORS];
246 struct damage_record team_monster_damage_taken[NUMBER_OF_TEAM_COLORS];
247 struct damage_record team_monster_damage_given[NUMBER_OF_TEAM_COLORS];
248 struct damage_record team_friendly_fire[NUMBER_OF_TEAM_COLORS];
249 
250 player_data* local_player = nullptr;
251 player_data* current_player = nullptr;
252 short local_player_index = NONE;
253 short current_player_index = NONE;
254 
255 // ZZZ: Let folks ask for a pointer to the main set of ActionQueues.
256 static ActionQueues*   sRealActionQueues = NULL;
GetRealActionQueues()257 ActionQueues* GetRealActionQueues() { return sRealActionQueues; }
258 
259 static struct player_shape_definitions player_shapes=
260 {
261 	6, /* collection */
262 
263 	9, 8, /* dying hard, dying soft */
264 	11, 10, /* dead hard, dead soft */
265 	{7, 0, 0, 24, 23}, /* legs: stationary, walking, running, sliding, airborne */
266 	// LP additions: SMG-wielding/firing shapes (just before last two)
267 	{1, 3, 20, 26, 14, 12, 31, 16, 28, 33, 5, 18}, /* idle torsos: fists, magnum, fusion, assault, rocket, flamethrower, alien, shotgun, double pistol, double shotgun, da ball */
268 	{1, 3, 21, 26, 14, 12, 31, 16, 28, 33, 5, 18}, /* charging torsos: fists, magnum, fusion, assault, rocket, flamethrower, alien, shotgun, double pistol, double shotgun, ball */
269 	{2, 4, 22, 27, 15, 13, 32, 17, 28, 34, 6, 19}, /* firing torsos: fists, magnum, fusion, assault, rocket, flamethrower, alien, shotgun, double pistol, double shotgun, ball */
270 };
271 
272 #define NUMBER_OF_PLAYER_INITIAL_ITEMS (sizeof(player_initial_items)/sizeof(short))
273 static short player_initial_items[]=
274 {
275 	_i_magnum,  // First weapon is the weapon he will use...
276 	_i_knife,
277 	_i_knife,
278 	_i_magnum_magazine,
279 	_i_magnum_magazine,
280 	_i_magnum_magazine,
281 
282 	// LP additions, in case one wants to start very loaded
283      // AS: if we want to start loaded, shouldn't it be '_i_bong'?
284 	_i_knife,
285 	_i_knife,
286 	_i_knife,
287 	_i_knife,
288 	_i_knife,
289 
290 	_i_knife,
291 	_i_knife,
292 	_i_knife,
293 	_i_knife,
294 	_i_knife
295 };
296 
297 #define NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS (sizeof(damage_response_definitions)/sizeof(struct damage_response_definition))
298 
299 static struct damage_response_definition damage_response_definitions[]=
300 {
301 	{_damage_explosion, 100, _fade_yellow, NONE, _snd_human_scream, _monster_is_dying_hard},
302 	{_damage_crushing, NONE, _fade_red, NONE, _snd_human_wail, _monster_is_dying_hard},
303 	{_damage_projectile, NONE, _fade_red, NONE, _snd_human_scream, NONE},
304 	{_damage_shotgun_projectile, NONE, _fade_red, NONE, _snd_human_scream, NONE},
305 	{_damage_electrical_staff, NONE, _fade_cyan, NONE, _snd_human_scream, NONE},
306 	{_damage_hulk_slap, NONE, _fade_cyan, NONE, _snd_human_scream, NONE},
307 	{_damage_absorbed, 100, _fade_white, _snd_absorbed, NONE, NONE},
308 	{_damage_teleporter, 100, _fade_white, _snd_absorbed, NONE, NONE},
309 	{_damage_flame, NONE, _fade_orange, NONE, _snd_human_wail, _monster_is_dying_flaming},
310 	{_damage_hound_claws, NONE, _fade_red, NONE, _snd_human_scream, NONE},
311 	{_damage_compiler_bolt, NONE, _fade_static, NONE, _snd_human_scream, NONE},
312 	{_damage_alien_projectile, NONE, _fade_dodge_purple, NONE, _snd_human_wail, _monster_is_dying_flaming},
313 	{_damage_hunter_bolt, NONE, _fade_burn_green, NONE, _snd_human_scream, NONE},
314 	{_damage_fusion_bolt, 60, _fade_negative, NONE, _snd_human_scream, NONE},
315 	{_damage_fist, 40, _fade_red, NONE, _snd_human_scream, NONE},
316 	{_damage_yeti_claws, NONE, _fade_burn_cyan, NONE, _snd_human_scream, NONE},
317 	{_damage_yeti_projectile, NONE, _fade_dodge_yellow, NONE, _snd_human_scream, NONE},
318 	{_damage_defender, NONE, _fade_purple, NONE, _snd_human_scream, NONE},
319 	{_damage_lava, NONE, _fade_long_orange, NONE, _snd_human_wail, _monster_is_dying_flaming},
320 	{_damage_goo, NONE, _fade_long_green, NONE, _snd_human_wail, _monster_is_dying_flaming},
321 	{_damage_suffocation, NONE, NONE, NONE, _snd_suffocation, _monster_is_dying_soft},
322 	{_damage_energy_drain, NONE, NONE, NONE, NONE, NONE},
323 	{_damage_oxygen_drain, NONE, NONE, NONE, NONE, NONE},
324 	{_damage_hummer_bolt, NONE, _fade_flicker_negative, NONE, _snd_human_scream, NONE},
325 };
326 
327 // LP change: made this much bigger than the number of M2/Moo polygons
328 #define NO_TELEPORTATION_DESTINATION INT16_MAX
329 
330 // These are all configureable with MML.
331 struct player_settings_definition player_settings = {
332 	PLAYER_MAXIMUM_SUIT_ENERGY,    // InitialEnergy
333 	PLAYER_MAXIMUM_SUIT_OXYGEN,    // InitialOxygen
334 	PLAYER_MAXIMUM_SUIT_ENERGY/4,  // StrippedEnergy
335 	PLAYER_MAXIMUM_SUIT_ENERGY,    // SingleEnergy
336 	2*PLAYER_MAXIMUM_SUIT_ENERGY,  // DoubleEnergy
337 	3*PLAYER_MAXIMUM_SUIT_ENERGY,  // TripleEnergy
338 	FIXED_ONE_HALF,                // PlayerSelfLuminosity
339 	true,                          // CanSwim
340 	false,                         // PlayerShotsGuided
341 	QUARTER_CIRCLE/3,              // PlayerHalfVisualArc
342 	QUARTER_CIRCLE/3,              // PlayerHalfVertVisualArc
343 	31,                            // PlayerVisualRange
344 	31,                            // PlayerDarkVisualRange
345 	1,                             // OxygenDepletion
346 	0,                             // OxygenReplenishment
347 	0,                             // OxygenChange
348 	_damage_fusion_bolt            // Vulnerability
349 };
350 
351 // LP: the various powerup item ID's are changeable, but set to appropriate defaults here
352 struct player_powerup_definition player_powerups = {
353 	_i_invincibility_powerup,
354 	_i_invisibility_powerup,
355 	_i_infravision_powerup,
356 	_i_extravision_powerup,
357 	_i_triple_energy_powerup,
358 	_i_double_energy_powerup,
359 	_i_energy_powerup,
360 	_i_oxygen_powerup
361 };
362 
363 /* ---------- private prototypes */
364 
365 static void set_player_shapes(short player_index, bool animate);
366 static void revive_player(short player_index);
367 static void recreate_player(short player_index);
368 static void kill_player(short player_index, short aggressor_player_index, short action);
369 static void give_player_initial_items(short player_index);
370 static void get_player_transfer_mode(short player_index, short *transfer_mode, short *transfer_period);
371 static void set_player_dead_shape(short player_index, bool dying);
372 static void remove_dead_player_items(short player_index);
373 static void update_player_teleport(short player_index);
374 static void handle_player_in_vacuum(short player_index, uint32 action_flags);
375 static void update_player_media(short player_index);
376 static short calculate_player_team(short base_team);
377 
378 static void try_and_strip_player_items(short player_index);
379 
380 // LP additions:
381 static void ReplenishPlayerOxygen(short player_index, uint32 action_flags);
382 
383 // From AlexJLS patch; monster data necessary so that player as monster can be activated
384 // to make guided missiles work
385 static void adjust_player_physics(monster_data *me);
386 
387 
388 /* ---------- code */
389 
get_player_data(const size_t player_index)390 player_data *get_player_data(
391 	const size_t player_index)
392 {
393 	player_data *data = GetMemberWithBounds(players,player_index,dynamic_world->player_count);
394 	vassert(data,
395 		csprintf(temporary, "asked for player #%zu/#%d", player_index, dynamic_world->player_count));
396 
397 	return data;
398 }
399 
allocate_player_memory(void)400 void allocate_player_memory(
401 	void)
402 {
403 	/* allocate space for all our players */
404 	players= new player_data[MAXIMUM_NUMBER_OF_PLAYERS];
405 	assert(players);
406 
407 #ifdef BETA
408 	dprintf("#%d players at %p (%x bytes each) ---------------------------------------;g;", MAXIMUM_NUMBER_OF_PLAYERS, players, sizeof(struct player_data));
409 #endif
410 
411 	sRealActionQueues = new ActionQueues(MAXIMUM_NUMBER_OF_PLAYERS, ACTION_QUEUE_BUFFER_DIAMETER, false);
412 }
413 
414 /* returns player index */
new_player(short team,short color,short identifier,new_player_flags flags)415 short new_player(
416 	short team,
417 	short color,
418 	short identifier,
419 	new_player_flags flags)
420 {
421 	short player_index, loop;
422 	struct player_data *player;
423 
424 	/* find a free slot */
425 	player_index= dynamic_world->player_count;
426 	assert(player_index<MAXIMUM_NUMBER_OF_PLAYERS);
427 	dynamic_world->player_count += 1;
428 	player= get_player_data(player_index);
429 
430 	/* and initialize it */
431 	if (flags & new_player_make_local)
432 		set_local_player_index(player_index);
433 	if (flags & new_player_make_current)
434 		set_current_player_index(player_index);
435 	obj_clear(*player);
436 	player->teleporting_destination= NO_TELEPORTATION_DESTINATION;
437 	player->interface_flags= 0; // Doesn't matter-> give_player_initial_items will take care of it.
438 	// LP change: using variables for these
439 	player->suit_energy= player_settings.InitialEnergy;
440 	player->suit_oxygen= player_settings.InitialOxygen;
441 	player->color= color;
442 	player->team= team;
443 	player->flags= 0;
444 
445 	player->invincibility_duration= 0;
446 	player->invisibility_duration= 0;
447 	player->infravision_duration= 0;
448 	player->extravision_duration= 0;
449 	player->identifier= player_identifier_value(identifier);
450 
451 	SET_PLAYER_DOESNT_AUTO_SWITCH_WEAPONS_STATUS(player, player_identifier_doesnt_auto_switch_weapons(identifier));
452 
453 	/* initialize inventory */
454 	for (loop=0;loop<NUMBER_OF_ITEMS;++loop) player->items[loop]= NONE;
455 
456 	/* create the player.. */
457 	recreate_player(player_index);
458 
459 	/* Mark the player's inventory as dirty */
460 	mark_player_inventory_as_dirty(player_index, NONE);
461 	initialize_player_weapons(player_index);
462 
463 	/* give the player his initial items */
464 	give_player_initial_items(player_index);
465 	try_and_strip_player_items(player_index);
466 
467 	return player_index;
468 }
469 
walk_player_list(void)470 void walk_player_list(
471 	void)
472 {
473 	struct player_data *player;
474 	short player_index= current_player_index;
475 
476 	/* find the next player in the list we can look at and switch to them */
477 	do
478 	{
479 		if ((player_index+= 1)>=dynamic_world->player_count) player_index= 0;
480 		player= get_player_data(player_index);
481 	}
482 	while (!(GET_GAME_OPTIONS()&_overhead_map_is_omniscient) && local_player->team!=player->team);
483 
484 	if (current_player_index!=player_index)
485 	{
486 		set_current_player_index(player_index);
487 		update_interface(NONE);
488 		dirty_terminal_view(player_index); /* In case they are in terminal mode.. */
489 	}
490 }
491 
492 
493 // ZZZ: need to reset other queues now besides just the RealActionQueues
494 // This doesn't necessarily belong in this file, but I wasn't sure quite where to put it.
495 static void
reset_other_queues()496 reset_other_queues() {
497 	// Not sure if we want to do this (not my code), put here as a reminder
498 	//GetPfhortranActionQueues->reset();
499 
500 	reset_intermediate_action_queues();
501 }
502 
initialize_players(void)503 void initialize_players(
504 	void)
505 {
506 	short i;
507 
508 	/* no players */
509 	dynamic_world->player_count= 0;
510 	set_local_player_index(NONE);
511 	set_current_player_index(NONE);
512 
513 	/* reset the action flag queues and zero the player slots */
514 	for (i=0;i<MAXIMUM_NUMBER_OF_PLAYERS;++i)
515 	{
516 		obj_clear(players[i]);
517 	}
518 
519 	sRealActionQueues->reset();
520 	reset_other_queues();
521 
522 	for (i = 0; i < NUMBER_OF_TEAM_COLORS; i++) {
523 	  obj_clear(team_damage_given[i]);
524 	  obj_clear(team_damage_taken[i]);
525 	  obj_clear(team_monster_damage_taken[i]);
526 	  obj_clear(team_monster_damage_given[i]);
527 	  obj_clear(team_friendly_fire[i]);
528 	}
529 }
530 
531 /* This will be called by entering map for two reasons:
532  * 1) get rid of crap typed between levels, by accident.
533  * 2) loading a game doesn't currently reset the player queues, so garbage will cause lags.
534  */
535 /* The above comment is stale.  Now loading map calls this and so does new_game. Calling this */
536 /*  from entering map would bone us. */
537 static void
reset_player_queues()538 reset_player_queues()
539 {
540 	sRealActionQueues->reset();
541 	reset_recording_and_playback_queues();
542 	sync_heartbeat_count(); //���MY ADDITION...
543 }
544 
545 // ZZZ addition: need to reset (potentially) multiple sets of ActionQueues, not just the RealActionQueues.
546 // This function doesn't necessarily belong in this file, but I wasn't sure where else to put it.
547 void
reset_action_queues()548 reset_action_queues()
549 {
550 	reset_player_queues();
551 	reset_other_queues();
552 }
553 
554 // ZZZ: queue_action_flags() replaced by ActionQueues::enqueueActionFlags()
555 // ZZZ: dequeue_action_flags() replaced by ActionQueues::dequeueActionFlags()
556 // ZZZ: get_action_queue_size() replaced by ActionQueues::countActionFlags()
557 
558 
559 // ZZZ addition: keep track of the number of ticks since the local player was in terminal mode
560 // Note this mechanism is not very careful; should not be used for _important_ decisions.
561 static int  sLocalPlayerTicksSinceTerminal = 1 * TICKS_PER_MINUTE;
562 
563 int
get_ticks_since_local_player_in_terminal()564 get_ticks_since_local_player_in_terminal() {
565     return sLocalPlayerTicksSinceTerminal;
566 }
567 
m1_solo_player_in_terminal()568 bool m1_solo_player_in_terminal()
569 {
570 	return (static_world->environment_flags & _environment_terminals_stop_time)
571 		&& (dynamic_world->player_count == 1)
572 		&& player_in_terminal_mode(local_player_index);
573 }
574 
update_m1_solo_player_in_terminal(ActionQueues * inActionQueuesToUse)575 void update_m1_solo_player_in_terminal(ActionQueues* inActionQueuesToUse)
576 {
577 	update_player_keys_for_terminal(local_player_index, inActionQueuesToUse->dequeueActionFlags(local_player_index));
578 	update_player_for_terminal_mode(local_player_index);
579 	sLocalPlayerTicksSinceTerminal = 0;
580 }
581 
582 /* assumes �t==1 tick */
update_players(ActionQueues * inActionQueuesToUse,bool inPredictive)583 void update_players(ActionQueues* inActionQueuesToUse, bool inPredictive)
584 {
585 	struct player_data *player;
586 	short player_index;
587 
588 	if(!inPredictive)
589 	{
590 		// ZZZ: update ticks-since-terminal stuff
591 		sLocalPlayerTicksSinceTerminal++;
592 		if(player_in_terminal_mode(local_player_index))
593 			sLocalPlayerTicksSinceTerminal = 0;
594 	}
595 
596 	for (player_index= 0, player= players; player_index<dynamic_world->player_count; ++player_index, ++player)
597 	{
598 		uint32 action_flags = inActionQueuesToUse->dequeueActionFlags(player_index);
599 
600 		if (action_flags == 0xffffffff)
601 		{
602 			// net dead
603 			if(!player->netdead && !inPredictive)
604 			{
605 				if (!PLAYER_IS_DEAD(player))
606 				{
607 					// kills invincible players, too  (ZZZ annotation: unless physics model says otherwise)
608 					detonate_projectile(&player->location, player->camera_polygon_index, _projectile_minor_fusion_dispersal,
609 			 NONE, NONE, 10*FIXED_ONE);
610 				}
611 
612 				screen_printf("%s has become disconnected", player->name);
613 				player->netdead = true;
614 			}
615 
616 			action_flags= 0;
617 		}
618 
619 		if (PLAYER_IS_TELEPORTING(player)) action_flags= 0;
620 
621 		/* Deal with the terminal mode crap. */
622 		if (player_in_terminal_mode(player_index))
623 		{
624 			if(!inPredictive)
625 			{
626 				update_player_keys_for_terminal(player_index, action_flags);
627 				update_player_for_terminal_mode(player_index);
628 			}
629 			action_flags= 0;
630 		}
631 
632 		bool IsSwimming = TEST_FLAG(player->variables.flags,_HEAD_BELOW_MEDIA_BIT) && player_settings.CanSwim;
633 
634 		// if we�ve got the ball we can�t run (that sucks)
635 		// Benad: also works with _game_of_rugby and _game_of_capture_the_flag
636 		// LP change: made it possible to swim under a liquid if one has the ball
637 		// START Benad changed oct. 1st (works with ANY ball color, d'uh...)
638 		if ((GET_GAME_TYPE()==_game_of_kill_man_with_ball)
639 		 && dynamic_world->game_player_index==player_index && !IsSwimming) action_flags&= ~_run_dont_walk;
640 
641 		if ((((GET_GAME_TYPE()==_game_of_rugby) || (GET_GAME_TYPE()==_game_of_capture_the_flag)) && (find_player_ball_color(player_index) != NONE))
642 			&& !IsSwimming) action_flags&= ~_run_dont_walk;
643 		// END Benad changed oct. 1st
644 
645 		// if (GET_GAME_TYPE()==_game_of_kill_man_with_ball && dynamic_world->game_player_index==player_index) action_flags&= ~_run_dont_walk;
646 
647 		// if our head is under media, we can�t run (that sucks, too)
648 		if (IsSwimming && (action_flags&_run_dont_walk)) action_flags&= ~_run_dont_walk, action_flags|= _swim;
649 
650 		update_player_physics_variables(player_index, action_flags, inPredictive);
651 
652 		if(!inPredictive)
653 		{
654 			player->invisibility_duration= FLOOR(player->invisibility_duration-1, 0);
655 			player->invincibility_duration= FLOOR(player->invincibility_duration-1, 0);
656 			player->infravision_duration= FLOOR(player->infravision_duration-1, 0);
657 			// ZZZ: alert the player when he can respawn, if he was being penalized
658 			if(player->reincarnation_delay > 0)
659 			{
660 				--player->reincarnation_delay;
661 				short message_player_index = local_player_index;
662 				if((get_game_controller() == _replay) || (get_game_controller() == _demo))
663 				{
664 					message_player_index = current_player_index;
665 				}
666 				if(((GET_GAME_OPTIONS()&_suicide_is_penalized) || (GET_GAME_OPTIONS()&_dying_is_penalized)) && (player_index == message_player_index))
667 				{
668 					if(player->reincarnation_delay == 0)
669 						screen_printf("You may rise to fight again");
670 					else if(player->reincarnation_delay < 4 * TICKS_PER_SECOND && (player->reincarnation_delay % TICKS_PER_SECOND) == 0)
671 						screen_printf("%d...", player->reincarnation_delay / TICKS_PER_SECOND);
672 				}
673 			}
674 			if (player->extravision_duration)
675 			{
676 				if (!(player->extravision_duration-= 1))
677 				{
678 					if (player_index==current_player_index) start_extravision_effect(false);
679 				}
680 			}
681 			// LP change: made this code more general;
682 			// find the oxygen-change rate appropriate to each environment,
683 			// then handle the rate appropriately.
684 			if ((static_world->environment_flags&_environment_vacuum) || (player->variables.flags&_HEAD_BELOW_MEDIA_BIT))
685 				player_settings.OxygenChange = - player_settings.OxygenDepletion;
686 			else
687 				player_settings.OxygenChange = player_settings.OxygenReplenishment;
688 
689 			if (player_settings.OxygenChange < 0)
690 				handle_player_in_vacuum(player_index, action_flags);
691 			else if (player_settings.OxygenChange > 0)
692 				ReplenishPlayerOxygen(player_index, action_flags);
693 
694 			// if ((static_world->environment_flags&_environment_vacuum) || (player->variables.flags&_HEAD_BELOW_MEDIA_BIT)) handle_player_in_vacuum(player_index, action_flags);
695 
696 #if !defined(DISABLE_NETWORKING)
697 			/* handle arbitration of the communications channel (i.e., dynamic_world->speaking_player_index) */
698 			if (action_flags&_microphone_button)
699 			{
700 				if (dynamic_world->speaking_player_index==NONE)
701 				{
702 					if (GET_GAME_OPTIONS() & _force_unique_teams || (get_player_data(player_index)->team == get_player_data(local_player_index)->team))
703 					{
704 						dynamic_world->speaking_player_index= player_index;
705 					}
706 
707 					if (player_index==local_player_index) set_interface_microphone_recording_state(true);
708 				}
709 			}
710 			else
711 			{
712 				if (dynamic_world->speaking_player_index==player_index)
713 				{
714 					dynamic_world->speaking_player_index= NONE;
715 					if (player_index==local_player_index) set_interface_microphone_recording_state(false);
716 				}
717 			}
718 #endif // !defined(DISABLE_NETWORKING)
719 
720 			if (PLAYER_IS_DEAD(player))
721 			{
722 				/* do things dead players do (sit around and check for self-reincarnation) */
723 				if (PLAYER_HAS_MAP_OPEN(player))
724 					SET_PLAYER_MAP_STATUS(player, false);
725 				if (PLAYER_IS_TOTALLY_DEAD(player) && (action_flags&_action_trigger_state) && (player->variables.action==_player_stationary||dynamic_world->player_count==1))
726 				{
727 					// ZZZ: let the player know why he's not respawning
728 					if(player->reincarnation_delay)
729 					{
730 						short message_player_index = local_player_index;
731 						if((get_game_controller() == _replay) || (get_game_controller() == _demo))
732 						{
733 							message_player_index = current_player_index;
734 						}
735 						if(player_index == message_player_index)
736 						{
737 							int theSeconds = player->reincarnation_delay / TICKS_PER_SECOND;
738 							// If 3 or less, he'll be getting a countdown anyway, and may start spamming the action key.
739 							if(theSeconds > 3)
740 								screen_printf("%d penalty seconds remain", theSeconds);
741 						}
742 					}
743 					else
744 					{
745 						if (dynamic_world->player_count == 1)
746 						{
747 							set_game_state(_revert_game);
748 						}
749 						else revive_player(player_index);
750 					}
751 				}
752 				update_player_weapons(player_index, 0);
753 				update_action_key(player_index, false);
754 			}
755 			else
756 			{
757 				/* do things live players do (get items, update weapons, check action key, breathe) */
758 				swipe_nearby_items(player_index);
759 				update_player_weapons(player_index, action_flags);
760 				update_action_key(player_index, (action_flags&_action_trigger_state) ? true : false);
761 				if (action_flags&_toggle_map)
762 					SET_PLAYER_MAP_STATUS(player, !PLAYER_HAS_MAP_OPEN(player));
763 
764 				// ZZZ: moved this here out of "player becoming netdead" area above; that looked wrong
765 				// AlexJLS patch: effect of dangerous polygons
766 				cause_polygon_damage(player->supporting_polygon_index,player->monster_index);
767 			}
768 
769 			update_player_teleport(player_index);
770 			update_player_media(player_index);
771 			set_player_shapes(player_index, true);
772 
773 		} // !inPredictive
774 
775 	} // loop over players
776 
777 } // update_players()
778 
779 
780 
damage_player(short monster_index,short aggressor_index,short aggressor_type,struct damage_definition * damage,short projectile_index)781 void damage_player(
782 	short monster_index,
783 	short aggressor_index,
784 	short aggressor_type,
785 	struct damage_definition *damage,
786 	short projectile_index)
787 {
788 	short player_index= monster_index_to_player_index(monster_index);
789 	short aggressor_player_index= NONE; /* will be valid if the aggressor is a player */
790 	struct player_data *player= get_player_data(player_index);
791 	short damage_amount= calculate_damage(damage);
792 	short damage_type= damage->type;
793 	struct damage_response_definition *definition;
794 
795 	(void) (aggressor_type);
796 
797 	// LP change: made this more general
798 	if (player->invincibility_duration && damage->type!=player_settings.Vulnerability)
799 	{
800 		damage_type= _damage_absorbed;
801 	}
802 
803 	{
804 		unsigned i;
805 
806 		for (i=0,definition=damage_response_definitions;
807 				definition->type!=damage_type && i<NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS;
808 				++i,++definition)
809 			;
810 		vwarn(i!=NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS, csprintf(temporary, "can't react to damage type #%d", damage_type));
811 		// vassert(i!=NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS, csprintf(temporary, "can't react to damage type #%d", damage_type));
812 	}
813 
814 	if (damage_type!=_damage_absorbed)
815 	{
816 		/* record damage taken */
817 		if (aggressor_index!=NONE)
818 		{
819 			struct monster_data *aggressor= get_monster_data(aggressor_index);
820 
821 			if (!PLAYER_IS_DEAD(player))
822 			{
823 				if (MONSTER_IS_PLAYER(aggressor))
824 				{
825 					struct player_data *aggressor_player;
826 
827 					aggressor_player_index= monster_index_to_player_index(aggressor_index);
828 					aggressor_player= get_player_data(aggressor_player_index);
829 					player->damage_taken[aggressor_player_index].damage+= damage_amount;
830 					aggressor_player->total_damage_given.damage+= damage_amount;
831 					team_damage_taken[player->team].damage += damage_amount;
832 					team_damage_given[aggressor_player->team].damage += damage_amount;
833 					if (player->team == aggressor_player->team) {
834 					  team_friendly_fire[player->team].damage += damage_amount;
835 					}
836 				}
837 				else
838 				{
839 					player->monster_damage_taken.damage+= damage_amount;
840 					team_monster_damage_taken[player->team].damage+= damage_amount;
841 				}
842 			}
843 		}
844 
845 		switch (damage->type)
846 		{
847 			case _damage_oxygen_drain:
848 			{
849 				// LP change: pegging to maximum value
850 				player->suit_oxygen= int16(MIN(int32(player->suit_oxygen)-int32(damage_amount),int32(INT16_MAX)));
851 				L_Call_Player_Damaged(player_index, aggressor_player_index, aggressor_index, damage->type, damage_amount, projectile_index);
852 				if (player->suit_oxygen < 0) player->suit_oxygen= 0;
853 				if (player_index==current_player_index) mark_oxygen_display_as_dirty();
854 				break;
855 			}
856 			default:
857 			{
858 				// LP change: pegging to maximum value
859 				player->suit_energy= int16(MIN(int32(player->suit_energy)-int32(damage_amount),int32(INT16_MAX)));
860 				L_Call_Player_Damaged(player_index, aggressor_player_index, aggressor_index, damage->type, damage_amount, projectile_index);
861 				/* damage the player, recording the kill if the aggressor was another player and we died */
862 				if (player->suit_energy<0)
863 				{
864 					if (damage->type!=_damage_energy_drain)
865 					{
866 						if (!PLAYER_IS_DEAD(player))
867 						{
868 							short action= definition->death_action;
869 
870 							play_object_sound(player->object_index, definition->death_sound);
871 
872 							if (action==NONE)
873 							{
874 								action= (damage_amount>PLAYER_MAXIMUM_SUIT_ENERGY/2) ? _monster_is_dying_hard : _monster_is_dying_soft;
875 							}
876 
877 							kill_player(player_index, aggressor_player_index, action);
878 #if !defined(DISABLE_NETWORKING)
879 							if (aggressor_player_index!=NONE)
880 							{
881 								struct player_data *aggressor_player= get_player_data(aggressor_player_index);
882 
883 								if (player_killed_player(player_index, aggressor_player_index))
884 								{
885 									player->damage_taken[aggressor_player_index].kills+= 1;
886 									team_damage_taken[player->team].kills += 1;
887 									if (aggressor_player_index != player_index)
888 									{
889 										aggressor_player->total_damage_given.kills+= 1;
890 										team_damage_given[aggressor_player->team].kills += 1;
891 									}
892 									if (player->team == aggressor_player->team) {
893 									  team_friendly_fire[aggressor_player->team].kills += 1;
894 									}
895 								}
896 								Console::instance()->report_kill(player_index, aggressor_player_index, projectile_index);
897 							}
898 							else
899 #endif // !defined(DISABLE_NETWORKING)
900 							{
901 								player->monster_damage_taken.kills+= 1;
902 								team_monster_damage_taken[player->team].kills += 1;
903 							}
904 							L_Call_Player_Killed (player_index, aggressor_player_index, action, projectile_index);
905 						}
906 
907 						player->suit_oxygen= 0;
908 						if (player_index==current_player_index) mark_oxygen_display_as_dirty();
909 					}
910 
911 					player->suit_energy= 0;
912 				}
913 				break;
914 			}
915 		}
916 	}
917 
918 	{
919 		if (!PLAYER_IS_DEAD(player)) play_object_sound(player->object_index, definition->sound);
920 		if (player_index==current_player_index)
921 		{
922 			if (definition->fade!=NONE) start_fade((definition->damage_threshhold!=NONE&&damage_amount>definition->damage_threshhold) ? (definition->fade+1) : definition->fade);
923 			if (damage_amount) mark_shield_display_as_dirty();
924 		}
925 	}
926 
927 	if(player_in_terminal_mode(player_index))
928 	{
929 		abort_terminal_mode(player_index);
930 	}
931 }
932 
player_identifier_to_player_index(short player_identifier)933 short player_identifier_to_player_index(
934 	short player_identifier)
935 {
936 	struct player_data *player;
937 	short player_index;
938 
939 	for (player_index=0;player_index<dynamic_world->player_count;++player_index)
940 	{
941 		player= get_player_data(player_index);
942 
943 		if (player->identifier==player_identifier) break;
944 	}
945 	assert(player_index!=dynamic_world->player_count);
946 
947 	return player_index;
948 }
949 
mark_player_collections(bool loading)950 void mark_player_collections(
951 	bool loading)
952 {
953 	mark_collection(player_shapes.collection, loading);
954 	// LP change: unload player shapes for single-player game only if
955 	// a chase cam cannot exist;
956 	if (!ChaseCam_CanExist())
957 		if (dynamic_world->player_count==1&&loading) strip_collection(player_shapes.collection);
958 
959 	mark_weapon_collections(loading);
960 	mark_item_collections(loading);
961 	mark_interface_collections(loading);
962 }
963 
964 player_shape_definitions*
get_player_shape_definitions()965 get_player_shape_definitions() {
966     return &player_shapes;
967 }
968 
set_local_player_index(short player_index)969 void set_local_player_index(
970 	short player_index)
971 {
972 	local_player_index= player_index;
973 	local_player = player_index == NONE ? nullptr : get_player_data(player_index);
974 }
975 
set_current_player_index(short player_index)976 void set_current_player_index(
977 	short player_index)
978 {
979 	current_player_index= player_index;
980 	current_player = player_index == NONE ? nullptr : get_player_data(player_index);
981 }
982 
983 /* We just teleported in as it were-> recreate all the players..  */
recreate_players_for_new_level(void)984 void recreate_players_for_new_level(
985 	void)
986 {
987 	short player_index;
988 
989 	for (player_index= 0; player_index<dynamic_world->player_count; ++player_index)
990 	{
991 		/* Recreate all of the players for the new level.. */
992 		recreate_player(player_index);
993 	}
994 }
995 
team_damage_from_player_data(void)996 void team_damage_from_player_data(void)
997 {
998   for (short player_index = 0; player_index < dynamic_world->player_count; player_index++) {
999     struct player_data *player = get_player_data(player_index);
1000     team_damage_given[player->team].damage += player->total_damage_given.damage;
1001     team_damage_given[player->team].kills += player->total_damage_given.kills;
1002     team_monster_damage_given[player->team].damage += player->monster_damage_given.damage;
1003     team_monster_damage_given[player->team].kills += player->monster_damage_given.kills;
1004     team_monster_damage_taken[player->team].damage += player->monster_damage_taken.damage;
1005     team_monster_damage_taken[player->team].kills += player->monster_damage_taken.kills;
1006     for (short opponent_index = 0; opponent_index < dynamic_world->player_count; opponent_index++) {
1007       struct player_data *opponent = get_player_data(player_index);
1008       team_damage_taken[player->team].damage += player->damage_taken[opponent_index].damage;
1009       team_damage_taken[player->team].kills += player->damage_taken[opponent_index].kills;
1010       if (player->team == opponent->team) {
1011 	team_friendly_fire[player->team].damage += player->damage_taken[opponent_index].damage;
1012 	team_friendly_fire[player->team].kills += player->damage_taken[opponent_index].kills;
1013       }
1014     }
1015   }
1016 }
1017 
monster_index_to_player_index(short monster_index)1018 short monster_index_to_player_index(
1019 	short monster_index)
1020 {
1021 	struct player_data *player;
1022 	short player_index;
1023 
1024 	for (player_index=0;player_index<dynamic_world->player_count;++player_index)
1025 	{
1026 		player= get_player_data(player_index);
1027 		if (player->monster_index==monster_index) break;
1028 	}
1029 	assert(player_index!=dynamic_world->player_count);
1030 
1031 	return player_index;
1032 }
1033 
get_polygon_index_supporting_player(short monster_index)1034 short get_polygon_index_supporting_player(
1035 	short monster_index)
1036 {
1037 	short player_index= monster_index_to_player_index(monster_index);
1038 	struct player_data *player= get_player_data(player_index);
1039 
1040 	return player->supporting_polygon_index;
1041 }
1042 
legal_player_powerup(short player_index,short item_index)1043 bool legal_player_powerup(
1044 	short player_index,
1045 	short item_index)
1046 {
1047 	struct player_data *player= get_player_data(player_index);
1048 	bool legal= true;
1049 
1050 	if (item_index == player_powerups.Powerup_Invincibility)
1051 	{
1052 		if (player->invincibility_duration) legal= false;
1053 	}
1054 	else if (item_index == player_powerups.Powerup_Invisibility)
1055 	{
1056 		if (player->invisibility_duration>kINVISIBILITY_DURATION) legal= false;
1057 	}
1058 	else if (item_index == player_powerups.Powerup_Infravision)
1059 	{
1060 		if (player->infravision_duration) legal= false;
1061 	}
1062 	else if (item_index == player_powerups.Powerup_Extravision)
1063 	{
1064 		if (player->extravision_duration) legal= false;
1065 	}
1066 	else if (item_index == player_powerups.Powerup_TripleEnergy)
1067 	{
1068 		if (player->suit_energy>=player_settings.TripleEnergy) legal= false;
1069 	}
1070 	else if (item_index == player_powerups.Powerup_DoubleEnergy)
1071 	{
1072 		if (player->suit_energy>=player_settings.DoubleEnergy) legal= false;
1073 	}
1074 	else if (item_index == player_powerups.Powerup_Energy)
1075 	{
1076 		if (player->suit_energy>=player_settings.SingleEnergy) legal= false;
1077 	}
1078 	else if (item_index == player_powerups.Powerup_Oxygen)
1079 	{
1080 		if (player->suit_oxygen>=5*PLAYER_MAXIMUM_SUIT_OXYGEN/6) legal= false;
1081 	}
1082 
1083 	return legal;
1084 }
1085 
process_player_powerup(short player_index,short item_index)1086 void process_player_powerup(
1087 	short player_index,
1088 	short item_index)
1089 {
1090 	struct player_data *player= get_player_data(player_index);
1091 
1092 	if (item_index == player_powerups.Powerup_Invincibility)
1093 	{
1094 		player->invincibility_duration+= kINVINCIBILITY_DURATION;
1095 	}
1096 	else if (item_index == player_powerups.Powerup_Invisibility)
1097 	{
1098 		player->invisibility_duration+= kINVISIBILITY_DURATION;
1099 	}
1100 	else if (item_index == player_powerups.Powerup_Infravision)
1101 	{
1102 		player->infravision_duration+= kINFRAVISION_DURATION;
1103 	}
1104 	else if (item_index == player_powerups.Powerup_Extravision)
1105 	{
1106 		if (player_index==current_player_index) start_extravision_effect(true);
1107 		player->extravision_duration+= kEXTRAVISION_DURATION;
1108 	}
1109 	else if (item_index == player_powerups.Powerup_TripleEnergy)
1110 	{
1111 		if (player->suit_energy<player_settings.TripleEnergy)
1112 		{
1113 			player->suit_energy= player_settings.TripleEnergy;
1114 			if (player_index==current_player_index) mark_shield_display_as_dirty();
1115 		}
1116 	}
1117 	else if (item_index == player_powerups.Powerup_DoubleEnergy)
1118 	{
1119 		if (player->suit_energy<player_settings.DoubleEnergy)
1120 		{
1121 			player->suit_energy= player_settings.DoubleEnergy;
1122 			if (player_index==current_player_index) mark_shield_display_as_dirty();
1123 		}
1124 	}
1125 	else if (item_index == player_powerups.Powerup_Energy)
1126 	{
1127 		if (player->suit_energy<player_settings.SingleEnergy)
1128 		{
1129 			player->suit_energy= player_settings.SingleEnergy;
1130 			if (player_index==current_player_index) mark_shield_display_as_dirty();
1131 		}
1132 	}
1133 	else if (item_index == player_powerups.Powerup_Oxygen)
1134 	{
1135 		player->suit_oxygen= CEILING(player->suit_oxygen+PLAYER_MAXIMUM_SUIT_OXYGEN/2, PLAYER_MAXIMUM_SUIT_OXYGEN);
1136 		if (player_index==current_player_index) mark_oxygen_display_as_dirty();
1137 	}
1138 }
1139 
dead_player_minimum_polygon_height(short polygon_index)1140 world_distance dead_player_minimum_polygon_height(
1141 	short polygon_index)
1142 {
1143 	short player_index;
1144 	struct player_data *player;
1145 	world_distance minimum_height= 0;
1146 
1147 	for (player_index= 0, player= players; player_index<dynamic_world->player_count; ++player_index, ++player)
1148 	{
1149 		if (polygon_index==player->camera_polygon_index)
1150 		{
1151 			if (PLAYER_IS_DEAD(player)) minimum_height= DEAD_PLAYER_HEIGHT;
1152 			break;
1153 		}
1154 	}
1155 
1156 	return minimum_height;
1157 }
1158 
try_and_subtract_player_item(short player_index,short item_type)1159 bool try_and_subtract_player_item(
1160 	short player_index,
1161 	short item_type)
1162 {
1163 	struct player_data *player= get_player_data(player_index);
1164 	bool found_one= false;
1165 
1166 	assert(item_type>=0 && item_type<NUMBER_OF_ITEMS);
1167 	if (player->items[item_type]>=0)
1168 	{
1169 		if (!(player->items[item_type]-= 1)) player->items[item_type]= NONE;
1170 		mark_player_inventory_as_dirty(player_index, item_type);
1171 		found_one= true;
1172 	}
1173 
1174 	return found_one;
1175 }
1176 
1177 /* ---------- private prototypes */
1178 
1179 // LP change: assumes nonpositive change rate
handle_player_in_vacuum(short player_index,uint32 action_flags)1180 static void handle_player_in_vacuum(
1181 	short player_index,
1182 	uint32 action_flags)
1183 {
1184 	struct player_data *player= get_player_data(player_index);
1185 
1186 	if (player->suit_oxygen>0)
1187 	{
1188 		short breathing_frequency;
1189 
1190 		// lolbungie
1191 		breathing_frequency = TICKS_PER_MINUTE/2;
1192 		/*
1193 		switch (player->suit_oxygen/TICKS_PER_MINUTE)
1194 		{
1195 			case 0: breathing_frequency= TICKS_PER_MINUTE/6;
1196 			case 1: breathing_frequency= TICKS_PER_MINUTE/5;
1197 			case 2: breathing_frequency= TICKS_PER_MINUTE/4;
1198 			case 3: breathing_frequency= TICKS_PER_MINUTE/3;
1199 			default: breathing_frequency= TICKS_PER_MINUTE/2;
1200 		}
1201 		 */
1202 
1203 		assert(player_settings.OxygenChange <= 0);
1204 		short oxygenChange = player_settings.OxygenChange;
1205 		switch (dynamic_world->game_information.difficulty_level)
1206 		{
1207 			case _total_carnage_level:
1208 				if (action_flags&_run_dont_walk) oxygenChange+= player_settings.OxygenChange;
1209 			case _major_damage_level:
1210 				if (action_flags&(_left_trigger_state|_right_trigger_state)) oxygenChange+= player_settings.OxygenChange;
1211 				break;
1212 		}
1213 
1214 		while (oxygenChange < 0)
1215 		{
1216 			player->suit_oxygen -= 1;
1217 			oxygenChange += 1;
1218 			if (!(player->suit_oxygen%breathing_frequency))
1219 				SoundManager::instance()->PlayLocalSound(Sound_Breathing());
1220 			if ((player->suit_oxygen+OXYGEN_WARNING_OFFSET)<OXYGEN_WARNING_LEVEL && !((player->suit_oxygen+OXYGEN_WARNING_OFFSET)%OXYGEN_WARNING_FREQUENCY))
1221 				SoundManager::instance()->PlayLocalSound(Sound_OxygenWarning());
1222 		}
1223 
1224 		if (player->suit_oxygen<=0)
1225 		{
1226 			struct damage_definition damage;
1227 
1228 			damage.flags= 0;
1229 			damage.type= _damage_suffocation;
1230 			damage.base= player->suit_energy+1;
1231 			damage.random= 0;
1232 			damage.scale= FIXED_ONE;
1233 
1234 			damage_player(player->monster_index, NONE, NONE, &damage, NONE);
1235 		}
1236 	}
1237 }
1238 
1239 // LP: assumes nonnegative change rate
ReplenishPlayerOxygen(short player_index,uint32 action_flags)1240 static void ReplenishPlayerOxygen(short player_index, uint32 action_flags)
1241 {
1242 	(void)(action_flags);
1243 
1244 	struct player_data *player= get_player_data(player_index);
1245 
1246 	// Be careful to avoid short-integer wraparound
1247 	assert(player_settings.OxygenChange >= 0);
1248 	if (player->suit_oxygen < PLAYER_MAXIMUM_SUIT_OXYGEN)
1249 	{
1250 		if (player->suit_oxygen < PLAYER_MAXIMUM_SUIT_OXYGEN - player_settings.OxygenChange)
1251 			player->suit_oxygen += player_settings.OxygenChange;
1252 		else
1253 			player->suit_oxygen = PLAYER_MAXIMUM_SUIT_OXYGEN;
1254 	}
1255 }
1256 
update_player_teleport(short player_index)1257 static void update_player_teleport(
1258 	short player_index)
1259 {
1260 	struct player_data *player= get_player_data(player_index);
1261 	struct monster_data *monster= get_monster_data(player->monster_index);
1262 	struct object_data *object= get_object_data(monster->object_index);
1263 	struct polygon_data *polygon= get_polygon_data(object->polygon);
1264 	bool player_was_interlevel_teleporting= false;
1265 
1266 	/* This is the players that are carried across the teleport unwillingly.. */
1267 	if(PLAYER_IS_INTERLEVEL_TELEPORTING(player))
1268 	{
1269 		player_was_interlevel_teleporting= true;
1270 
1271 		player->interlevel_teleport_phase+= 1;
1272 		switch(player->interlevel_teleport_phase)
1273 		{
1274 			case PLAYER_TELEPORTING_DURATION:
1275 				monster->action= _monster_is_moving;
1276 				SET_PLAYER_TELEPORTING_STATUS(player, false);
1277 				SET_PLAYER_INTERLEVEL_TELEPORTING_STATUS(player, false);
1278 				break;
1279 
1280 			/* +1 because they are teleporting to a new level, and we want the squeeze in to happen */
1281 			/*  after the level transition */
1282 			case PLAYER_TELEPORTING_MIDPOINT+1:
1283 				/* Either the player is teleporting, or everyone is. (level change) */
1284 				if(player_index==current_player_index)
1285 				{
1286 					if (View_DoInterlevelTeleportInEffects()) {
1287 						start_teleporting_effect(false);
1288 						play_object_sound(player->object_index, Sound_TeleportIn());
1289 					}
1290 				}
1291 				player->teleporting_destination= NO_TELEPORTATION_DESTINATION;
1292 				break;
1293 
1294 			default:
1295 				break;
1296 		}
1297 	}
1298 
1299 	if (PLAYER_IS_TELEPORTING(player))
1300 	{
1301 		switch (player->teleporting_phase+= 1)
1302 		{
1303 			case PLAYER_TELEPORTING_MIDPOINT:
1304 				if(player->teleporting_destination>=0) /* Intralevel. */
1305 				{
1306 					short destination_polygon_index= player->teleporting_destination;
1307 					struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
1308 					struct damage_definition damage;
1309 					world_point3d destination;
1310 
1311 					/* Determine where we are going. */
1312 					destination.x= destination_polygon->center.x;
1313 					destination.y= destination_polygon->center.y;
1314 					destination.z= destination_polygon->floor_height;
1315 
1316 					damage.type= _damage_teleporter;
1317 					damage.base= damage.random= damage.flags= damage.scale= 0;
1318 					damage_monsters_in_radius(NONE, NONE, NONE, &destination, destination_polygon_index,
1319 						WORLD_ONE, &damage, NONE);
1320 
1321 					translate_map_object(player->object_index, &destination, destination_polygon_index);
1322 					initialize_player_physics_variables(player_index);
1323 
1324 					// LP addition: handles the current player's chase cam;
1325 					// in screen.c, we find that it's the current player whose view gets rendered
1326 					if (player_index == current_player_index) ChaseCam_Reset();
1327 				} else { /* -level number is the interlevel */
1328 					// LP change: moved down by 1 so that level 0 will be valid
1329  					short level_number= -player->teleporting_destination - 1;
1330 
1331 					// change to the next level (if this is the last level, it will be handled further on)
1332 					set_game_state(_change_level);
1333 					set_change_level_destination(level_number);
1334 				}
1335 				break;
1336 
1337 			case PLAYER_TELEPORTING_MIDPOINT+1:
1338 				 /* Interlevel or my intralevel.. */
1339 				if(player_index==current_player_index)
1340 				{
1341 					if (player->teleporting_destination >= 0 || View_DoInterlevelTeleportInEffects()) {
1342 						start_teleporting_effect(false);
1343 						play_object_sound(player->object_index, Sound_TeleportIn());
1344 					}
1345 				}
1346 				player->teleporting_destination= NO_TELEPORTATION_DESTINATION;
1347 				break;
1348 
1349 			case PLAYER_TELEPORTING_DURATION:
1350 				monster->action= _monster_is_moving;
1351 				SET_PLAYER_TELEPORTING_STATUS(player, false);
1352 				break;
1353 		}
1354 	}
1355 	else if(!player_was_interlevel_teleporting)
1356 	{
1357 		/* Note that control panels can set the teleporting destination. */
1358 		if (player->teleporting_destination!=NO_TELEPORTATION_DESTINATION ||
1359 			(((polygon->type==_polygon_is_automatic_exit && calculate_level_completion_state()!=_level_unfinished) || polygon->type==_polygon_is_teleporter) &&
1360 				player->variables.position.x==player->variables.last_position.x &&
1361 				player->variables.position.y==player->variables.last_position.y &&
1362 				player->variables.position.z==player->variables.last_position.z &&
1363 				player->variables.last_direction==player->variables.direction &&
1364 				object->location.z==polygon->floor_height))
1365 		{
1366 			if(--player->delay_before_teleport<0)
1367 			{
1368 				SET_PLAYER_TELEPORTING_STATUS(player, true);
1369 				monster->action= _monster_is_teleporting;
1370 				player->teleporting_phase= 0;
1371 				player->delay_before_teleport= 0; /* The only function that changes this are */
1372 													/* computer terminals. */
1373 
1374 				/* They are in an automatic exit. */
1375 				if (player->teleporting_destination==NO_TELEPORTATION_DESTINATION)
1376 				{
1377 					if (polygon->type==_polygon_is_automatic_exit && calculate_level_completion_state()!=_level_unfinished)
1378 					{
1379 						/* This is an auto exit, and they are successful */
1380 						// LP change: moved down by 1 so that level 0 will be valid
1381 						player->teleporting_destination= -polygon->permutation - 1;
1382 					}
1383 					else
1384 					{
1385 						/* This is a simple teleporter */
1386 						player->teleporting_destination= polygon->permutation;
1387 					}
1388 				}
1389 
1390 				if (player->teleporting_destination>=0) /* simple teleport */
1391 				{
1392 					if (player_index==current_player_index)
1393 					{
1394 						start_teleporting_effect(true);
1395 					}
1396 					play_object_sound(player->object_index, Sound_TeleportOut());
1397 				}
1398 				else /* Level change */
1399 				{
1400 					short other_player_index;
1401 
1402 					/* Everyone plays the teleporting effect out. */
1403 					if (View_DoInterlevelTeleportOutEffects()) {
1404 						start_teleporting_effect(true);
1405 						play_object_sound(current_player->object_index, Sound_TeleportOut());
1406 					}
1407 
1408 					/* Every players object plays the sound, and everyones monster responds. */
1409 					for (other_player_index= 0; other_player_index<dynamic_world->player_count; ++other_player_index)
1410 					{
1411 						player= get_player_data(other_player_index);
1412 
1413 						/* Set them to be teleporting if the already aren�t, or if they are but it */
1414 						/*  is a simple teleport (intralevel) */
1415 						if (player_index!=other_player_index)
1416 						{
1417 							monster= get_monster_data(player->monster_index);
1418 
1419 							/* Tell everyone else to use the teleporting junk... */
1420 							SET_PLAYER_INTERLEVEL_TELEPORTING_STATUS(player, true);
1421 							player->interlevel_teleport_phase= 0;
1422 
1423 							monster->action= _monster_is_teleporting;
1424 						}
1425 					}
1426 				}
1427 			}
1428 		}
1429 	}
1430 }
1431 
update_player_media(short player_index)1432 static void update_player_media(
1433 	short player_index)
1434 {
1435 	struct player_data *player= get_player_data(player_index);
1436 	struct monster_data *monster= get_monster_data(player->monster_index);
1437 	struct object_data *object= get_object_data(monster->object_index);
1438 	struct polygon_data *polygon= get_polygon_data(object->polygon);
1439 
1440 	{
1441 		short sound_type= NONE;
1442 
1443 		if (player_index==current_player_index)
1444 		{
1445 			bool under_media= (player->variables.flags&_HEAD_BELOW_MEDIA_BIT);
1446 			short media_index= polygon->media_index;
1447 
1448 			world_point3d cam_pos;
1449 			short cam_poly;
1450 			angle cam_yaw;
1451 			angle cam_pitch;
1452 			if (ChaseCam_GetPosition(cam_pos, cam_poly, cam_yaw, cam_pitch))
1453 			{
1454 				struct polygon_data *cam_polygon= get_polygon_data(cam_poly);
1455 				media_index= cam_polygon->media_index;
1456 				media_data *media = get_media_data(media_index);
1457 				world_distance media_height= (media_index==NONE || !media) ? INT16_MIN : media->height;
1458 				under_media = (cam_pos.z < media_height);
1459 			}
1460 			set_fade_effect(under_media ? get_media_submerged_fade_effect(media_index) : NONE);
1461 		}
1462 
1463 		if (player->variables.flags&_FEET_BELOW_MEDIA_BIT)
1464 		{
1465 			// LP change: idiot-proofing
1466 			struct media_data *media= get_media_data(polygon->media_index); // should be valid
1467 			{
1468 			world_distance current_magnitude= (player->variables.old_flags&_HEAD_BELOW_MEDIA_BIT) ? media->current_magnitude : (media->current_magnitude>>1);
1469 			world_distance external_magnitude= FIXED_TO_WORLD(GUESS_HYPOTENUSE(ABS(player->variables.external_velocity.i), ABS(player->variables.external_velocity.j)));
1470 			struct damage_definition *damage= get_media_damage(polygon->media_index, (player->variables.flags&_HEAD_BELOW_MEDIA_BIT) ? FIXED_ONE : FIXED_ONE/4);
1471 
1472 			// apply current if possible
1473 			if (!PLAYER_IS_DEAD(player) && external_magnitude<current_magnitude) accelerate_player(player->monster_index, 0, NORMALIZE_ANGLE(media->current_direction-HALF_CIRCLE), media->current_magnitude>>2);
1474 
1475 			// cause damage if possible
1476 			if (damage) damage_player(player->monster_index, NONE, NONE, damage, NONE);
1477 
1478 			// feet entering media sound
1479 			if (!(player->variables.old_flags&_FEET_BELOW_MEDIA_BIT)) sound_type= _media_snd_feet_entering;
1480 			// head entering media sound
1481 			if (!(player->variables.old_flags&_HEAD_BELOW_MEDIA_BIT) && (player->variables.flags&_HEAD_BELOW_MEDIA_BIT)) sound_type= _media_snd_head_entering;
1482 			// head leaving media sound
1483 			if (!(player->variables.flags&_HEAD_BELOW_MEDIA_BIT) && (player->variables.old_flags&_HEAD_BELOW_MEDIA_BIT)) sound_type= _media_snd_head_leaving;
1484 			}
1485 		}
1486 		else
1487 		{
1488 			// feet leaving media sound
1489 			if (polygon->media_index!=NONE && (player->variables.old_flags&_FEET_BELOW_MEDIA_BIT)) sound_type= _media_snd_feet_leaving;
1490 		}
1491 
1492 		if (sound_type!=NONE)
1493 		{
1494 			play_object_sound(monster->object_index, get_media_sound(polygon->media_index, sound_type));
1495 		}
1496 	}
1497 
1498 	if (player->variables.flags&_STEP_PERIOD_BIT)
1499 	{
1500 		short sound_index= NONE;
1501 
1502 		if ((player->variables.flags&_FEET_BELOW_MEDIA_BIT) && !(player->variables.flags&_HEAD_BELOW_MEDIA_BIT))
1503 		{
1504 			sound_index= get_media_sound(polygon->media_index, _media_snd_splashing);
1505 		}
1506 		else
1507 		{
1508 			/* make ordinary walking sound */
1509 		}
1510 
1511 		if (sound_index!=NONE)
1512 		{
1513 			play_object_sound(monster->object_index, sound_index);
1514 		}
1515 	}
1516 }
1517 
set_player_shapes(short player_index,bool animate)1518 static void set_player_shapes(
1519 	short player_index,
1520 	bool animate)
1521 {
1522 	struct player_data *player= get_player_data(player_index);
1523 	struct monster_data *monster= get_monster_data(player->monster_index);
1524 	struct physics_variables *variables= &player->variables;
1525 	struct object_data *legs= get_object_data(monster->object_index);
1526 	struct object_data *torso= get_object_data(legs->parasitic_object);
1527 	shape_descriptor new_torso_shape, new_legs_shape;
1528 	short transfer_mode, transfer_period;
1529 
1530 	get_player_transfer_mode(player_index, &transfer_mode, &transfer_period);
1531 
1532 	/* if we�re not dead, handle changing shapes (if we are dead, the correct dying shape has
1533 		already been set and we just have to wait for the animation to finish) */
1534 	if (!PLAYER_IS_DEAD(player))
1535 	{
1536 		short torso_shape;
1537 		short mode, pseudo_weapon_type;
1538 
1539 		get_player_weapon_mode_and_type(player_index, &pseudo_weapon_type, &mode);
1540 		vassert(pseudo_weapon_type>=0 && pseudo_weapon_type<PLAYER_TORSO_SHAPE_COUNT,
1541 			csprintf(temporary, "Pseudo Weapon Type out of range: %d", pseudo_weapon_type));
1542 		switch(mode)
1543 		{
1544 			case _shape_weapon_firing: torso_shape= player_shapes.firing_torsos[pseudo_weapon_type]; break;
1545 			case _shape_weapon_idle: torso_shape= player_shapes.torsos[pseudo_weapon_type]; break;
1546 			case _shape_weapon_charging: torso_shape= player_shapes.charging_torsos[pseudo_weapon_type]; break;
1547 			default:
1548 				assert(false);
1549 				break;
1550 		}
1551 		assert(player->variables.action>=0 && player->variables.action<NUMBER_OF_PLAYER_ACTIONS);
1552 
1553 		new_legs_shape= BUILD_DESCRIPTOR(BUILD_COLLECTION(player_shapes.collection, player->team), player_shapes.legs[player->variables.action]);
1554 		new_torso_shape= BUILD_DESCRIPTOR(BUILD_COLLECTION(player_shapes.collection, player->color), torso_shape);
1555 
1556 		/* stuff in the transfer modes */
1557 		if (legs->transfer_mode!=transfer_mode) legs->transfer_mode= transfer_mode, legs->transfer_period= transfer_period, legs->transfer_phase= 0;
1558 		if (torso->transfer_mode!=transfer_mode) torso->transfer_mode= transfer_mode, torso->transfer_period= transfer_period, torso->transfer_phase= 0;
1559 
1560 		/* stuff in new shapes only if they have changed (and reset phases if they have) */
1561 		if (new_legs_shape!= legs->shape) legs->shape= new_legs_shape, legs->sequence= 0;
1562 		if (new_torso_shape!=torso->shape) torso->shape= new_torso_shape, torso->sequence= 0;
1563 	}
1564 
1565 	if (animate)
1566 	{
1567 		/* animate the player only if we�re not airborne and not totally dead */
1568 		if ((variables->action!=_player_airborne || (PLAYER_IS_TELEPORTING(player) || PLAYER_IS_INTERLEVEL_TELEPORTING(player)))&&!PLAYER_IS_TOTALLY_DEAD(player)) animate_object(monster->object_index);
1569 		if (PLAYER_IS_DEAD(player) && !PLAYER_IS_TELEPORTING(player) && (GET_OBJECT_ANIMATION_FLAGS(legs)&_obj_last_frame_animated) && !PLAYER_IS_TOTALLY_DEAD(player))
1570 		{
1571 			/* we�ve finished the animation; let the player reincarnate if he wants to */
1572 			SET_PLAYER_TOTALLY_DEAD_STATUS(player, true);
1573 			set_player_dead_shape(player_index, false);
1574 
1575 			/* If you had something cool, you don't anymore.. */
1576 			remove_dead_player_items(player_index);
1577 		}
1578 	}
1579 }
1580 
1581 /* We can rebuild him!! */
revive_player(short player_index)1582 static void revive_player(
1583 	short player_index)
1584 {
1585 	struct player_data *player= get_player_data(player_index);
1586 	struct monster_data *monster= get_monster_data(player->monster_index);
1587 	struct object_location location;
1588 	struct object_data *object;
1589 	short team;
1590 
1591 	/* Figure out where the player starts */
1592 	team= calculate_player_team(player->team);
1593 	get_random_player_starting_location_and_facing(dynamic_world->player_count, team, &location);
1594 
1595 	monster->action= _monster_is_moving; /* was probably _dying or something */
1596 
1597 	/* remove only the player�s torso, which should be invisible anyway, and turn his legs
1598 		into garbage */
1599 	remove_parasitic_object(monster->object_index);
1600 	turn_object_to_shit(monster->object_index);
1601 
1602 	/* create a new pair of legs, and (completely behind MONSTERS.C�s back) reattach it to
1603 		it�s monster (shape will be set by set_player_shapes, below) */
1604 	player->object_index= monster->object_index= new_map_object(&location, 0);
1605 	object= get_object_data(monster->object_index);
1606 	SET_OBJECT_SOLIDITY(object, true);
1607 	SET_OBJECT_OWNER(object, _object_is_monster);
1608 	object->permutation= player->monster_index;
1609 
1610 	/* create a new torso (shape will be set by set_player_shapes, below) */
1611 	attach_parasitic_object(monster->object_index, 0, location.yaw);
1612 
1613 	initialize_player_physics_variables(player_index);
1614 
1615 	player->weapon_intensity_decay= 0;
1616 	player->suit_energy= PLAYER_MAXIMUM_SUIT_ENERGY;
1617 	player->suit_oxygen= PLAYER_MAXIMUM_SUIT_OXYGEN;
1618 	SET_PLAYER_DEAD_STATUS(player, false);
1619 	SET_PLAYER_TOTALLY_DEAD_STATUS(player, false);
1620 	SET_PLAYER_TELEPORTING_STATUS(player, false);
1621 	SET_PLAYER_INTERLEVEL_TELEPORTING_STATUS(player, false);
1622 	player->invincibility_duration= 0;
1623 	player->invisibility_duration= 0;
1624 	player->infravision_duration= 0;
1625 	player->extravision_duration= 0;
1626 	player->control_panel_side_index= NONE; // not using a control panel.
1627 
1628 	give_player_initial_items(player_index);
1629 
1630 	/* set the correct shapes and transfer mode */
1631 	set_player_shapes(player_index, false);
1632 
1633 	try_and_strip_player_items(player_index);
1634 
1635 	/* Update the interface to reflect your player's changed status */
1636 	if (player_index==current_player_index) update_interface(NONE);
1637 
1638 	// LP addition: handles the current player's chase cam;
1639 	// in screen.c, we find that it's the current player whose view gets rendered
1640 	if (player_index == current_player_index) ChaseCam_Reset();
1641 
1642 	// LP addition: set field-of-view approrpriately
1643 	if (player_index == current_player_index) ResetFieldOfView();
1644 
1645 	L_Call_Player_Revived (player_index);
1646 }
1647 
1648 /* The player just changed map levels, recreate him, and all of the objects */
1649 /*  associated with him. */
recreate_player(short player_index)1650 static void recreate_player(
1651 	short player_index)
1652 {
1653 	short monster_index;
1654 	struct monster_data *monster;
1655 	struct player_data *player= get_player_data(player_index);
1656 	short placement_team;
1657 	struct object_location location;
1658 	bool  player_teleported_dead= false;
1659 
1660 	/* Determine the location */
1661 	placement_team= calculate_player_team(player->team);
1662 	get_random_player_starting_location_and_facing(player_index, placement_team, &location);
1663 
1664 	/* create an object and a monster for this player */
1665 	monster_index= new_monster(&location, _monster_marine);
1666 	monster= get_monster_data(monster_index);
1667 
1668 	/* add our parasitic torso */
1669 	attach_parasitic_object(monster->object_index, 0, location.yaw);
1670 
1671 	/* and initialize it */
1672 	if(PLAYER_IS_TOTALLY_DEAD(player) || PLAYER_IS_DEAD(player))
1673 	{
1674 		player_teleported_dead= true;
1675 	}
1676 
1677 	/* Clear the transient flags, leave the persistant flags, like Player has cheated */
1678 	player->flags &= (_player_is_teleporting_flag | _player_is_interlevel_teleporting_flag | PLAYER_PERSISTANT_FLAGS );
1679 	player->monster_index= monster_index;
1680 	player->object_index= monster->object_index;
1681 
1682 	/* initialize_player_physics_variables sets all of these */
1683 	player->facing= player->elevation= 0;
1684 	player->location.x= player->location.y= player->location.z= 0;
1685 	player->camera_location.x= player->camera_location.y= player->camera_location.z= 0;
1686 
1687 	/* We don't change... */
1688 	/* physics_model, suit_energy, suit_oxygen, current_weapon, desired_weapon */
1689 	/* None of the weapons array data... */
1690 	/* None of the items array data.. */
1691 	/* The inventory offset/dirty flags.. */
1692 	// ZZZ: netdead...
1693 	mark_player_inventory_screen_as_dirty(player_index, _weapon);
1694 
1695 	/* Nuke the physics */
1696 	obj_clear(player->variables);
1697 
1698 	/* Reset the player weapon data and the physics variable.. (after updating player_count) */
1699 	initialize_player_physics_variables(player_index);
1700 	set_player_shapes(player_index, false);
1701 
1702 	player->control_panel_side_index = NONE; // not using a control panel.
1703 	initialize_player_terminal_info(player_index);
1704 
1705 	try_and_strip_player_items(player_index);
1706 
1707 	if(player_teleported_dead)
1708 	{
1709 		kill_player(player_index, NONE, _monster_is_dying_soft);
1710 	}
1711 
1712 	// LP addition: handles the current player's chase cam;
1713 	// in screen.c, we find that it's the current player whose view gets rendered
1714 	if (player_index == current_player_index) ChaseCam_Reset();
1715 
1716 	// Done here so that players' missiles will always be guided
1717 	// if they are intended to be guided
1718 	adjust_player_physics(get_monster_data(player->monster_index));
1719 
1720 	// Marathon 1 won't activate monsters immediately at level start
1721 	if (static_world->environment_flags & _environment_activation_ranges)
1722 		monster->ticks_since_last_activation = dynamic_world->tick_count;
1723 }
1724 
kill_player(short player_index,short aggressor_player_index,short action)1725 static void kill_player(
1726 	short player_index,
1727 	short aggressor_player_index,
1728 	short action)
1729 {
1730 	struct player_data *player= get_player_data(player_index);
1731 	struct monster_data *monster= get_monster_data(player->monster_index);
1732 	struct object_data *legs= get_object_data(monster->object_index);
1733 	struct object_data *torso= get_object_data(legs->parasitic_object);
1734 
1735 	/* discharge any of our weapons which were holding charges */
1736 	discharge_charged_weapons(player_index);
1737 	initialize_player_weapons(player_index);
1738 
1739 	/* make our legs ownerless scenery, mark our monster as dying, stuff in the right dying shape */
1740 	SET_OBJECT_OWNER(legs, _object_is_normal);
1741 	monster->action= action;
1742 	monster_died(player->monster_index);
1743 	set_player_dead_shape(player_index, true);
1744 
1745 	/* make our torso invisible */
1746 	SET_OBJECT_INVISIBILITY(torso, true);
1747 
1748 	/* make our player dead */
1749 	SET_PLAYER_DEAD_STATUS(player, true);
1750 
1751 	player->reincarnation_delay= MINIMUM_REINCARNATION_DELAY;
1752 	if (GET_GAME_OPTIONS()&_dying_is_penalized) player->reincarnation_delay+= NORMAL_REINCARNATION_DELAY;
1753 	if (aggressor_player_index==player_index && (GET_GAME_OPTIONS()&_suicide_is_penalized)) player->reincarnation_delay+= SUICIDE_REINCARNATION_DELAY;
1754 
1755 	kill_player_physics_variables(player_index);
1756 }
1757 
give_player_initial_items(short player_index)1758 static void give_player_initial_items(
1759 	short player_index)
1760 {
1761 	struct player_data *player= get_player_data(player_index);
1762 
1763 	for(unsigned loop= 0; loop<NUMBER_OF_PLAYER_INITIAL_ITEMS; ++loop)
1764 	{
1765 		/* Get the item.. */
1766 		assert(player_initial_items[loop]>=0 && player_initial_items[loop]<NUMBER_OF_ITEMS);
1767 
1768 		if(player->items[player_initial_items[loop]]==NONE)
1769 		{
1770 			player->items[player_initial_items[loop]]= 1;
1771 		} else {
1772 			player->items[player_initial_items[loop]]+= 1;
1773 		}
1774 
1775 		process_new_item_for_reloading(player_index, player_initial_items[loop]);
1776 	}
1777 }
1778 
remove_dead_player_items(short player_index)1779 static void remove_dead_player_items(
1780 	short player_index)
1781 {
1782 	struct player_data *player= get_player_data(player_index);
1783 	short item_type;
1784 
1785 	// subtract all initial items
1786 	for (unsigned i= 0; i<NUMBER_OF_PLAYER_INITIAL_ITEMS; ++i)
1787 	{
1788 		if (player->items[player_initial_items[i]]>0)
1789 		{
1790 			player->items[player_initial_items[i]]-= 1;
1791 		}
1792 	}
1793 
1794 	// drop any balls
1795 	// START Benad: dec. 3
1796 	{
1797 		short ball_color= find_player_ball_color(player_index);
1798 
1799 		if (ball_color!=NONE)
1800 		{
1801 			struct polygon_data *polygon= get_polygon_data(player->supporting_polygon_index);
1802 
1803 			if ( ((GET_GAME_TYPE()==_game_of_rugby) || (GET_GAME_TYPE()==_game_of_capture_the_flag))
1804 				&& (( (dynamic_world->game_information.kill_limit == 819) && (polygon->type==_polygon_is_hill) ) ||
1805 					( polygon->type==_polygon_is_base && polygon->permutation != player->team ) ) )
1806 			{
1807 				player->items[BALL_ITEM_BASE + ball_color]= NONE;
1808 				dynamic_world->current_item_count[BALL_ITEM_BASE + ball_color]--;
1809 			}
1810 			else
1811 			{
1812 				short item_type= BALL_ITEM_BASE + ball_color;
1813 
1814 				// Benad: using location and supporting_polygon_index instead of
1815 				// camera_location and camera_polygon_index... D'uh...
1816 				drop_the_ball(&player->location, player->supporting_polygon_index,
1817 					player->monster_index, _monster_marine, item_type);
1818 				player->items[item_type]= NONE;
1819 			}
1820 		}
1821 	}
1822 	// END Benad: dec. 3
1823 
1824 	for (item_type= 0; item_type<NUMBER_OF_ITEMS; ++item_type)
1825 	{
1826 		short item_count= player->items[item_type];
1827 		// START Benad
1828 		if ((item_type >= BALL_ITEM_BASE) && (item_type < BALL_ITEM_BASE+MAXIMUM_NUMBER_OF_PLAYERS))
1829 			continue;
1830 		// END Benad
1831 		while ((item_count-= 1)>=0)
1832 		{
1833 			short item_kind= get_item_kind(item_type);
1834 			bool dropped= false;
1835 
1836 			// if we�re not set to burn items or this is an important item (i.e., repair chip) drop it
1837 			if (!(GET_GAME_OPTIONS()&_burn_items_on_death) ||
1838 				(item_kind==_item && dynamic_world->player_count>1))
1839 			{
1840 				if (item_kind!=_ammunition || !(global_random()&1))
1841 				{
1842 					world_point3d random_point;
1843 					short random_polygon_index;
1844 					short retries= 5;
1845 
1846 					do
1847 					{
1848 						random_point_on_circle(&player->location, player->supporting_polygon_index, WORLD_ONE_FOURTH, &random_point, &random_polygon_index);
1849 					}
1850 					while (random_polygon_index==NONE && --retries);
1851 
1852 					if (random_polygon_index!=NONE)
1853 					{
1854 						struct object_location location;
1855 
1856 						location.polygon_index= random_polygon_index;
1857 						location.p.x= random_point.x, location.p.y= random_point.y, location.p.z= 0;
1858 						location.yaw= 0;
1859 						location.flags= 0;
1860 						new_item(&location, item_type);
1861 
1862 						dropped= true;
1863 					}
1864 				}
1865 			}
1866 
1867 			if (film_profile.count_dead_dropped_items_correctly || !dropped) object_was_just_destroyed(_object_is_item, item_type);
1868 		}
1869 
1870 		player->items[item_type]= NONE;
1871 	}
1872 
1873 	mark_player_inventory_as_dirty(player_index, NONE);
1874 }
1875 
get_player_transfer_mode(short player_index,short * transfer_mode,short * transfer_period)1876 static void get_player_transfer_mode(
1877 	short player_index,
1878 	short *transfer_mode,
1879 	short *transfer_period)
1880 {
1881 	struct player_data *player= get_player_data(player_index);
1882 	short duration= 0;
1883 
1884 	*transfer_period= 1;
1885 	*transfer_mode= NONE;
1886 	if (PLAYER_IS_TELEPORTING(player))
1887 	{
1888 		if (player->teleporting_destination >= 0 || (player->teleporting_phase < PLAYER_TELEPORTING_MIDPOINT && View_DoInterlevelTeleportOutEffects()) || (player->teleporting_phase >= PLAYER_TELEPORTING_MIDPOINT && View_DoInterlevelTeleportInEffects()))
1889 		{
1890 			*transfer_mode= player->teleporting_phase<PLAYER_TELEPORTING_MIDPOINT ? _xfer_fold_out : _xfer_fold_in;
1891 			*transfer_period= PLAYER_TELEPORTING_MIDPOINT+1;
1892 		}
1893 	}
1894 	else if (PLAYER_IS_INTERLEVEL_TELEPORTING(player))
1895 	{
1896 		if (player->teleporting_destination >= 0 || (player->teleporting_phase < PLAYER_TELEPORTING_MIDPOINT && View_DoInterlevelTeleportOutEffects()) || (player->teleporting_phase >= PLAYER_TELEPORTING_MIDPOINT && View_DoInterlevelTeleportInEffects()))
1897 		{
1898 			*transfer_mode= player->teleporting_phase<PLAYER_TELEPORTING_MIDPOINT ? _xfer_fold_out : _xfer_fold_in;
1899 			*transfer_period= PLAYER_TELEPORTING_MIDPOINT+1;
1900 		}
1901 	}
1902 	else
1903 	{
1904 		if (player->invincibility_duration)
1905 		{
1906 			*transfer_mode= _xfer_static;
1907 			duration= player->invincibility_duration;
1908 		}
1909 		else
1910 		{
1911 			if (player->invisibility_duration)
1912 			{
1913 				*transfer_mode= player->invisibility_duration>kINVISIBILITY_DURATION ? _xfer_subtle_invisibility : _xfer_invisibility;
1914 				duration= player->invisibility_duration;
1915 			}
1916 		}
1917 
1918 		if (duration && duration<10*TICKS_PER_SECOND)
1919 		{
1920 			switch (duration/(TICKS_PER_SECOND/6))
1921 			{
1922 				case 46: case 37: case 29: case 22: case 16: case 11: case 7: case 4: case 2:
1923 					if(player->invincibility_duration && player->invisibility_duration)
1924 						*transfer_mode= player->invisibility_duration>kINVISIBILITY_DURATION ? _xfer_subtle_invisibility : _xfer_invisibility;
1925 					else
1926 						*transfer_mode= NONE;
1927 					break;
1928 			}
1929 		}
1930 	}
1931 }
1932 
set_player_dead_shape(short player_index,bool dying)1933 static void set_player_dead_shape(
1934 	short player_index,
1935 	bool dying)
1936 {
1937 	struct player_data *player= get_player_data(player_index);
1938 	struct monster_data *monster= get_monster_data(player->monster_index);
1939 	short shape;
1940 
1941 	if (monster->action==_monster_is_dying_flaming)
1942 	{
1943 		shape= dying ? FLAMING_DYING_SHAPE : FLAMING_DEAD_SHAPE;
1944 	}
1945 	else
1946 	{
1947 		if (dying)
1948 		{
1949 			shape= (monster->action==_monster_is_dying_hard) ? player_shapes.dying_hard : player_shapes.dying_soft;
1950 		}
1951 		else
1952 		{
1953 			shape= (monster->action==_monster_is_dying_hard) ? player_shapes.dead_hard : player_shapes.dead_soft;
1954 		}
1955 
1956 		shape= BUILD_DESCRIPTOR(BUILD_COLLECTION(player_shapes.collection, player->team), shape);
1957 	}
1958 
1959 	if (dying)
1960 	{
1961 		set_object_shape_and_transfer_mode(monster->object_index, shape, NONE);
1962 	}
1963 	else
1964 	{
1965 		randomize_object_sequence(monster->object_index, shape);
1966 	}
1967 }
1968 
calculate_player_team(short base_team)1969 static short calculate_player_team(
1970 	short base_team)
1971 {
1972 	short team = NONE;
1973 
1974 	/* Starting locations are based on the team type. */
1975 	switch(GET_GAME_TYPE())
1976 	{
1977 		case _game_of_kill_monsters:
1978 		case _game_of_cooperative_play:
1979 		case _game_of_tag:
1980 		case _game_of_king_of_the_hill:
1981 		case _game_of_kill_man_with_ball:
1982 			team= NONE;
1983 			break;
1984 
1985 	case _game_of_custom:
1986 		case _game_of_defense:
1987 		case _game_of_rugby:
1988 		case _game_of_capture_the_flag:
1989 			// START Benad
1990 			if ((dynamic_world->game_information.kill_limit == 819) ||
1991 				((GET_GAME_TYPE() == _game_of_defense) &&
1992 				(dynamic_world->game_information.kill_limit == 1080))) // 1080 seconds, or 18:00...
1993 				team= NONE;
1994 			else
1995 				team= base_team;
1996 				// vassert(false, csprintf(temporary, "Kill limit: %d", dynamic_world->game_information.kill_limit));
1997 			// END Benad
1998 			break;
1999 	}
2000 
2001 	return team;
2002 }
2003 
2004 // #define STRIPPED_ENERGY (PLAYER_MAXIMUM_SUIT_ENERGY/4)
try_and_strip_player_items(short player_index)2005 static void try_and_strip_player_items(
2006 	short player_index)
2007 {
2008 	struct player_data *player= get_player_data(player_index);
2009 	short item_type;
2010 
2011 	if (static_world->environment_flags&_environment_rebellion)
2012 	{
2013 		for (item_type= 0; item_type<NUMBER_OF_ITEMS; ++item_type)
2014 		{
2015 			switch (item_type)
2016 			{
2017 				case _i_knife:
2018 					break;
2019 
2020 				default:
2021 					player->items[item_type]= NONE;
2022 					break;
2023 			}
2024 		}
2025 
2026 		mark_player_inventory_as_dirty(player_index, NONE);
2027 		initialize_player_weapons(player_index);
2028 
2029 		// LP change: using variable for this
2030 		if (player->suit_energy > player_settings.StrippedEnergy)
2031 			player->suit_energy = player_settings.StrippedEnergy;
2032 	}
2033 }
2034 
2035 
adjust_player_physics(monster_data * me)2036 static void adjust_player_physics(monster_data *me)
2037 {
2038 	// LP: Fix the player physics so that guided missiles will work correctly
2039 	if (player_settings.PlayerShotsGuided)
2040 	{
2041 		struct monster_definition *vacbob = get_monster_definition_external(_civilian_fusion_security);
2042 		// AlexJLS patch: make this player active, so guided weapons can work
2043 		SET_MONSTER_ACTIVE_STATUS(me,true);
2044 
2045 		// Gets called once for every player character created or re-created;
2046 		// that seems to be OK
2047 		SetPlayerViewAttribs(vacbob->half_visual_arc,vacbob->half_vertical_visual_arc / 3,
2048 			vacbob->visual_range * 2,
2049 			vacbob->dark_visual_range * 2);
2050 	}
2051 }
2052 
2053 
StreamToPhysVars(uint8 * & S,physics_variables & Object)2054 static void StreamToPhysVars(uint8* &S, physics_variables& Object)
2055 {
2056 		StreamToValue(S,Object.head_direction);
2057 		StreamToValue(S,Object.last_direction);
2058 		StreamToValue(S,Object.direction);
2059 		StreamToValue(S,Object.elevation);
2060 		StreamToValue(S,Object.angular_velocity);
2061 		StreamToValue(S,Object.vertical_angular_velocity);
2062 		StreamToValue(S,Object.velocity);
2063 		StreamToValue(S,Object.perpendicular_velocity);
2064 		StreamToValue(S,Object.last_position.x);
2065 		StreamToValue(S,Object.last_position.y);
2066 		StreamToValue(S,Object.last_position.z);
2067 		StreamToValue(S,Object.position.x);
2068 		StreamToValue(S,Object.position.y);
2069 		StreamToValue(S,Object.position.z);
2070 		StreamToValue(S,Object.actual_height);
2071 
2072 		StreamToValue(S,Object.adjusted_pitch);
2073 		StreamToValue(S,Object.adjusted_yaw);
2074 
2075 		StreamToValue(S,Object.external_velocity.i);
2076 		StreamToValue(S,Object.external_velocity.j);
2077 		StreamToValue(S,Object.external_velocity.k);
2078 		StreamToValue(S,Object.external_angular_velocity);
2079 
2080 		StreamToValue(S,Object.step_phase);
2081 		StreamToValue(S,Object.step_amplitude);
2082 
2083 		StreamToValue(S,Object.floor_height);
2084 		StreamToValue(S,Object.ceiling_height);
2085 		StreamToValue(S,Object.media_height);
2086 
2087 		StreamToValue(S,Object.action);
2088 		StreamToValue(S,Object.old_flags);
2089 		StreamToValue(S,Object.flags);
2090 }
2091 
PhysVarsToStream(uint8 * & S,physics_variables & Object)2092 static void PhysVarsToStream(uint8* &S, physics_variables& Object)
2093 {
2094 		ValueToStream(S,Object.head_direction);
2095 		ValueToStream(S,Object.last_direction);
2096 		ValueToStream(S,Object.direction);
2097 		ValueToStream(S,Object.elevation);
2098 		ValueToStream(S,Object.angular_velocity);
2099 		ValueToStream(S,Object.vertical_angular_velocity);
2100 		ValueToStream(S,Object.velocity);
2101 		ValueToStream(S,Object.perpendicular_velocity);
2102 		ValueToStream(S,Object.last_position.x);
2103 		ValueToStream(S,Object.last_position.y);
2104 		ValueToStream(S,Object.last_position.z);
2105 		ValueToStream(S,Object.position.x);
2106 		ValueToStream(S,Object.position.y);
2107 		ValueToStream(S,Object.position.z);
2108 		ValueToStream(S,Object.actual_height);
2109 
2110 		ValueToStream(S,Object.adjusted_pitch);
2111 		ValueToStream(S,Object.adjusted_yaw);
2112 
2113 		ValueToStream(S,Object.external_velocity.i);
2114 		ValueToStream(S,Object.external_velocity.j);
2115 		ValueToStream(S,Object.external_velocity.k);
2116 		ValueToStream(S,Object.external_angular_velocity);
2117 
2118 		ValueToStream(S,Object.step_phase);
2119 		ValueToStream(S,Object.step_amplitude);
2120 
2121 		ValueToStream(S,Object.floor_height);
2122 		ValueToStream(S,Object.ceiling_height);
2123 		ValueToStream(S,Object.media_height);
2124 
2125 		ValueToStream(S,Object.action);
2126 		ValueToStream(S,Object.old_flags);
2127 		ValueToStream(S,Object.flags);
2128 }
2129 
2130 
StreamToDamRec(uint8 * & S,damage_record & Object)2131 inline void StreamToDamRec(uint8* &S, damage_record& Object)
2132 {
2133 		StreamToValue(S,Object.damage);
2134 		StreamToValue(S,Object.kills);
2135 }
2136 
DamRecToStream(uint8 * & S,damage_record & Object)2137 static void DamRecToStream(uint8* &S, damage_record& Object)
2138 {
2139 		ValueToStream(S,Object.damage);
2140 		ValueToStream(S,Object.kills);
2141 }
2142 
2143 
unpack_player_data(uint8 * Stream,player_data * Objects,size_t Count)2144 uint8 *unpack_player_data(uint8 *Stream, player_data *Objects, size_t Count)
2145 {
2146 	uint8* S = Stream;
2147 	player_data* ObjPtr = Objects;
2148 
2149 	for (size_t k = 0; k < Count; k++, ObjPtr++)
2150 	{
2151 		StreamToValue(S,ObjPtr->identifier);
2152 		StreamToValue(S,ObjPtr->flags);
2153 
2154 		StreamToValue(S,ObjPtr->color);
2155 		StreamToValue(S,ObjPtr->team);
2156 		StreamToBytes(S,ObjPtr->name,MAXIMUM_PLAYER_NAME_LENGTH+2);
2157 
2158 		StreamToValue(S,ObjPtr->location.x);
2159 		StreamToValue(S,ObjPtr->location.y);
2160 		StreamToValue(S,ObjPtr->location.z);
2161 		StreamToValue(S,ObjPtr->camera_location.x);
2162 		StreamToValue(S,ObjPtr->camera_location.y);
2163 		StreamToValue(S,ObjPtr->camera_location.z);
2164 		StreamToValue(S,ObjPtr->camera_polygon_index);
2165 		StreamToValue(S,ObjPtr->facing);
2166 		StreamToValue(S,ObjPtr->elevation);
2167 		StreamToValue(S,ObjPtr->supporting_polygon_index);
2168 		StreamToValue(S,ObjPtr->last_supporting_polygon_index);
2169 
2170 		StreamToValue(S,ObjPtr->suit_energy);
2171 		StreamToValue(S,ObjPtr->suit_oxygen);
2172 
2173 		StreamToValue(S,ObjPtr->monster_index);
2174 		StreamToValue(S,ObjPtr->object_index);
2175 
2176 		StreamToValue(S,ObjPtr->weapon_intensity_decay);
2177 		StreamToValue(S,ObjPtr->weapon_intensity);
2178 
2179 		StreamToValue(S,ObjPtr->invisibility_duration);
2180 		StreamToValue(S,ObjPtr->invincibility_duration);
2181 		StreamToValue(S,ObjPtr->infravision_duration);
2182 		StreamToValue(S,ObjPtr->extravision_duration);
2183 
2184 		StreamToValue(S,ObjPtr->delay_before_teleport);
2185 		StreamToValue(S,ObjPtr->teleporting_phase);
2186 		StreamToValue(S,ObjPtr->teleporting_destination);
2187 		StreamToValue(S,ObjPtr->interlevel_teleport_phase);
2188 
2189 		StreamToList(S,ObjPtr->items,NUMBER_OF_ITEMS);
2190 
2191 		StreamToValue(S,ObjPtr->interface_flags);
2192 		StreamToValue(S,ObjPtr->interface_decay);
2193 
2194 		StreamToPhysVars(S,ObjPtr->variables);
2195 
2196 		StreamToDamRec(S,ObjPtr->total_damage_given);
2197 		for (int k=0; k<MAXIMUM_NUMBER_OF_PLAYERS; k++)
2198 			StreamToDamRec(S,ObjPtr->damage_taken[k]);
2199 		StreamToDamRec(S,ObjPtr->monster_damage_taken);
2200 		StreamToDamRec(S,ObjPtr->monster_damage_given);
2201 
2202 		StreamToValue(S,ObjPtr->reincarnation_delay);
2203 
2204 		StreamToValue(S,ObjPtr->control_panel_side_index);
2205 
2206 		StreamToValue(S,ObjPtr->ticks_at_last_successful_save);
2207 
2208 		StreamToList(S,ObjPtr->netgame_parameters,2);
2209 
2210 		S += 256*2;
2211 	}
2212 
2213 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_player_data));
2214 	return S;
2215 }
pack_player_data(uint8 * Stream,player_data * Objects,size_t Count)2216 uint8 *pack_player_data(uint8 *Stream, player_data *Objects, size_t Count)
2217 {
2218 	uint8* S = Stream;
2219 	player_data* ObjPtr = Objects;
2220 
2221 	for (size_t k = 0; k < Count; k++, ObjPtr++)
2222 	{
2223 		ValueToStream(S,ObjPtr->identifier);
2224 		ValueToStream(S,ObjPtr->flags);
2225 
2226 		ValueToStream(S,ObjPtr->color);
2227 		ValueToStream(S,ObjPtr->team);
2228 		BytesToStream(S,ObjPtr->name,MAXIMUM_PLAYER_NAME_LENGTH+2);
2229 
2230 		ValueToStream(S,ObjPtr->location.x);
2231 		ValueToStream(S,ObjPtr->location.y);
2232 		ValueToStream(S,ObjPtr->location.z);
2233 		ValueToStream(S,ObjPtr->camera_location.x);
2234 		ValueToStream(S,ObjPtr->camera_location.y);
2235 		ValueToStream(S,ObjPtr->camera_location.z);
2236 		ValueToStream(S,ObjPtr->camera_polygon_index);
2237 		ValueToStream(S,ObjPtr->facing);
2238 		ValueToStream(S,ObjPtr->elevation);
2239 		ValueToStream(S,ObjPtr->supporting_polygon_index);
2240 		ValueToStream(S,ObjPtr->last_supporting_polygon_index);
2241 
2242 		ValueToStream(S,ObjPtr->suit_energy);
2243 		ValueToStream(S,ObjPtr->suit_oxygen);
2244 
2245 		ValueToStream(S,ObjPtr->monster_index);
2246 		ValueToStream(S,ObjPtr->object_index);
2247 
2248 		ValueToStream(S,ObjPtr->weapon_intensity_decay);
2249 		ValueToStream(S,ObjPtr->weapon_intensity);
2250 
2251 		ValueToStream(S,ObjPtr->invisibility_duration);
2252 		ValueToStream(S,ObjPtr->invincibility_duration);
2253 		ValueToStream(S,ObjPtr->infravision_duration);
2254 		ValueToStream(S,ObjPtr->extravision_duration);
2255 
2256 		ValueToStream(S,ObjPtr->delay_before_teleport);
2257 		ValueToStream(S,ObjPtr->teleporting_phase);
2258 		ValueToStream(S,ObjPtr->teleporting_destination);
2259 		ValueToStream(S,ObjPtr->interlevel_teleport_phase);
2260 
2261 		ListToStream(S,ObjPtr->items,NUMBER_OF_ITEMS);
2262 
2263 		ValueToStream(S,ObjPtr->interface_flags);
2264 		ValueToStream(S,ObjPtr->interface_decay);
2265 
2266 		PhysVarsToStream(S,ObjPtr->variables);
2267 
2268 		DamRecToStream(S,ObjPtr->total_damage_given);
2269 		for (int k=0; k<MAXIMUM_NUMBER_OF_PLAYERS; k++)
2270 			DamRecToStream(S,ObjPtr->damage_taken[k]);
2271 		DamRecToStream(S,ObjPtr->monster_damage_taken);
2272 		DamRecToStream(S,ObjPtr->monster_damage_given);
2273 
2274 		ValueToStream(S,ObjPtr->reincarnation_delay);
2275 
2276 		ValueToStream(S,ObjPtr->control_panel_side_index);
2277 
2278 		ValueToStream(S,ObjPtr->ticks_at_last_successful_save);
2279 
2280 		ListToStream(S,ObjPtr->netgame_parameters,2);
2281 
2282 		S += 256*2;
2283 	}
2284 
2285 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_player_data));
2286 	return S;
2287 }
2288 
2289 short *original_player_initial_items = NULL;
2290 struct damage_response_definition *original_damage_response_definitions = NULL;
2291 struct player_powerup_durations_definition *original_player_powerup_durations = NULL;
2292 struct player_powerup_definition *original_player_powerups = NULL;
2293 struct player_shape_definitions *original_player_shapes = NULL;
2294 struct player_settings_definition *original_player_settings = NULL;
2295 
reset_mml_player()2296 void reset_mml_player()
2297 {
2298 	if (original_player_settings) {
2299 		player_settings = *original_player_settings;
2300 		free(original_player_settings);
2301 		original_player_settings = NULL;
2302 	}
2303 	if (original_player_initial_items) {
2304 		for (unsigned i = 0; i < NUMBER_OF_PLAYER_INITIAL_ITEMS; i++)
2305 			player_initial_items[i] = original_player_initial_items[i];
2306 		free(original_player_initial_items);
2307 		original_player_initial_items = NULL;
2308 	}
2309 	if (original_damage_response_definitions) {
2310 		for (unsigned i = 0; i < NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS; i++)
2311 			damage_response_definitions[i] = original_damage_response_definitions[i];
2312 		free(original_damage_response_definitions);
2313 		original_damage_response_definitions = NULL;
2314 	}
2315 	if (original_player_powerup_durations) {
2316 		player_powerup_durations = *original_player_powerup_durations;
2317 		free(original_player_powerup_durations);
2318 		original_player_powerup_durations = NULL;
2319 	}
2320 	if (original_player_powerups) {
2321 		player_powerups = *original_player_powerups;
2322 		free(original_player_powerups);
2323 		original_player_powerups = NULL;
2324 	}
2325 	if (original_player_shapes) {
2326 		player_shapes = *original_player_shapes;
2327 		free(original_player_shapes);
2328 		original_player_shapes = NULL;
2329 	}
2330 }
2331 
parse_mml_player(const InfoTree & root)2332 void parse_mml_player(const InfoTree& root)
2333 {
2334 	// back up old values first
2335 	if (!original_player_settings) {
2336 		original_player_settings = (struct player_settings_definition *) malloc(sizeof(struct player_settings_definition));
2337         assert(original_player_settings);
2338 		*original_player_settings = player_settings;
2339 	}
2340 	if (!original_player_initial_items) {
2341 		original_player_initial_items = (short *) malloc(sizeof(short) * NUMBER_OF_PLAYER_INITIAL_ITEMS);
2342 		assert(original_player_initial_items);
2343 		for (unsigned i = 0; i < NUMBER_OF_PLAYER_INITIAL_ITEMS; i++)
2344 			original_player_initial_items[i] = player_initial_items[i];
2345 	}
2346 	if (!original_damage_response_definitions) {
2347 		original_damage_response_definitions = (struct damage_response_definition *) malloc(sizeof(struct damage_response_definition) * NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS);
2348 		assert(original_damage_response_definitions);
2349 		for (unsigned i = 0; i < NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS; i++)
2350 			original_damage_response_definitions[i] = damage_response_definitions[i];
2351 	}
2352 	if (!original_player_powerup_durations) {
2353 		original_player_powerup_durations = (struct player_powerup_durations_definition *) malloc(sizeof(struct player_powerup_durations_definition));
2354 		assert(original_player_powerup_durations);
2355 		*original_player_powerup_durations = player_powerup_durations;
2356 	}
2357 	if (!original_player_powerups) {
2358 		original_player_powerups = (struct player_powerup_definition *) malloc(sizeof(struct player_powerup_definition));
2359 		assert(original_player_powerups);
2360 		*original_player_powerups = player_powerups;
2361 	}
2362 	if (!original_player_shapes) {
2363 		original_player_shapes = (struct player_shape_definitions *) malloc(sizeof(struct player_shape_definitions));
2364         assert(original_player_powerups);
2365 		*original_player_shapes = player_shapes;
2366 	}
2367 
2368 	root.read_attr_bounded<int16>("energy", player_settings.InitialEnergy, 0, SHRT_MAX);
2369 	root.read_attr_bounded<int16>("oxygen", player_settings.InitialOxygen, 0, SHRT_MAX);
2370 	root.read_attr_bounded<int16>("stripped", player_settings.StrippedEnergy, 0, SHRT_MAX);
2371 	root.read_fixed("light", player_settings.PlayerSelfLuminosity, 0, SHRT_MAX);
2372 	root.read_attr("oxygen_deplete", player_settings.OxygenDepletion);
2373 	root.read_attr("oxygen_replenish", player_settings.OxygenReplenishment);
2374 	root.read_indexed("vulnerability", player_settings.Vulnerability, NUMBER_OF_DAMAGE_TYPES, true);
2375 	root.read_attr("guided", player_settings.PlayerShotsGuided);
2376 	root.read_attr("half_visual_arc", player_settings.PlayerHalfVisualArc);
2377 	root.read_attr("half_vertical_visual_arc", player_settings.PlayerHalfVertVisualArc);
2378 	root.read_attr("visual_range", player_settings.PlayerVisualRange);
2379 	root.read_attr("dark_visual_range", player_settings.PlayerDarkVisualRange);
2380 	root.read_attr("single_energy", player_settings.SingleEnergy);
2381 	root.read_attr("double_energy", player_settings.DoubleEnergy);
2382 	root.read_attr("triple_energy", player_settings.TripleEnergy);
2383 	root.read_attr("can_swim", player_settings.CanSwim);
2384 
2385 	BOOST_FOREACH(InfoTree item, root.children_named("item"))
2386 	{
2387 		int16 index;
2388 		if (!item.read_indexed("index", index, NUMBER_OF_PLAYER_INITIAL_ITEMS))
2389 			continue;
2390 		item.read_indexed("type", player_initial_items[index], NUMBER_OF_DEFINED_ITEMS);
2391 	}
2392 
2393 	BOOST_FOREACH(InfoTree dmg, root.children_named("damage"))
2394 	{
2395 		int16 index;
2396 		if (!dmg.read_indexed("index", index, NUMBER_OF_DAMAGE_RESPONSE_DEFINITIONS))
2397 			continue;
2398 		damage_response_definition& def = damage_response_definitions[index];
2399 
2400 		dmg.read_attr("threshold", def.damage_threshhold);
2401 		dmg.read_attr("fade", def.fade);
2402 		dmg.read_attr("sound", def.sound);
2403 		dmg.read_attr("death_sound", def.death_sound);
2404 		dmg.read_attr("death_action", def.death_action);
2405 	}
2406 
2407 	BOOST_FOREACH(InfoTree assign, root.children_named("powerup_assign"))
2408 	{
2409 		assign.read_indexed("invincibility", player_powerups.Powerup_Invincibility, NUMBER_OF_DEFINED_ITEMS, true);
2410 		assign.read_indexed("invisibility", player_powerups.Powerup_Invisibility, NUMBER_OF_DEFINED_ITEMS, true);
2411 		assign.read_indexed("infravision", player_powerups.Powerup_Infravision, NUMBER_OF_DEFINED_ITEMS, true);
2412 		assign.read_indexed("extravision", player_powerups.Powerup_Extravision, NUMBER_OF_DEFINED_ITEMS, true);
2413 		assign.read_indexed("triple_energy", player_powerups.Powerup_TripleEnergy, NUMBER_OF_DEFINED_ITEMS, true);
2414 		assign.read_indexed("double_energy", player_powerups.Powerup_DoubleEnergy, NUMBER_OF_DEFINED_ITEMS, true);
2415 		assign.read_indexed("energy", player_powerups.Powerup_Energy, NUMBER_OF_DEFINED_ITEMS, true);
2416 		assign.read_indexed("oxygen", player_powerups.Powerup_Oxygen, NUMBER_OF_DEFINED_ITEMS, true);
2417 	}
2418 
2419 	BOOST_FOREACH(InfoTree powerup, root.children_named("powerup"))
2420 	{
2421 		powerup.read_attr_bounded<int16>("invincibility", kINVINCIBILITY_DURATION, 0, SHRT_MAX);
2422 		powerup.read_attr_bounded<int16>("invisibility", kINVISIBILITY_DURATION, 0, SHRT_MAX);
2423 		powerup.read_attr_bounded<int16>("infravision", kINFRAVISION_DURATION, 0, SHRT_MAX);
2424 		powerup.read_attr_bounded<int16>("extravision", kEXTRAVISION_DURATION, 0, SHRT_MAX);
2425 	}
2426 
2427 	BOOST_FOREACH(InfoTree shp, root.children_named("shape"))
2428 	{
2429 		int16 type;
2430 		if (!shp.read_indexed("type", type, 5))
2431 			continue;
2432 
2433 		if (type == 0)
2434 		{
2435 			int16 subtype;
2436 			if (!shp.read_indexed("subtype", subtype, 5))
2437 				continue;
2438 			switch (subtype)
2439 			{
2440 				case 0:
2441 					shp.read_indexed("value", player_shapes.collection, MAXIMUM_COLLECTIONS);
2442 					break;
2443 				case 1:
2444 					shp.read_indexed("value", player_shapes.dying_hard, MAXIMUM_SHAPES_PER_COLLECTION);
2445 					break;
2446 				case 2:
2447 					shp.read_indexed("value", player_shapes.dying_soft, MAXIMUM_SHAPES_PER_COLLECTION);
2448 					break;
2449 				case 3:
2450 					shp.read_indexed("value", player_shapes.dead_hard, MAXIMUM_SHAPES_PER_COLLECTION);
2451 					break;
2452 				case 4:
2453 					shp.read_indexed("value", player_shapes.dead_soft, MAXIMUM_SHAPES_PER_COLLECTION);
2454 					break;
2455 			}
2456 		}
2457 		else if (type == 1)
2458 		{
2459 			int16 subtype;
2460 			if (!shp.read_indexed("subtype", subtype, NUMBER_OF_PLAYER_ACTIONS))
2461 				continue;
2462 			shp.read_indexed("value", player_shapes.legs[subtype], MAXIMUM_SHAPES_PER_COLLECTION);
2463 		}
2464 		else if (type == 2)
2465 		{
2466 			int16 subtype;
2467 			if (!shp.read_indexed("subtype", subtype, PLAYER_TORSO_SHAPE_COUNT))
2468 				continue;
2469 			shp.read_indexed("value", player_shapes.torsos[subtype], MAXIMUM_SHAPES_PER_COLLECTION);
2470 		}
2471 		else if (type == 3)
2472 		{
2473 			int16 subtype;
2474 			if (!shp.read_indexed("subtype", subtype, PLAYER_TORSO_SHAPE_COUNT))
2475 				continue;
2476 			shp.read_indexed("value", player_shapes.charging_torsos[subtype], MAXIMUM_SHAPES_PER_COLLECTION);
2477 		}
2478 		else if (type == 4)
2479 		{
2480 			int16 subtype;
2481 			if (!shp.read_indexed("subtype", subtype, PLAYER_TORSO_SHAPE_COUNT))
2482 				continue;
2483 			shp.read_indexed("value", player_shapes.firing_torsos[subtype], MAXIMUM_SHAPES_PER_COLLECTION);
2484 		}
2485 	}
2486 }
2487 
2488