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