• R/O
  • SSH
  • HTTPS

marathon: コミット


コミットメタ情報

リビジョン532 (tree)
日時2012-10-08 16:01:52
作者logue

ログメッセージ

公式版r4767相当とマージ。(動作確認できず)
今回より、FFmpegが別途必要になった。

変更サマリ

差分

--- marathon/trunk/Source_Files/GameWorld/monsters.cpp (revision 531)
+++ marathon/trunk/Source_Files/GameWorld/monsters.cpp (revision 532)
@@ -1,3948 +1,3949 @@
1-/*
2-MONSTERS.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-Tuesday, November 10, 1992 1:10:20 PM
22-
23-Friday, May 27, 1994 11:21:07 AM
24- split into MONSTERS.C, PROJECTILES.C and EFFECTS.C; unified active_monster and monster array.
25-Friday, September 30, 1994 5:48:25 PM (Jason)
26- started adding comments again. damage_monsters_in_radius() is less forgiving in z now.
27-Monday, December 5, 1994 9:07:37 PM (Jason)
28- rebellion environment function (all _clients hate all _pfhor).
29-Wednesday, February 1, 1995 2:29:01 AM (Jason')
30- kill_sounds; invisible monsters donユt move
31-Wednesday, June 14, 1995 10:14:24 AM (Jason)
32- rewrite for marathon2 (halfway done).
33-Monday, July 10, 1995 11:49:06 AM (Jason)
34- rewrite for marathon2 done. my bobs wonユt listen to your fucking whining.
35-
36-Jan 30, 2000 (Loren Petrich):
37- Added some typecasts
38- Removed some "static" declarations that conflict with "extern"
39-
40-Feb 3, 2000 (Loren Petrich):
41- Treating Jjaro goo like sewage
42-
43-Feb. 4, 2000 (Loren Petrich):
44- Changed halt() to assert(false) for better debugging
45-
46-Feb 6, 2000 (Loren Petrich):
47- Added access to size of monster-definition structure
48-
49-Feb 12, 2000 (Loren Petrich):
50- Suppressed an exposed "dprintf" as an unnecessary interrupt.
51-
52-Feb 16, 2000 (Loren Petrich):
53- Added a check on the polygon index after a line-transparency check;
54- this is in case there is no polygon on the other side.
55-
56-Feb 17, 2000 (Loren Petrich):
57- Fixed stuff near GUESS_HYPOTENUSE() to be long-distance-friendly
58-
59-Feb 19, 2000 (Loren Petrich):
60- Added growable lists of indices of objects to be checked for collisions
61-
62-Feb 24, 2000 (Loren Petrich):
63- Suppressed some asserts about monster speeds
64-
65-Apr 27, 2000 (Loren Petrich):
66- Added some behavior in the case of a monster both floating and flying
67- to handle the map "Aqualung" correctly.
68-
69-May 29, 2000 (Loren Petirch):
70- Fixed side effect of fixing keyframe-never-zero bug:
71- if the keyframe is zero, then a sequence never triggers shrapnel damage.
72- Thus, Hunters die a soft death more harmlessly.
73-
74-Jun 11, 2000 (Loren Petrich):
75- Pegging health and oxygen to maximum values when damaged;
76- takes into account negative damage from healing projectiles.
77-
78-Jul 1, 2000 (Loren Petrich):
79- Inlined the accessors
80-
81-Aug 30, 2000 (Loren Petrich):
82- Added stuff for unpacking and packing
83-
84-Oct 13, 2000 (Loren Petrich)
85- Converted the intersected-objects list into a Standard Template Library vector
86-
87-Oct 26, 2000 (Mark Levin)
88- Revealed a few functions needed by Pfhortran
89-
90-Jan 12, 2003 (Loren Petrich)
91- Added controllable damage kicks
92-*/
93-
94-#include <string.h>
95-#include <limits.h>
96-
97-#include "cseries.h"
98-#include "map.h"
99-#include "render.h"
100-#include "interface.h"
101-#include "FilmProfile.h"
102-#include "flood_map.h"
103-#include "effects.h"
104-#include "monsters.h"
105-#include "projectiles.h"
106-#include "player.h"
107-#include "platforms.h"
108-#include "scenery.h"
109-#include "SoundManager.h"
110-#include "fades.h"
111-#include "items.h"
112-#include "media.h"
113-#include "Packing.h"
114-#include "lua_script.h"
115-#include "Logging.h"
116-
117-
118-#ifdef env68k
119-#pragma segment objects
120-#endif
121-
122-/*
123-//explosive deaths should cause damage during their key frame
124-*/
125-
126-/* ---------- sounds */
127-
128-/* ---------- constants */
129-
130-#define OBSTRUCTION_DEACTIVATION_MASK 0x7
131-
132-#define EVASIVE_MANOUVER_DISTANCE WORLD_ONE_HALF
133-
134-#define MONSTER_EXTERNAL_DECELERATION (WORLD_ONE/200)
135-#define MONSTER_MINIMUM_EXTERNAL_VELOCITY (10*MONSTER_EXTERNAL_DECELERATION)
136-#define MONSTER_MAXIMUM_EXTERNAL_VELOCITY (TICKS_PER_SECOND*MONSTER_EXTERNAL_DECELERATION)
137-
138-/* the height below which we donユt bother to float up a ledge (we just run right over it) */
139-#define MINIMUM_FLOATING_HEIGHT WORLD_ONE_FOURTH
140-
141-#define MINIMUM_ACTIVATION_SEPARATION TICKS_PER_SECOND
142-
143-/* when looking for things under or at this light intensity the monster must use his dark visual range */
144-#define LOW_LIGHT_INTENSITY 0
145-
146-/* maximum area we will search out to find a new target */
147-#define MAXIMUM_TARGET_SEARCH_AREA (7*WORLD_ONE*WORLD_ONE)
148-
149-#define MONSTER_PLATFORM_BUFFER_DISTANCE (WORLD_ONE/8)
150-
151-#define GLUE_TRIGGER_ACTIVATION_RANGE (8*WORLD_ONE)
152-#define MONSTER_ALERT_ACTIVATION_RANGE (5*WORLD_ONE)
153-
154-#define MONSTER_PATHFINDING_OBSTRUCTION_COST (2*WORLD_ONE*WORLD_ONE)
155-#define MONSTER_PATHFINDING_PLATFORM_COST (4*WORLD_ONE*WORLD_ONE)
156-#define MINIMUM_MONSTER_PATHFINDING_POLYGON_AREA (WORLD_ONE)
157-
158-#define TERMINAL_VERTICAL_MONSTER_VELOCITY (WORLD_ONE/5)
159-
160-#define MINIMUM_DYING_EXTERNAL_VELOCITY (WORLD_ONE/8)
161-
162-#define CIVILIANS_KILLED_BY_PLAYER_THRESHHOLD 3
163-#define CIVILIANS_KILLED_DECREMENT_MASK 0x1ff
164-
165-enum /* monster attitudes, extracted from enemies and friends bitfields by get_monster_attitude() */
166-{
167- _neutral,
168- _friendly,
169- _hostile
170-};
171-
172-enum /* returned by find_obstructing_terrain_feature() */
173-{
174- _standing_on_sniper_ledge,
175- _entering_platform_polygon,
176- _leaving_platform_polygon,
177- _flying_or_floating_transition
178-};
179-
180-#define MINIMUM_SNIPER_ELEVATION WORLD_ONE_HALF
181-
182-/* ---------- structures */
183-
184-struct monster_pathfinding_data
185-{
186- struct monster_definition *definition;
187- struct monster_data *monster;
188-
189- bool cross_zone_boundaries;
190-};
191-
192-// How much external velocity is imparted by some damage?
193-struct damage_kick_definition
194-{
195- short base_value;
196- float delta_vitality_multiplier;
197- bool is_also_vertical;
198-
199- // if non-zero, will enable vertical_component if
200- // delta_vitality is greater than threshold
201- short vertical_threshold;
202-};
203-
204-/* ---------- definitions */
205-
206-// LP: implements commented-out damage-kick code
207-struct damage_kick_definition damage_kick_definitions[NUMBER_OF_DAMAGE_TYPES] =
208-{
209- {0, 1, true, 0}, // _damage_explosion,
210- {0, 3, true, 0}, // _damage_electrical_staff,
211- {0, 1, false, 0}, // _damage_projectile,
212- {0, 1, false, 0}, // _damage_absorbed,
213- {0, 1, false, 0}, // _damage_flame,
214- {0, 1, false, 0}, // _damage_hound_claws,
215- {0, 1, false, 0}, // _damage_alien_projectile,
216- {0, 1, false, 0}, // _damage_hulk_slap,
217- {0, 3, true, 0}, // _damage_compiler_bolt,
218- {0, 0, false, 100}, // _damage_fusion_bolt,
219- {0, 1, false, 0}, // _damage_hunter_bolt,
220- {0, 1, false, 0}, // _damage_fist,
221- {250, 0, false, 0}, // _damage_teleporter,
222- {0, 1, false, 0}, // _damage_defender,
223- {0, 3, true, 0}, // _damage_yeti_claws,
224- {0, 1, false, 0}, // _damage_yeti_projectile,
225- {0, 1, false, 0}, // _damage_crushing,
226- {0, 1, false, 0}, // _damage_lava,
227- {0, 1, false, 0}, // _damage_suffocation,
228- {0, 1, false, 0}, // _damage_goo,
229- {0, 1, false, 0}, // _damage_energy_drain,
230- {0, 1, false, 0}, // _damage_oxygen_drain,
231- {0, 1, false, 0}, // _damage_hummer_bolt,
232- {0, 0, true, 0} // _damage_shotgun_projectile,
233-};
234-
235-/* ---------- globals */
236-
237-/* import monster definition constants, structures and globals */
238-#include "monster_definitions.h"
239-
240-// LP addition: growable list of intersected objects
241-static vector<short> IntersectedObjects;
242-
243-/* ---------- private prototypes */
244-
245-static monster_definition *get_monster_definition(
246- const short type);
247-
248-static void monster_needs_path(short monster_index, bool immediately);
249-static void generate_new_path_for_monster(short monster_index);
250-void advance_monster_path(short monster_index);
251-
252-static short get_monster_attitude(short monster_index, short target_index);
253-void change_monster_target(short monster_index, short target_index);
254-static bool switch_target_check(short monster_index, short attacker_index, short delta_vitality);
255-static bool clear_line_of_sight(short viewer_index, short target_index, bool full_circle);
256-
257-static void handle_moving_or_stationary_monster(short monster_index);
258-static void execute_monster_attack(short monster_index);
259-static void kill_monster(short monster_index);
260-static bool translate_monster(short monster_index, world_distance distance);
261-static bool try_monster_attack(short monster_index);
262-
263-int32 monster_pathfinding_cost_function(short source_polygon_index, short line_index,
264- short destination_polygon_index, void *data);
265-
266-void set_monster_action(short monster_index, short action);
267-void set_monster_mode(short monster_index, short new_mode, short target_index);
268-
269-static short find_obstructing_terrain_feature(short monster_index, short *feature_index, short *relevant_polygon_index);
270-
271-static short position_monster_projectile(short aggressor_index, short target_index, struct attack_definition *attack,
272- world_point3d *origin, world_point3d *destination, world_point3d *_vector, angle theta);
273-
274-static void update_monster_vertical_physics_model(short monster_index);
275-static void update_monster_physics_model(short monster_index);
276-
277-static int32 monster_activation_flood_proc(short source_polygon_index, short line_index,
278- short destination_polygon_index, void *data);
279-
280-static bool attempt_evasive_manouvers(short monster_index);
281-
282-static short nearest_goal_polygon_index(short polygon_index);
283-static int32 nearest_goal_cost_function(short source_polygon_index, short line_index,
284- short destination_polygon_index, void *unused);
285-
286-static void cause_shrapnel_damage(short monster_index);
287-
288-// For external use
289-monster_definition *get_monster_definition_external(const short type);
290-
291-/* ---------- code */
292-
293-monster_data *get_monster_data(
294- short monster_index)
295-{
296- struct monster_data *monster = GetMemberWithBounds(monsters,monster_index,MAXIMUM_MONSTERS_PER_MAP);
297-
298- vassert(monster, csprintf(temporary, "monster index #%d is out of range", monster_index));
299- vassert(SLOT_IS_USED(monster), csprintf(temporary, "monster index #%d (%p) is unused", monster_index, monster));
300-
301- return monster;
302-}
303-
304-monster_definition *get_monster_definition(
305- const short type)
306-{
307- monster_definition *definition = GetMemberWithBounds(monster_definitions,type,NUMBER_OF_MONSTER_TYPES);
308- assert(definition);
309-
310- return definition;
311-}
312-
313-//a non-inlined version for external use
314-monster_definition *get_monster_definition_external(
315- const short type)
316-{
317- return get_monster_definition(type);
318-}
319-
320-
321-/* returns new monster index if successful, NONE otherwise */
322-short new_monster(
323- struct object_location *location,
324- short monster_type)
325-{
326- struct monster_definition *definition= get_monster_definition(monster_type);
327- short original_monster_type= monster_type;
328- struct monster_data *monster;
329- short drop_mask= NONE;
330- short monster_index= NONE;
331- short flags= _monster_has_never_been_activated;
332-
333- switch (dynamic_world->game_information.difficulty_level)
334- {
335- case _wuss_level: drop_mask= 3; break; /* drop every fourth monster */
336- case _easy_level: drop_mask= 7; break; /* drop every eighth monster */
337- /* otherwise, drop no monsters */
338- }
339-
340- if ((definition->flags&_monster_cannot_be_dropped) || !(definition->flags&_monster_is_alien) || drop_mask==NONE || (++dynamic_world->new_monster_vanishing_cookie&drop_mask))
341- {
342- /* check to see if we should promote or demote this monster based on difficulty level */
343- if (definition->flags&_monster_major)
344- {
345- short demote_mask= NONE;
346-
347- switch (dynamic_world->game_information.difficulty_level)
348- {
349- case _wuss_level: demote_mask= 1; break; /* demote every other major */
350- case _easy_level: demote_mask= 3; break; /* demote every fourth major */
351- /* otherwise, demote no monsters */
352- }
353-
354- if (demote_mask!=NONE && !(++dynamic_world->new_monster_mangler_cookie&demote_mask)) definition= get_monster_definition(monster_type-= 1), flags|= _monster_was_demoted;
355- }
356- else
357- {
358- if (definition->flags&_monster_minor)
359- {
360- short promote_mask= NONE;
361-
362- switch (dynamic_world->game_information.difficulty_level)
363- {
364- case _major_damage_level: promote_mask= 1; break; /* promote every other minor */
365- case _total_carnage_level: promote_mask= 0; break; /* promote every minor */
366- /* otherwise, promote no monsters */
367- }
368- if (promote_mask!=NONE && !(++dynamic_world->new_monster_mangler_cookie&promote_mask)) definition= get_monster_definition(monster_type+= 1), flags|= _monster_was_promoted;
369- }
370- }
371-
372- for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster_index,++monster)
373- {
374- if (SLOT_IS_FREE(monster))
375- {
376- short object_index= new_map_object(location, BUILD_DESCRIPTOR(definition->collection, definition->stationary_shape));
377-
378- if (object_index!=NONE)
379- {
380- struct object_data *object= get_object_data(object_index);
381-
382- /* not doing this in !DEBUG resulted in sync errors; mmm... random data, so tasty */
383- obj_set(*monster, 0x80);
384-
385- if (location->flags&_map_object_is_blind) flags|= _monster_is_blind;
386- if (location->flags&_map_object_is_deaf) flags|= _monster_is_deaf;
387- if (location->flags&_map_object_floats) flags|= _monster_teleports_out_when_deactivated;
388-
389- /* initialize the monster_data structure; we donユt touch most of the fields here
390- because the monster is initially inactive (and they will be initialized when the
391- monster is activated) */
392- monster->type= monster_type;
393- monster->activation_bias= DECODE_ACTIVATION_BIAS(location->flags);
394- monster->vitality= NONE; /* if a monster is activated with vitality==NONE, it will be properly initialized */
395- monster->object_index= object_index;
396- monster->flags= flags;
397- monster->goal_polygon_index= monster->activation_bias==_activate_on_goal ?
398- nearest_goal_polygon_index(location->polygon_index) : NONE;
399- monster->sound_polygon_index= object->polygon;
400- monster->sound_location= object->location;
401- MARK_SLOT_AS_USED(monster);
402-
403- /* initialize the monsterユs object */
404- if (definition->flags&_monster_is_invisible) object->transfer_mode= _xfer_invisibility;
405- if (definition->flags&_monster_is_subtly_invisible) object->transfer_mode= _xfer_subtle_invisibility;
406- if (definition->flags&_monster_is_enlarged) object->flags|= _object_is_enlarged;
407- if (definition->flags&_monster_is_tiny) object->flags|= _object_is_tiny;
408- SET_OBJECT_SOLIDITY(object, true);
409- SET_OBJECT_OWNER(object, _object_is_monster);
410- object->permutation= monster_index;
411- object->sound_pitch= definition->sound_pitch;
412-
413- /* make sure the object frequency stuff keeps track of how many monsters are
414- on the map */
415- object_was_just_added(_object_is_monster, original_monster_type);
416- }
417- else
418- {
419- monster_index= NONE;
420- }
421-
422- break;
423- }
424- }
425- if (monster_index==MAXIMUM_MONSTERS_PER_MAP) monster_index= NONE;
426- }
427-
428- /* keep track of how many civilians we drop on this level */
429-// if (monster_index!=NONE && (definition->_class&_class_human_civilian)) dynamic_world->current_civilian_count+= 1;
430-
431- return monster_index;
432-}
433-
434-/* assumes カt==1 tick */
435-void move_monsters(
436- void)
437-{
438- struct monster_data *monster;
439- bool monster_got_time= false;
440- bool monster_built_path= (dynamic_world->tick_count&3) ? true : false;
441- short monster_index;
442-
443- for (monster_index= 0, monster= monsters; monster_index<MAXIMUM_MONSTERS_PER_MAP; ++monster_index, ++monster)
444- {
445- if (SLOT_IS_USED(monster) && !MONSTER_IS_PLAYER(monster))
446- {
447- struct object_data *object= get_object_data(monster->object_index);
448-
449- if (MONSTER_IS_ACTIVE(monster))
450- {
451- if (!OBJECT_IS_INVISIBLE(object))
452- {
453- struct monster_definition *definition= get_monster_definition(monster->type);
454- short animation_flags;
455-
456- // AlexJLS patch: effect of dangerous polygons
457- cause_polygon_damage(object->polygon,monster_index);
458-
459- /* clear the recovering from hit flag, mark the monster as not idle */
460- SET_MONSTER_IDLE_STATUS(monster, false);
461-
462- update_monster_vertical_physics_model(monster_index);
463-
464- /* update our objectユs animation unless weユre ヤsufferingユ from an external velocity
465- or weユre airborne (if weユre a flying or floating monster, ignore both of these */
466- if ((!monster->external_velocity&&!monster->vertical_velocity) ||
467- ((monster->action!=_monster_is_being_hit||!monster->external_velocity) && (definition->flags&(_monster_floats|_monster_flys))))
468- {
469- animate_object(monster->object_index);
470- }
471- animation_flags= GET_OBJECT_ANIMATION_FLAGS(object);
472-
473- /* give this monster time, if we can and he needs it */
474- if (!monster_got_time && monster_index>dynamic_world->last_monster_index_to_get_time && !MONSTER_IS_DYING(monster))
475- {
476- switch (monster->mode)
477- {
478- case _monster_unlocked:
479- /* if this monster is unlocked and we havenユt already given a monster time,
480- call find_closest_appropriate_target() */
481- change_monster_target(monster_index, find_closest_appropriate_target(monster_index, false));
482- monster_got_time= true;
483- break;
484-
485- case _monster_lost_lock:
486- case _monster_losing_lock:
487- /* if this monster has lost or is losing lock and we havenユt already given a monster
488- time, check to see if his target has become visible again */
489- if (clear_line_of_sight(monster_index, monster->target_index, false))
490- {
491- change_monster_target(monster_index, monster->target_index);
492- }
493- monster_got_time= true;
494- break;
495- }
496-
497- /* if we gave this guy time, make room for the next guy */
498- if (monster_got_time) dynamic_world->last_monster_index_to_get_time= monster_index;
499- }
500-
501- /* if this monster needs a path, generate one (unless weユve already generated a
502- path this frame in which case weユll wait until next frame, UNLESS the monster
503- has no path in which case it needs one regardless) */
504- if (MONSTER_NEEDS_PATH(monster) && !MONSTER_IS_DYING(monster) && !MONSTER_IS_ATTACKING(monster) &&
505- ((!monster_built_path && monster_index>dynamic_world->last_monster_index_to_build_path) || monster->path==NONE))
506- {
507- generate_new_path_for_monster(monster_index);
508- if (!monster_built_path)
509- {
510- monster_built_path= true;
511- dynamic_world->last_monster_index_to_build_path= monster_index;
512- }
513- }
514-
515- /* itユs possible that we couldnユt get where we wanted to go, or that we arrived there
516- and deactivated ourselves; if this happens we donユt want to continue processing
517- the monster as if it were active */
518- if (MONSTER_IS_ACTIVE(monster))
519- {
520- /* move the monster; check to see if we can attack; resolve modes ending; etc. */
521- switch (monster->action)
522- {
523- case _monster_is_waiting_to_attack_again:
524- case _monster_is_stationary:
525- case _monster_is_moving:
526- handle_moving_or_stationary_monster(monster_index);
527- break;
528-
529- case _monster_is_attacking_close:
530- case _monster_is_attacking_far:
531- if (animation_flags&_obj_keyframe_started) execute_monster_attack(monster_index);
532- if (animation_flags&_obj_last_frame_animated)
533- {
534- if (((monster->attack_repetitions-=1)<0) || !try_monster_attack(monster_index))
535- {
536- /* after an attack has been initiated successfully we need to return to
537- _monster_is_moving action, kill our path and ask for a new one
538- (because weユre pointed in the wrong direction now) */
539- set_monster_action(monster_index,
540- (monster->attack_repetitions<0 && (definition->flags&_monster_waits_with_clear_shot) && MONSTER_IS_LOCKED(monster)) ?
541- _monster_is_waiting_to_attack_again : _monster_is_moving);
542- monster_needs_path(monster_index, true);
543- monster->ticks_since_attack= 0;
544- }
545- }
546- break;
547-
548- case _monster_is_teleporting_in:
549- if (animation_flags&_obj_last_frame_animated)
550- {
551- monster->action= _monster_is_moving;
552- set_monster_action(monster_index, _monster_is_moving);
553- change_monster_target(monster_index, find_closest_appropriate_target(monster_index, false));
554- }
555- break;
556- case _monster_is_teleporting_out:
557- if (animation_flags&_obj_keyframe_started)
558- {
559- monster->action= _monster_is_dying_soft; // to prevent aggressors from relocking
560- monster_died(monster_index);
561- teleport_object_out(monster->object_index);
562- remove_map_object(monster->object_index);
563- L_Invalidate_Monster(monster_index);
564- MARK_SLOT_AS_FREE(monster);
565- }
566- break;
567-
568- case _monster_is_being_hit:
569- update_monster_physics_model(monster_index);
570- if (animation_flags&_obj_last_frame_animated)
571- {
572- monster_needs_path(monster_index, true);
573- set_monster_action(monster_index, _monster_is_moving);
574- monster->external_velocity= 0;
575- }
576- break;
577-
578- case _monster_is_dying_soft:
579- case _monster_is_dying_hard:
580- case _monster_is_dying_flaming:
581- update_monster_physics_model(monster_index);
582- if ((definition->flags&_monster_has_delayed_hard_death) && monster->action==_monster_is_dying_soft)
583- {
584- if (!monster->external_velocity && object->location.z==monster->desired_height) //&& !monster->vertical_velocity)
585- {
586- set_monster_action(monster_index, _monster_is_dying_hard);
587- }
588- else
589- {
590- if (definition->contrail_effect!=NONE) new_effect(&object->location, object->polygon, definition->contrail_effect, object->facing);
591- }
592- }
593- else
594- {
595- // LP change: if keyframe is zero, then a monster should not produce shrapnel damage.
596- // This fixes a side effect of a fix of the keyframe-never-zero bug,
597- // which is that Hunters injure those nearby when they die a soft death.
598- if (animation_flags&_obj_keyframe_started && (!film_profile.keyframe_fix || GET_SEQUENCE_FRAME(object->sequence) != 0))
599- cause_shrapnel_damage(monster_index);
600- if (animation_flags&_obj_last_frame_animated) kill_monster(monster_index);
601- }
602- break;
603-
604- default:
605- assert(false);
606- break;
607- }
608- }
609- }
610- }
611- else
612- {
613- /* all inactive monsters get time to scan for targets */
614- if (!monster_got_time && !MONSTER_IS_BLIND(monster) && monster_index>dynamic_world->last_monster_index_to_get_time)
615- {
616- change_monster_target(monster_index, find_closest_appropriate_target(monster_index, false));
617- if (MONSTER_HAS_VALID_TARGET(monster)) activate_nearby_monsters(monster->target_index, monster_index, _pass_one_zone_border);
618-
619- monster_got_time= true;
620- dynamic_world->last_monster_index_to_get_time= monster_index;
621- }
622- }
623- }
624-
625- /* WARNING: a large number of unusual things could have happened here, including the monster
626- being dead, his slot being free, and his object having been removed from the map; in other
627- words, itユs probably not a good idea to do any postprocessing here */
628- }
629-
630- /* either there are no unlocked monsters or ヤdynamic_world->last_monster_index_to_get_timeユ is higher than
631- all of them (so we reset it to zero) ... same for paths */
632- if (!monster_got_time) dynamic_world->last_monster_index_to_get_time= -1;
633- if (!monster_built_path) dynamic_world->last_monster_index_to_build_path= -1;
634-
635- if (dynamic_world->civilians_killed_by_players)
636- {
637- uint32 mask = 0;
638-
639- switch (dynamic_world->game_information.difficulty_level)
640- {
641- case _wuss_level: mask= 0x7f; break;
642- case _easy_level: mask= 0xff; break;
643- case _normal_level: mask= 0x1ff; break;
644- case _major_damage_level: mask= 0x3ff; break;
645- case _total_carnage_level: mask= 0x7ff; break;
646- }
647-
648- if (!(dynamic_world->tick_count&mask))
649- {
650- dynamic_world->civilians_killed_by_players-= 1;
651- }
652- }
653-}
654-
655-/* when a monster dies, all monsters locked on it need to find something better to do; this
656- function should be called before the given target is expunged from the monster list but
657- after it is marked as dying */
658-void monster_died(
659- short target_index)
660-{
661- struct monster_data *monster= get_monster_data(target_index);
662- short monster_index;
663-
664-// dprintf("monster #%d is dead;g;", target_index);
665-
666- /* orphan this monsterユs projectiles if they donユt belong to a player (playerユs monster
667- slots are always valid and we want to correctly attribute damage and kills that ocurr
668- after a player dies) */
669- if (!MONSTER_IS_PLAYER(monster)) orphan_projectiles(target_index);
670-
671- /* active monsters need extant paths deleted and should be marked as unlocked */
672- if (MONSTER_IS_ACTIVE(monster))
673- {
674- set_monster_mode(target_index, _monster_unlocked, NONE);
675- if (monster->path!=NONE) delete_path(monster->path);
676- SET_MONSTER_NEEDS_PATH_STATUS(monster, false);
677- monster->path= NONE;
678- }
679-
680- /* anyone locked on this monster needs a clue */
681- for (monster_index= 0, monster= monsters; monster_index<MAXIMUM_MONSTERS_PER_MAP; ++monster_index, ++monster)
682- {
683- if (SLOT_IS_USED(monster) && MONSTER_IS_ACTIVE(monster) && monster->target_index==target_index)
684- {
685- short closest_target_index= find_closest_appropriate_target(monster_index, true);
686-
687- monster->target_index= NONE;
688- monster_needs_path(monster_index, false);
689-
690- play_object_sound(monster->object_index, get_monster_definition(monster->type)->kill_sound);
691- if (closest_target_index!=NONE)
692- {
693- change_monster_target(monster_index, closest_target_index);
694- }
695- else
696- {
697- if (monster->action==_monster_is_waiting_to_attack_again) set_monster_action(monster_index, _monster_is_moving);
698- set_monster_mode(monster_index, _monster_unlocked, NONE);
699- }
700- }
701- }
702-}
703-
704-void initialize_monsters(
705- void)
706-{
707- /* initialize our globals to be the same thing on all machines */
708- dynamic_world->civilians_killed_by_players= 0;
709- dynamic_world->last_monster_index_to_get_time= -1;
710- dynamic_world->last_monster_index_to_build_path= -1;
711- dynamic_world->new_monster_mangler_cookie= global_random();
712- dynamic_world->new_monster_vanishing_cookie= global_random();
713-}
714-
715-/* call this when a new level is loaded from disk so the monsters can cope with their new world */
716-void initialize_monsters_for_new_level(
717- void)
718-{
719- struct monster_data *monster;
720- short monster_index;
721-
722- /* when a level is loaded after being saved all of an active monsterユs data is still intact,
723- but itユs path no longer exists. this function resets all monsters so that they recalculate
724- their paths, first thing. */
725- for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster_index,++monster)
726- {
727- if (SLOT_IS_USED(monster)&&MONSTER_IS_ACTIVE(monster))
728- {
729- SET_MONSTER_NEEDS_PATH_STATUS(monster, true);
730- monster->path= NONE;
731- }
732- }
733-}
734-
735-static void load_sound(short sound_index)
736-{
737- SoundManager::instance()->LoadSound(sound_index);
738-}
739-
740-void load_monster_sounds(
741- short monster_type)
742-{
743- if (monster_type!=NONE)
744- {
745- struct monster_definition *definition= get_monster_definition(monster_type);
746-
747- process_collection_sounds(definition->collection, load_sound);
748-
749- load_projectile_sounds(definition->ranged_attack.type);
750- load_projectile_sounds(definition->melee_attack.type);
751-
752- SoundManager::instance()->LoadSounds(&definition->activation_sound, 8);
753- }
754-}
755-
756-void mark_monster_collections(
757- short monster_type,
758- bool loading)
759-{
760- if (monster_type!=NONE)
761- {
762- struct monster_definition *definition= get_monster_definition(monster_type);
763-
764- /* mark the monster collection */
765- mark_collection(definition->collection, loading);
766-
767- /* mark the monsterユs projectileユs collection */
768- mark_projectile_collections(definition->ranged_attack.type, loading);
769- mark_projectile_collections(definition->melee_attack.type, loading);
770- }
771-}
772-
773-enum
774-{
775- MAXIMUM_NEED_TARGET_INDEXES= 32
776-};
777-
778-void activate_nearby_monsters(
779- short target_index, /* activate with lock on this target (or NONE for lock-less activation) */
780- short caller_index, /* start the flood from here */
781- short flags)
782-{
783- struct monster_data *caller= get_monster_data(caller_index);
784-
785- if (dynamic_world->tick_count-caller->ticks_since_last_activation>MINIMUM_ACTIVATION_SEPARATION ||
786- (flags&_activation_cannot_be_avoided))
787- {
788- short polygon_index= get_object_data(caller->object_index)->polygon;
789- short need_target_indexes[MAXIMUM_NEED_TARGET_INDEXES];
790- short need_target_count= 0;
791- int32 flood_flags= flags;
792-
793- /* flood out from the target monsterユs polygon, searching through the object lists of all
794- polygons we encounter */
795- polygon_index= flood_map(polygon_index, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
796- while (polygon_index!=NONE)
797- {
798- short object_index;
799- struct object_data *object;
800-
801- /* loop through all objects in this polygon looking for _hostile inactive or unlocked monsters */
802- for (object_index= get_polygon_data(polygon_index)->first_object; object_index!=NONE; object_index= object->next_object)
803- {
804- object= get_object_data(object_index);
805- if (GET_OBJECT_OWNER(object)==_object_is_monster &&
806- (!OBJECT_IS_INVISIBLE(object) || (flags&_activate_invisible_monsters)))
807- {
808- short aggressor_index= object->permutation;
809- struct monster_data *aggressor= get_monster_data(aggressor_index);
810-// bool target_hostile= get_monster_attitude(aggressor_index, target_index)==_hostile;
811-// bool caller_hostile= get_monster_attitude(aggressor_index, caller_index)==_hostile;
812-
813-// deaf monsters are only deaf to players which have always been hostile, so:
814-// bobs are deaf to friendly players but not hostile ones
815-// monsters are deaf to all players
816-// deaf monsters ignore friendly monsters activating on other friendly monsters but
817-// non-deaf ones DO NOT
818-
819-// !MONSTER_IS_PLAYER(caller) || TYPE_IS_FRIEND(get_monster_definition(aggressor->type), caller->type) || caller_hostile
820-
821- /* donユt activate players or ourselves, and only activate monsters on glue polygons
822- if they have previously been activated or weユve been explicitly told to */
823- if (!MONSTER_IS_PLAYER(aggressor) && caller_index!=aggressor_index && target_index!=aggressor_index &&
824- (!(flood_flags&_passed_zone_border) || (!(aggressor->flags&_monster_has_never_been_activated))) &&
825- ((flood_flags&_activate_deaf_monsters) || !MONSTER_IS_DEAF(aggressor)) && // || !MONSTER_IS_PLAYER(caller) || !TYPE_IS_FRIEND(get_monster_definition(aggressor->type), caller->type) || !caller_hostile) &&
826- aggressor->mode!=_monster_locked)
827- {
828- bool monster_was_active= true;
829-
830- /* activate the monster if heユs inactive */
831- if (!MONSTER_IS_ACTIVE(aggressor))
832- {
833- activate_monster(aggressor_index);
834- monster_was_active= false;
835- }
836-
837- if (monster_was_active || !(flags&_use_activation_biases) ||
838- (aggressor->activation_bias!=_activate_on_goal && aggressor->activation_bias!=_activate_randomly))
839- {
840- if (monster_was_active || aggressor->activation_bias!=_activate_on_nearest_hostile)
841- {
842- /* if we have valid target and this monster thinks that target is hostile, lock on */
843- if (get_monster_attitude(aggressor_index, target_index)==_hostile)
844- {
845- switch_target_check(aggressor_index, target_index, 0);
846- }
847- else
848- {
849- /* but hey, if the target isnユt hostile, maybe the caller is ...
850- (mostly for the automated defenses and the civilians on the ship) */
851- if (get_monster_attitude(aggressor_index, caller_index)==_hostile)
852- {
853- switch_target_check(aggressor_index, caller_index, 0);
854- }
855- }
856- }
857- else
858- {
859- // must defer find_closest_appropriate_target; pathfinding is not reentrant
860- if (need_target_count<MAXIMUM_NEED_TARGET_INDEXES)
861- {
862- need_target_indexes[need_target_count++]= aggressor_index;
863- }
864- }
865- }
866- }
867- }
868- }
869-
870- polygon_index= flood_map(NONE, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
871- }
872-
873- // deferred find_closest_appropriate_target() calls
874- while (--need_target_count>=0)
875- {
876- change_monster_target(need_target_indexes[need_target_count],
877- find_closest_appropriate_target(need_target_indexes[need_target_count], true));
878- }
879-
880- caller->ticks_since_last_activation= dynamic_world->tick_count;
881- }
882-}
883-
884-static int32 monster_activation_flood_proc(
885- short source_polygon_index,
886- short line_index,
887- short destination_polygon_index,
888- void *data)
889-{
890- int32 *flags=(int32 *)data;
891- struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
892- struct line_data *line= get_line_data(line_index);
893- int32 cost= 1;
894-
895-// dprintf("P#%d==>P#%d by L#%d", source_polygon_index, destination_polygon_index, line_index);
896-
897- (void) (source_polygon_index);
898-
899- if (destination_polygon->type==_polygon_is_zone_border)
900- {
901- if (((*flags)&_pass_one_zone_border) && !((*flags)&_passed_zone_border))
902- {
903- *flags|= _passed_zone_border;
904- }
905- else
906- {
907- // canユt pass this zone border
908- cost= -1;
909- }
910- }
911-
912- if (!((*flags)&_pass_solid_lines) && LINE_IS_SOLID(line)) cost= -1;
913-
914- return cost;
915-}
916-
917-#define LIVE_ALIEN_THRESHHOLD 8
918-
919-bool live_aliens_on_map(
920- void)
921-{
922- bool found_alien_which_must_be_killed= false;
923- struct monster_data *monster;
924- short live_alien_count= 0;
925- short threshhold= LIVE_ALIEN_THRESHHOLD;
926- short monster_index;
927-
928- for (monster_index= 0, monster= monsters; monster_index<MAXIMUM_MONSTERS_PER_MAP; ++monster_index, ++monster)
929- {
930- if (SLOT_IS_USED(monster))
931- {
932- struct monster_definition *definition= get_monster_definition(monster->type);
933-
934-#if 0
935- switch (monster->type)
936- {
937- case _monster_juggernaut_minor:
938- case _monster_juggernaut_major:
939- case _monster_alien_leader:
940- found_alien_which_must_be_killed= true;
941- break;
942- }
943-#endif
944-
945- if ((definition->flags&_monster_is_alien) ||
946- ((static_world->environment_flags&_environment_rebellion) && !MONSTER_IS_PLAYER(monster)))
947- {
948- live_alien_count+= 1;
949- }
950- }
951- }
952-
953- if (static_world->environment_flags&_environment_rebellion) threshhold= 0;
954-
955- return live_alien_count<=threshhold ? found_alien_which_must_be_killed : true;
956-}
957-
958-/* activate the given monster (initially unlocked) */
959-void activate_monster(
960- short monster_index)
961-{
962- struct monster_data *monster= get_monster_data(monster_index);
963- struct object_data *object= get_object_data(monster->object_index);
964- struct monster_definition *definition= get_monster_definition(monster->type);
965-
966-// dprintf("monster #%d activated;g;", monster_index);
967-
968- assert(!MONSTER_IS_ACTIVE(monster));
969- assert(!MONSTER_IS_PLAYER(monster));
970-
971- if (OBJECT_IS_INVISIBLE(object))
972- {
973- struct polygon_data *polygon= get_polygon_data(object->polygon);
974-
975- if (polygon->media_index!=NONE)
976- {
977- struct media_data *media= get_media_data(polygon->media_index);
978-
979- // LP change: idiot-proofed this
980- if (media)
981- {
982- if (media->height>object->location.z+definition->height &&
983- !(definition->flags&_monster_can_teleport_under_media))
984- {
985- return;
986- }
987- }
988- }
989- }
990-
991- CLEAR_MONSTER_RECOVERING_FROM_HIT(monster);
992- SET_MONSTER_IDLE_STATUS(monster, false);
993- SET_MONSTER_ACTIVE_STATUS(monster, true);
994- SET_MONSTER_BERSERK_STATUS(monster, false);
995- SET_MONSTER_HAS_BEEN_ACTIVATED(monster);
996- monster->flags&= ~(_monster_is_blind|_monster_is_deaf);
997-
998- monster->path= NONE;
999- /* we used to set monster->target_index here, but it is invalid when mode==_monster_unlocked */
1000- monster->mode= _monster_unlocked, monster->target_index= NONE;
1001-
1002- if (definition->attack_frequency == 0) // IP: Avoid division by zero
1003- definition->attack_frequency = 1;
1004-
1005- monster->ticks_since_attack= (definition->flags&_monster_attacks_immediately) ?
1006- definition->attack_frequency : global_random()%definition->attack_frequency;
1007- monster->desired_height= object->location.z; /* best guess */
1008- monster->random_desired_height= INT16_MAX; // to be out of range and recalculated
1009- monster->external_velocity= monster->vertical_velocity= 0;
1010- monster->ticks_since_last_activation= 0;
1011-
1012- /* if vitality is NONE (-1) initialize it from the monster_definition, respecting
1013- the difficulty level if necessary */
1014- if (monster->vitality==NONE)
1015- {
1016- short vitality= definition->vitality;
1017-
1018- if (definition->flags&_monster_is_alien)
1019- {
1020- switch (dynamic_world->game_information.difficulty_level)
1021- {
1022- case _wuss_level: vitality-= vitality>>1; break;
1023- case _easy_level: vitality-= vitality>>2; break;
1024- case _major_damage_level: vitality+= vitality>>2; break;
1025- case _total_carnage_level: vitality+= vitality>>1; break;
1026- }
1027- }
1028-
1029- monster->vitality= vitality;
1030- }
1031-
1032- set_monster_action(monster_index, _monster_is_stationary);
1033- monster_needs_path(monster_index, true);
1034-
1035- if (OBJECT_IS_INVISIBLE(object))
1036- {
1037- teleport_object_in(monster->object_index);
1038- }
1039-
1040- changed_polygon(object->polygon, object->polygon, NONE);
1041-}
1042-
1043-void deactivate_monster(
1044- short monster_index)
1045-{
1046- struct monster_data *monster= get_monster_data(monster_index);
1047-
1048-// dprintf("monster #%d deactivated;g;", monster_index);
1049-
1050- assert(MONSTER_IS_ACTIVE(monster));
1051-
1052- if (MONSTER_TELEPORTS_OUT_WHEN_DEACTIVATED(monster)) monster->vertical_velocity= monster->external_velocity= 0;
1053-
1054- if (!monster->vertical_velocity && !monster->external_velocity)
1055- {
1056- if (MONSTER_TELEPORTS_OUT_WHEN_DEACTIVATED(monster) && monster->action!=_monster_is_teleporting_out)
1057- {
1058- set_monster_action(monster_index, _monster_is_teleporting_out);
1059- }
1060- else
1061- {
1062- /* assume stationary shape before deactivation */
1063- set_monster_action(monster_index, _monster_is_stationary);
1064-
1065- /* get rid of this monsterユs path if he has one */
1066- if (monster->path!=NONE) delete_path(monster->path);
1067-
1068- SET_MONSTER_ACTIVE_STATUS(monster, false);
1069- }
1070- }
1071-}
1072-
1073-/* returns a list of object indexes of all monsters in or adjacent to the given polygon,
1074- up to maximum_object_count. */
1075-// LP change: called with growable list
1076-bool possible_intersecting_monsters(
1077- vector<short> *IntersectedObjectsPtr,
1078- unsigned maximum_object_count,
1079- short polygon_index,
1080- bool include_scenery)
1081-{
1082- struct polygon_data *polygon= get_polygon_data(polygon_index);
1083- short *neighbor_indexes= get_map_indexes(polygon->first_neighbor_index, polygon->neighbor_count);
1084- bool found_solid_object= false;
1085-
1086- // Skip this step if neighbor indexes were not found
1087- if (!neighbor_indexes) return found_solid_object;
1088-
1089- for (short i=0;i<polygon->neighbor_count;++i)
1090- {
1091- struct polygon_data *neighboring_polygon= get_polygon_data(*neighbor_indexes++);
1092-
1093- if (!POLYGON_IS_DETACHED(neighboring_polygon))
1094- {
1095- short object_index= neighboring_polygon->first_object;
1096-
1097- while (object_index!=NONE)
1098- {
1099- struct object_data *object= get_object_data(object_index);
1100- bool solid_object= false;
1101-
1102- if (!OBJECT_IS_INVISIBLE(object))
1103- {
1104- switch (GET_OBJECT_OWNER(object))
1105- {
1106- case _object_is_monster:
1107- {
1108- struct monster_data *monster= get_monster_data(object->permutation);
1109-
1110- if (!MONSTER_IS_DYING(monster) && !MONSTER_IS_TELEPORTING(monster))
1111- {
1112- solid_object= true;
1113- }
1114-
1115- break;
1116- }
1117-
1118- case _object_is_scenery:
1119- if (include_scenery && OBJECT_IS_SOLID(object)) solid_object= true;
1120- break;
1121- }
1122-
1123- if (solid_object)
1124- {
1125- found_solid_object= true;
1126-
1127- // LP change:
1128- if (IntersectedObjectsPtr && IntersectedObjectsPtr->size()<maximum_object_count) /* do we have enough space to add it? */
1129- {
1130- unsigned j;
1131-
1132- /* only add this object_index if it's not already in the list */
1133- vector<short>& IntersectedObjects = *IntersectedObjectsPtr;
1134- for (j=0; j<IntersectedObjects.size() && IntersectedObjects[j]!=object_index; ++j)
1135- ;
1136- if (j==IntersectedObjects.size())
1137- IntersectedObjects.push_back(object_index);
1138- }
1139- }
1140- }
1141-
1142- object_index= object->next_object;
1143- }
1144- }
1145- }
1146-
1147- return found_solid_object;
1148-}
1149-
1150-/* when a target changes polygons, all monsters locked on it must recalculate their paths.
1151- target is an index into the monster list. */
1152-void monster_moved(
1153- short target_index,
1154- short old_polygon_index)
1155-{
1156- struct monster_data *monster= get_monster_data(target_index);
1157- struct object_data *object= get_object_data(monster->object_index);
1158- short monster_index;
1159-
1160- if (!MONSTER_IS_PLAYER(monster))
1161- {
1162- /* cause lights to light, platforms to trigger, etc.; the player does this differently */
1163- changed_polygon(old_polygon_index, object->polygon, NONE);
1164- }
1165-
1166- for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster_index,++monster)
1167- {
1168- /* look for active monsters locked (or losing lock) on the given target_index */
1169- if (SLOT_IS_USED(monster) && MONSTER_HAS_VALID_TARGET(monster) && monster->target_index==target_index)
1170- {
1171- if (clear_line_of_sight(monster_index, target_index, true))
1172- {
1173- if (monster->mode==_monster_losing_lock) set_monster_mode(monster_index, _monster_locked, monster->target_index);
1174- }
1175- else
1176- {
1177- struct monster_definition *definition= get_monster_definition(monster->type);
1178- short intelligence= definition->intelligence;
1179-
1180- switch (dynamic_world->game_information.difficulty_level)
1181- {
1182- case _wuss_level: intelligence= intelligence>>2; break;
1183- case _easy_level: intelligence= intelligence>>1; break;
1184- case _major_damage_level: intelligence= 2*intelligence; break;
1185- case _total_carnage_level: intelligence= 4*intelligence; break;
1186- }
1187-
1188- /* we canユt see our target: if this is first time, change from _monster_locked
1189- to _monster_losing_lock, if this isnユt the first time and our target has
1190- switched polygons more times out of our sight than we have intelligence points,
1191- go to _lost_lock (which means we wonユt get any more new paths when our target
1192- switches polygons, but we wonユt clear our last one until we reach the end). */
1193- if (monster->mode==_monster_locked) monster->changes_until_lock_lost= 0;
1194- if (monster->mode==_monster_losing_lock) monster->changes_until_lock_lost+= 1;
1195- set_monster_mode(monster_index, (monster->changes_until_lock_lost>=definition->intelligence) ?
1196- _monster_lost_lock : _monster_losing_lock, NONE);
1197- }
1198-
1199- /* if weユre losing lock, donユt recalculate our path (weユre headed towards the targetユs
1200- last-known location) */
1201- if (monster->mode!=_monster_losing_lock) monster_needs_path(monster_index, false);
1202- }
1203- }
1204-}
1205-
1206-/* returns NONE or a monster_index that prevented us from moving */
1207-short legal_player_move(
1208- short monster_index,
1209- world_point3d *new_location,
1210- world_distance *object_floor) /* must be set on entry */
1211-{
1212- struct monster_data *monster= get_monster_data(monster_index);
1213- struct object_data *object= get_object_data(monster->object_index);
1214- world_point3d *old_location= &object->location;
1215- size_t monster_count;
1216- world_distance radius, height;
1217- short obstacle_index= NONE;
1218-
1219- get_monster_dimensions(monster_index, &radius, &height);
1220-
1221- IntersectedObjects.clear();
1222- possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, object->polygon, true);
1223- monster_count = IntersectedObjects.size();
1224- for (size_t i=0;i<monster_count;++i)
1225- {
1226- struct object_data *obstacle= get_object_data(IntersectedObjects[i]);
1227- world_distance obstacle_radius, obstacle_height;
1228-
1229- switch (GET_OBJECT_OWNER(obstacle))
1230- {
1231- case _object_is_monster: get_monster_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1232- case _object_is_scenery: get_scenery_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1233- default:
1234- assert(false);
1235- break;
1236- }
1237-
1238- if (IntersectedObjects[i]!=monster->object_index) /* no self-intersection */
1239- {
1240- world_point3d *obstacle_location= &obstacle->location;
1241-
1242- world_distance separation= radius+obstacle_radius;
1243- int32 separation_squared= separation*separation;
1244-
1245- world_distance new_dx= obstacle_location->x-new_location->x;
1246- world_distance new_dy= obstacle_location->y-new_location->y;
1247- int32 new_distance_squared= new_dx*new_dx+new_dy*new_dy;
1248-
1249- if (new_distance_squared<separation_squared)
1250- {
1251- world_distance old_dx= obstacle_location->x-old_location->x;
1252- world_distance old_dy= obstacle_location->y-old_location->y;
1253- int32 old_distance_squared= old_dx*old_dx+old_dy*old_dy;
1254-
1255- if (old_distance_squared>new_distance_squared)
1256- {
1257- world_distance this_object_floor= obstacle_location->z+obstacle_height;
1258-
1259- /* itユs possible we donユt intersect in z */
1260- if (new_location->z+height<obstacle_location->z) continue;
1261- if (new_location->z>this_object_floor)
1262- {
1263- if (this_object_floor>*object_floor) *object_floor= this_object_floor;
1264- continue;
1265- }
1266-
1267-// dprintf("#%d (%d,%d) hit #%d (%d,%d) moving to (%d,%d)", monster_index, old_location->x, old_location->y, obstacle->permutation, obstacle_location->x, obstacle_location->y, new_location->x, new_location->y);
1268- obstacle_index= IntersectedObjects[i];
1269- break;
1270- }
1271- }
1272- }
1273- }
1274-
1275- return obstacle_index;
1276-}
1277-
1278-/* returns NONE or a monster_index that prevented us from moving */
1279-short legal_monster_move(
1280- short monster_index,
1281- angle facing, /* could be different than object->facing for players and ヤflyingユ (heh heh) monsters */
1282- world_point3d *new_location)
1283-{
1284- struct monster_data *monster= get_monster_data(monster_index);
1285- struct object_data *object= get_object_data(monster->object_index);
1286-// world_point2d *old_location= (world_point2d *) &object->location;
1287- size_t monster_count;
1288- world_distance radius, height;
1289- short obstacle_index= NONE;
1290-
1291- get_monster_dimensions(monster_index, &radius, &height);
1292-
1293- IntersectedObjects.clear();
1294- possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, object->polygon, true);
1295- monster_count= IntersectedObjects.size();
1296- for (size_t i=0;i<monster_count;++i)
1297- {
1298- struct object_data *obstacle= get_object_data(IntersectedObjects[i]);
1299- world_distance obstacle_radius, obstacle_height;
1300-
1301- switch (GET_OBJECT_OWNER(obstacle))
1302- {
1303- case _object_is_monster: get_monster_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1304- case _object_is_scenery: get_scenery_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1305- default:
1306- assert(false);
1307- break;
1308- }
1309-
1310- // LP change:
1311- if (IntersectedObjects[i]!=monster->object_index) /* no self-intersection */
1312- {
1313- world_point3d *obstacle_location= &obstacle->location;
1314-
1315- if (obstacle_location->z<new_location->z+height && obstacle_location->z+obstacle_height>new_location->z)
1316- {
1317- world_distance separation= radius+obstacle_radius;
1318- world_distance dx= obstacle_location->x-new_location->x;
1319- world_distance dy= obstacle_location->y-new_location->y;
1320-
1321- if (GET_OBJECT_OWNER(obstacle)!=_object_is_scenery && obstacle->permutation>monster_index && !MONSTER_IS_PLAYER(get_monster_data(obstacle->permutation))) separation= (separation>>1) + (separation>>2);
1322- if (dx>-separation && dx<separation && dy>-separation && dy<separation)
1323- {
1324- /* we intersect sloppily; get arctan to be sure */
1325- angle theta= NORMALIZE_ANGLE(arctangent(dx, dy)-facing);
1326-
1327- if (theta<EIGHTH_CIRCLE||theta>FULL_CIRCLE-EIGHTH_CIRCLE)
1328- {
1329-// dprintf("#%d (%d,%d) hit #%d (%d,%d) moving to (%d,%d)", monster_index, old_location->x, old_location->y, obstacle->permutation, obstacle_location->x, obstacle_location->y, new_location->x, new_location->y);
1330- obstacle_index= IntersectedObjects[i];
1331- break;
1332- }
1333- }
1334- }
1335- }
1336- }
1337-
1338- return obstacle_index;
1339-}
1340-
1341-void get_monster_dimensions(
1342- short monster_index,
1343- world_distance *radius,
1344- world_distance *height)
1345-{
1346- struct monster_data *monster= get_monster_data(monster_index);
1347- struct monster_definition *definition= get_monster_definition(monster->type);
1348-
1349- *radius= definition->radius;
1350- *height= definition->height;
1351-}
1352-
1353-void damage_monsters_in_radius(
1354- short primary_target_index,
1355- short aggressor_index,
1356- short aggressor_type,
1357- world_point3d *epicenter,
1358- short epicenter_polygon_index,
1359- world_distance radius,
1360- struct damage_definition *damage,
1361- short projectile_index)
1362-{
1363- size_t object_count;
1364-
1365- bool aggressor_is_live_player = false;
1366-
1367- (void) (primary_target_index);
1368-
1369- IntersectedObjects.clear();
1370- possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, epicenter_polygon_index, false);
1371- object_count= IntersectedObjects.size();
1372- struct object_data *aggressor = NULL;
1373- if (film_profile.infinity_tag_fix && aggressor_index != NONE)
1374- {
1375- monster_data* monster = get_monster_data(aggressor_index);
1376- if (MONSTER_IS_PLAYER(monster))
1377- {
1378- player_data* player = get_player_data(monster_index_to_player_index(aggressor_index));
1379-
1380- if (!PLAYER_IS_DEAD(player)) aggressor_is_live_player = true;
1381- }
1382- }
1383-
1384- for (size_t i=0;i<object_count;++i)
1385- {
1386- struct object_data *object= get_object_data(IntersectedObjects[i]);
1387- if (film_profile.damage_aggressor_last_in_tag &&
1388- GET_GAME_TYPE() == _game_of_tag &&
1389- object->permutation == aggressor_index) {
1390- // damage the aggressor last, so tag suicides are handled correctly
1391- aggressor = object;
1392- } else {
1393- world_distance distance= distance2d((world_point2d*)epicenter, (world_point2d*)&object->location);
1394- world_distance monster_radius, monster_height;
1395-
1396- get_monster_dimensions(object->permutation, &monster_radius, &monster_height);
1397-
1398- /* make sure we intersect the monsterユs radius in the x,y-plane and that we intersect
1399- his cylinder in z */
1400- if (distance<radius+monster_radius)
1401- {
1402- if (epicenter->z+radius-distance>object->location.z && epicenter->z-radius+distance<object->location.z+monster_height)
1403- {
1404- if (!line_is_obstructed(epicenter_polygon_index, (world_point2d*)epicenter, object->polygon, (world_point2d*)&object->location))
1405- {
1406- damage_monster(object->permutation, aggressor_index, aggressor_type, epicenter, damage, projectile_index);
1407- }
1408- }
1409- }
1410- }
1411- }
1412-
1413- // damage the aggressor
1414- if (film_profile.damage_aggressor_last_in_tag && aggressor != NULL)
1415- {
1416- world_distance distance= distance2d((world_point2d*)epicenter, (world_point2d*)&aggressor->location);
1417- world_distance monster_radius, monster_height;
1418-
1419- get_monster_dimensions(aggressor->permutation, &monster_radius, &monster_height);
1420- if (distance<radius+monster_radius)
1421- {
1422- if (epicenter->z+radius-distance>aggressor->location.z && epicenter->z-radius+distance<aggressor->location.z+monster_height)
1423- {
1424- if (!line_is_obstructed(epicenter_polygon_index, (world_point2d*)epicenter, aggressor->polygon, (world_point2d*)&aggressor->location))
1425- {
1426- damage_monster(aggressor->permutation, aggressor_index, aggressor_type, epicenter, damage, projectile_index);
1427- }
1428- }
1429- }
1430- }
1431-
1432- // or, just make him it
1433- if (GET_GAME_TYPE() == _game_of_tag && aggressor_is_live_player)
1434- {
1435- monster_data* monster = get_monster_data(aggressor_index);
1436- if (MONSTER_IS_PLAYER(monster))
1437- {
1438- short player_index = monster_index_to_player_index(aggressor_index);
1439- player_data* player = get_player_data(player_index);
1440-
1441- // he blew himself up, so make sure he's it
1442- if (PLAYER_IS_DEAD(player))
1443- {
1444- dynamic_world->game_player_index = player_index;
1445- }
1446- }
1447- }
1448-}
1449-
1450-void damage_monster(
1451- short target_index,
1452- short aggressor_index,
1453- short aggressor_type,
1454- world_point3d *epicenter,
1455- struct damage_definition *damage,
1456- short projectile_index)
1457-{
1458- struct monster_data *monster= get_monster_data(target_index);
1459- struct monster_definition *definition= get_monster_definition(monster->type);
1460- struct monster_data *aggressor_monster= aggressor_index!=NONE ? get_monster_data(aggressor_index) : (struct monster_data *) NULL;
1461-// dprintf("%d base, %d random, %d scale.\n", damage->base, damage->random, damage->scale);
1462- short delta_vitality= calculate_damage(damage);
1463-// dprintf("we are doing %d damage\n", delta_vitality);
1464- world_distance external_velocity= 0;
1465- bool vertical_component= false;
1466-
1467- if (!(definition->immunities&FLAG(damage->type)))
1468- {
1469- // double damage for weaknesses
1470- if (definition->weaknesses&FLAG(damage->type)) delta_vitality<<= 1;
1471-
1472- // if this player was shot by a friendly, make him apologise
1473- if (aggressor_index!=NONE && get_monster_attitude(aggressor_index, target_index)==_friendly)
1474- {
1475- play_object_sound(aggressor_monster->object_index, get_monster_definition(aggressor_monster->type)->apology_sound);
1476- }
1477-
1478- if (MONSTER_IS_PLAYER(monster))
1479- {
1480- damage_player(target_index, aggressor_index, aggressor_type, damage, projectile_index);
1481- }
1482- else
1483- {
1484- struct player_data *aggressor_player= (struct player_data *) NULL;
1485-
1486- /* only active monsters can take damage */
1487- if (!MONSTER_IS_ACTIVE(monster)) activate_monster(target_index);
1488-
1489- /* convert aggressor monster index to a player index, if possible, to record damage */
1490- if (aggressor_index!=NONE)
1491- {
1492- if (MONSTER_IS_PLAYER(aggressor_monster))
1493- {
1494- aggressor_player= get_player_data(monster_index_to_player_index(aggressor_index));
1495- aggressor_player->monster_damage_given.damage+= MAX(monster->vitality, delta_vitality);
1496- team_monster_damage_given[aggressor_player->team].damage += MAX(monster->vitality, delta_vitality);
1497- }
1498- }
1499-
1500- // LP change: pegging to maximum value
1501- monster->vitality = MIN(int32(monster->vitality) - int32(delta_vitality), int32(INT16_MAX));
1502- L_Call_Monster_Damaged(target_index, aggressor_index, damage->type, delta_vitality, projectile_index);
1503-
1504- if (monster->vitality > 0)
1505- {
1506- set_monster_action(target_index, _monster_is_being_hit);
1507- if ((definition->flags&_monster_is_berserker) && monster->vitality<(definition->vitality>>2))
1508- SET_MONSTER_BERSERK_STATUS(monster, true);
1509- if (aggressor_index!=NONE) switch_target_check(target_index, aggressor_index, delta_vitality);
1510-
1511- // if a player shoots a monster who thinks the player is friendly; ask him what the fuck is up
1512- if (aggressor_player && get_monster_attitude(target_index, aggressor_index)==_friendly) play_object_sound(monster->object_index, definition->friendly_fire_sound);
1513- }
1514- else
1515- {
1516- if (!MONSTER_IS_DYING(monster))
1517- {
1518- short action;
1519-
1520- if ((damage->type==_damage_flame||damage->type==_damage_lava||damage->type==_damage_alien_projectile) && (definition->flags&_monster_can_die_in_flames))
1521- {
1522- action= _monster_is_dying_flaming;
1523- }
1524- else
1525- {
1526- if ((damage->type==_damage_explosion || damage->type==_damage_crushing || (FLAG(damage->type)&definition->weaknesses) ||
1527- definition->soft_dying_shape==UNONE) && definition->hard_dying_shape!=UNONE && !(definition->flags&_monster_has_delayed_hard_death))
1528- {
1529- action= _monster_is_dying_hard;
1530- }
1531- else
1532- {
1533- action= _monster_is_dying_soft;
1534- }
1535- if (definition->flags&_monster_has_delayed_hard_death) monster->vertical_velocity= -1;
1536- }
1537-
1538- if (action==_monster_is_dying_flaming) play_object_sound(monster->object_index, definition->flaming_sound);
1539- set_monster_action(target_index, action);
1540- monster_died(target_index); /* orphan projectile, recalculate aggressor paths */
1541-
1542- if (aggressor_player)
1543- {
1544- aggressor_player->monster_damage_given.kills+= 1;
1545- team_monster_damage_given[aggressor_player->team].kills += 1;
1546-
1547- if (definition->_class&_class_human_civilian) dynamic_world->civilians_killed_by_players+= 1;
1548- }
1549- }
1550-
1551- // Lua script hook
1552- int aggressor_player_index = -1;
1553- if (aggressor_index!=NONE)
1554- if (MONSTER_IS_PLAYER(aggressor_monster))
1555- aggressor_player_index = monster_index_to_player_index(aggressor_index);
1556- L_Call_Monster_Killed (target_index, aggressor_player_index, projectile_index);
1557- }
1558- }
1559-
1560-
1561- if (damage->type >= 0 && damage->type < NUMBER_OF_DAMAGE_TYPES)
1562- {
1563- damage_kick_definition& kick_def = damage_kick_definitions[damage->type];
1564-
1565- external_velocity = (world_distance)(kick_def.base_value + kick_def.delta_vitality_multiplier*delta_vitality);
1566- vertical_component = kick_def.is_also_vertical;
1567- if (film_profile.use_vertical_kick_threshold
1568- && kick_def.vertical_threshold
1569- && delta_vitality > kick_def.vertical_threshold)
1570- {
1571- vertical_component = true;
1572- }
1573- }
1574-
1575-/*
1576- switch (damage->type)
1577- {
1578- case _damage_teleporter:
1579- external_velocity= 250;
1580- break;
1581-
1582- case _damage_fusion_bolt:
1583- if (delta_vitality>100) vertical_component= true;
1584- break;
1585-
1586- case _damage_electrical_staff:
1587- case _damage_yeti_claws:
1588- case _damage_compiler_bolt:
1589- vertical_component= true;
1590- external_velocity= 3*delta_vitality;
1591- break;
1592-
1593- case _damage_shotgun_projectile:
1594- vertical_component= true;
1595- break;
1596-
1597- case _damage_explosion:
1598- vertical_component= true;
1599- external_velocity= delta_vitality;
1600- break;
1601-
1602- default:
1603- external_velocity= delta_vitality;
1604- break;
1605- }
1606-*/
1607-
1608- if (MONSTER_IS_DYING(monster) && external_velocity<MINIMUM_DYING_EXTERNAL_VELOCITY) external_velocity= MINIMUM_DYING_EXTERNAL_VELOCITY;
1609- external_velocity= (external_velocity*definition->external_velocity_scale)>>FIXED_FRACTIONAL_BITS;
1610- if (external_velocity && epicenter)
1611- {
1612- struct object_data *object= get_object_data(monster->object_index);
1613- world_distance dx= object->location.x - epicenter->x;
1614- world_distance dy= object->location.y - epicenter->y;
1615- world_distance dz= object->location.z + (definition->height>>1) - epicenter->z;
1616- angle direction= arctangent(dx, dy);
1617- world_distance radius= isqrt(dx*dx+dy*dy+dz*dz);
1618- world_distance vertical_velocity= (vertical_component&&radius) ? ((external_velocity*dz)/radius) : 0;
1619-
1620- accelerate_monster(target_index, vertical_velocity, direction, external_velocity);
1621- }
1622- }
1623-}
1624-
1625-bool bump_monster(
1626- short aggressor_index,
1627- short monster_index)
1628-{
1629-#if 0
1630-#ifdef DEBUG
1631-#ifdef env68k
1632- if (MONSTER_IS_PLAYER(get_monster_data(aggressor_index)))
1633- {
1634- dprintf("bumped monster @%p;dm #%d #%d;", get_monster_data(monster_index),
1635- get_monster_data(monster_index), sizeof(struct monster_data));
1636- }
1637-#endif
1638-#endif
1639-#endif
1640-
1641- return switch_target_check(monster_index, aggressor_index, 0);
1642-}
1643-
1644-
1645-bool legal_polygon_height_change(
1646- short polygon_index,
1647- world_distance new_floor_height,
1648- world_distance new_ceiling_height,
1649- struct damage_definition *damage)
1650-{
1651- world_distance new_polygon_height= new_ceiling_height-new_floor_height;
1652- struct polygon_data *polygon= get_polygon_data(polygon_index);
1653- short object_index= polygon->first_object;
1654- world_distance minimum_height= dead_player_minimum_polygon_height(polygon_index);
1655- bool legal_change= true;
1656-
1657- while (object_index!=NONE)
1658- {
1659- struct object_data *object= get_object_data(object_index);
1660-
1661- if (GET_OBJECT_OWNER(object)==_object_is_monster && OBJECT_IS_VISIBLE(object))
1662- {
1663- world_distance radius, height;
1664-
1665- get_monster_dimensions(object->permutation, &radius, &height);
1666- if (height>=new_polygon_height)
1667- {
1668- if (damage)
1669- {
1670- damage_monster(object->permutation, NONE, NONE, (world_point3d *) NULL, damage, NONE);
1671- play_object_sound(object_index, Sound_Crunched());
1672- }
1673- legal_change= false;
1674- }
1675- }
1676-
1677- object_index= object->next_object;
1678- }
1679-
1680- return new_polygon_height<minimum_height ? false : legal_change;
1681-}
1682-
1683-/* weユve already checked and this monster is not obstructing the polygon from changing heights */
1684-void adjust_monster_for_polygon_height_change(
1685- short monster_index,
1686- short polygon_index,
1687- world_distance new_floor_height,
1688- world_distance new_ceiling_height)
1689-{
1690- struct polygon_data *polygon= get_polygon_data(polygon_index);
1691- struct monster_data *monster= get_monster_data(monster_index);
1692- world_distance radius, height;
1693-
1694- get_monster_dimensions(monster_index, &radius, &height);
1695-
1696- if (MONSTER_IS_PLAYER(monster))
1697- {
1698- adjust_player_for_polygon_height_change(monster_index, polygon_index, new_floor_height, new_ceiling_height);
1699- }
1700- else
1701- {
1702- struct object_data *object= get_object_data(monster->object_index);
1703-
1704- if (object->location.z==polygon->floor_height) object->location.z= new_floor_height;
1705- }
1706-}
1707-
1708-void accelerate_monster(
1709- short monster_index,
1710- world_distance vertical_velocity,
1711- angle direction,
1712- world_distance velocity)
1713-{
1714- struct monster_data *monster= get_monster_data(monster_index);
1715-
1716- if (MONSTER_IS_PLAYER(monster))
1717- {
1718- accelerate_player(monster_index, vertical_velocity, direction, velocity);
1719- }
1720- else
1721- {
1722- struct object_data *object= get_object_data(monster->object_index);
1723-
1724- object->facing= NORMALIZE_ANGLE(direction+HALF_CIRCLE);
1725- monster->external_velocity+= velocity;
1726- monster->vertical_velocity+= PIN(monster->vertical_velocity+vertical_velocity, -TERMINAL_VERTICAL_MONSTER_VELOCITY, TERMINAL_VERTICAL_MONSTER_VELOCITY);
1727- }
1728-}
1729-
1730-short get_monster_impact_effect(
1731- short monster_index)
1732-{
1733- struct monster_data *monster= get_monster_data(monster_index);
1734- struct monster_definition *definition= get_monster_definition(monster->type);
1735- short impact_effect_index= definition->impact_effect;
1736-
1737- if (MONSTER_IS_PLAYER(monster))
1738- {
1739- struct object_data *object= get_object_data(monster->object_index);
1740-
1741- switch (object->transfer_mode)
1742- {
1743- case _xfer_static:
1744- impact_effect_index= NONE;
1745- break;
1746- }
1747- }
1748-
1749- return impact_effect_index;
1750-}
1751-
1752-short get_monster_melee_impact_effect(
1753- short monster_index)
1754-{
1755- struct monster_data *monster= get_monster_data(monster_index);
1756- struct monster_definition *definition= get_monster_definition(monster->type);
1757-
1758- return definition->melee_impact_effect;
1759-}
1760-
1761-#if 0
1762-/* pick a random player; flood out from that player until we find a polygon legal for a monster
1763- drop whose center is not player visible. */
1764-void pick_nearby_random_monster_position(
1765- world_point2d *p,
1766- short *polygon_index)
1767-{
1768- short player_index= global_random()%dynamic_world->player_count;
1769- short flood_polygon_index= get_player_data(player_index)->camera_polygon_index;
1770-
1771- polygon_index= flood_map(polygon_index, area, monster_activation_flood_proc, _breadth_first, &flood_flags);
1772- while (polygon_index!=NONE)
1773- {
1774- short object_index;
1775- struct object_data *object;
1776- struct polygon_data *polygon= get_polygon_data(polygon_index);
1777-
1778- polygon_index= flood_map(NONE, area, monster_activation_flood_proc, _breadth_first, &flood_flags);
1779- }
1780-}
1781-#endif
1782-
1783-/* ---------- private code */
1784-
1785-static void cause_shrapnel_damage(
1786- short monster_index)
1787-{
1788- struct monster_data *monster= get_monster_data(monster_index);
1789- struct object_data *object= get_object_data(monster->object_index);
1790- struct monster_definition *definition= get_monster_definition(monster->type);
1791-
1792- if (definition->shrapnel_radius!=NONE)
1793- {
1794- damage_monsters_in_radius(NONE, NONE, NONE, &object->location, object->polygon,
1795- definition->shrapnel_radius, &definition->shrapnel_damage, NONE);
1796- }
1797-}
1798-
1799-static void update_monster_vertical_physics_model(
1800- short monster_index)
1801-{
1802- struct monster_data *monster= get_monster_data(monster_index);
1803- struct monster_definition *definition= get_monster_definition(monster->type);
1804- struct object_data *object= get_object_data(monster->object_index);
1805- struct polygon_data *polygon= get_polygon_data(object->polygon);
1806- struct media_data *media= polygon->media_index==NONE ? (struct media_data *) NULL : get_media_data(polygon->media_index);
1807- uint32 moving_flags= MONSTER_IS_DYING(monster) ? 0 : (definition->flags&(_monster_flys|_monster_floats));
1808- world_distance gravity= (static_world->environment_flags&_environment_low_gravity) ? (definition->gravity>>1) : definition->gravity;
1809- world_distance floor_height= polygon->floor_height;
1810- world_distance desired_height;
1811- world_distance old_height= object->location.z;
1812- bool above_ground, below_ground;
1813-
1814- if (media)
1815- {
1816- // flying and floating monsters treat media as the floor
1817- if (moving_flags && media->height>floor_height) floor_height= media->height + WORLD_ONE/16;
1818-
1819- // take damage if necessary
1820- if (media->height>object->location.z)
1821- {
1822- struct damage_definition *damage= get_media_damage(polygon->media_index, FIXED_ONE);
1823-
1824- if (damage) damage_monster(monster_index, NONE, NONE, (world_point3d *) NULL, damage, NONE);
1825- }
1826- }
1827- desired_height= (monster->desired_height==NONE||MONSTER_IS_DYING(monster)) ? polygon->floor_height : monster->desired_height;
1828- above_ground= object->location.z>desired_height;
1829- below_ground= object->location.z<desired_height;
1830-
1831- switch (moving_flags)
1832- {
1833- case 0:
1834- /* if weユre above the floor, adjust vertical velocity */
1835- if (above_ground) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1836- if (below_ground) monster->vertical_velocity= 0, object->location.z= desired_height;
1837- break;
1838-
1839- case _monster_flys:
1840- if (above_ground && !MONSTER_IS_ATTACKING(monster)) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1841- if (below_ground) monster->vertical_velocity= CEILING(monster->vertical_velocity+gravity, definition->terminal_velocity);
1842- break;
1843-
1844- case _monster_floats:
1845- if (above_ground && !MONSTER_IS_ATTACKING(monster)) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1846- if (below_ground) monster->vertical_velocity= CEILING(monster->vertical_velocity+gravity, definition->terminal_velocity);
1847- break;
1848-
1849- default:
1850- /* canユt fly and float, beavis */
1851- // LP change: this stuff put in to handle the map "Aqualung" correctly
1852- if (above_ground && !MONSTER_IS_ATTACKING(monster)) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1853- if (below_ground) monster->vertical_velocity= CEILING(monster->vertical_velocity+gravity, definition->terminal_velocity);
1854- break;
1855- }
1856-
1857- /* add our vertical velocity to z */
1858- object->location.z= PIN(object->location.z+monster->vertical_velocity, polygon->floor_height, polygon->ceiling_height-definition->height);
1859-
1860- /* if weユre under the floor moving down, put us on the floor and clear our velocity;
1861- if weユre above the floor moving up, put us on the floor and clear our velocity if we were previously below ground */
1862- switch (moving_flags)
1863- {
1864- case 0:
1865- case _monster_floats:
1866- if (object->location.z<=desired_height && monster->vertical_velocity<0) monster->vertical_velocity= 0, object->location.z= desired_height;
1867- if (object->location.z>=desired_height && monster->vertical_velocity>0 && below_ground) monster->vertical_velocity= 0, object->location.z= desired_height;
1868- break;
1869-
1870- case _monster_flys:
1871- default: // LP: added this case to handle "Aqualung" correctly
1872- if (object->location.z<=desired_height && above_ground) monster->vertical_velocity>>= 1, object->location.z= desired_height;
1873- if (object->location.z>=desired_height && below_ground) monster->vertical_velocity>>= 1, object->location.z= desired_height;
1874- break;
1875- }
1876-
1877- /* reset desired height (flying and floating monsters often change this later) */
1878- if (moving_flags&_monster_flys)
1879- {
1880- /* weユre flying!: if we have no target, take the middle ground; if we have a target aim
1881- for his midsection */
1882- if (MONSTER_HAS_VALID_TARGET(monster))
1883- {
1884- struct monster_data *target= get_monster_data(monster->target_index);
1885- struct monster_definition *target_definition= get_monster_definition(target->type);
1886-
1887- monster->desired_height= get_object_data(target->object_index)->location.z + ((target_definition->height-definition->height)>>1) + definition->preferred_hover_height;
1888- monster->desired_height= PIN(monster->desired_height, floor_height+(definition->height>>2), polygon->ceiling_height-definition->height);
1889- }
1890- else
1891- {
1892- if (monster->random_desired_height<floor_height || monster->random_desired_height>polygon->ceiling_height)
1893- {
1894- world_distance delta= polygon->ceiling_height-floor_height-definition->height;
1895-
1896- monster->random_desired_height= floor_height + ((delta>0) ? (global_random()%delta) : 0);
1897- }
1898-
1899- monster->desired_height= MONSTER_IS_DYING(monster) ? polygon->floor_height : monster->random_desired_height;
1900- }
1901- }
1902- else
1903- {
1904- monster->desired_height= floor_height;
1905- }
1906-
1907- monster->sound_location= object->location;
1908- monster->sound_polygon_index= object->polygon;
1909- monster->sound_location.z+= definition->height - (definition->height>>1);
1910-
1911- if (media)
1912- {
1913- world_point3d location= object->location;
1914- short media_effect_type= NONE;
1915-
1916- location.z= media->height;
1917- if (old_height>=media->height && object->location.z<media->height)
1918- {
1919- media_effect_type= _large_media_detonation_effect;
1920- }
1921- if (old_height<media->height && object->location.z>=media->height)
1922- {
1923- media_effect_type= _large_media_emergence_effect;
1924- }
1925-
1926- if (media_effect_type!=NONE)
1927- {
1928- short effect_type= NONE;
1929-
1930- get_media_detonation_effect(polygon->media_index, media_effect_type, &effect_type);
1931- new_effect(&location, object->polygon, effect_type, object->facing);
1932- }
1933- }
1934-}
1935-
1936-static void update_monster_physics_model(
1937- short monster_index)
1938-{
1939- struct monster_data *monster= get_monster_data(monster_index);
1940- struct monster_definition *definition= get_monster_definition(monster->type);
1941- struct object_data *object= get_object_data(monster->object_index);
1942-
1943- if (monster->external_velocity)
1944- {
1945- world_point3d new_location= object->location;
1946- world_distance adjusted_floor_height, adjusted_ceiling_height;
1947- angle negative_facing= NORMALIZE_ANGLE(HALF_CIRCLE+object->facing);
1948- struct polygon_data *polygon;
1949- short supporting_polygon_index;
1950-
1951- /* move the monster */
1952- translate_point2d((world_point2d*)&new_location, monster->external_velocity, negative_facing);
1953- keep_line_segment_out_of_walls(object->polygon, &object->location, &new_location,
1954- 0, definition->height, &adjusted_floor_height, &adjusted_ceiling_height, &supporting_polygon_index);
1955- if (legal_monster_move(monster_index, negative_facing, &new_location)==NONE)
1956- {
1957- short old_polygon_index= object->polygon;
1958-
1959- if (translate_map_object(monster->object_index, &new_location, NONE)) monster_moved(monster_index, old_polygon_index);
1960- }
1961-
1962- /* slow him down if heユs touching the ground or flying */
1963- polygon= get_polygon_data(object->polygon);
1964- if (object->location.z<=polygon->floor_height || (definition->flags&(_monster_flys|_monster_floats)))
1965- {
1966- if ((monster->external_velocity-= MONSTER_EXTERNAL_DECELERATION)<MONSTER_MINIMUM_EXTERNAL_VELOCITY)
1967- {
1968- monster->external_velocity= 0;
1969- }
1970- }
1971- }
1972-}
1973-
1974-static void monster_needs_path(
1975- short monster_index,
1976- bool immediately)
1977-{
1978- struct monster_data *monster= get_monster_data(monster_index);
1979-
1980- if (monster->path!=NONE && immediately) delete_path(monster->path), monster->path= NONE;
1981- if (monster->action==_monster_is_moving && immediately) set_monster_action(monster_index, _monster_is_stationary);
1982- SET_MONSTER_NEEDS_PATH_STATUS(monster, true);
1983-}
1984-
1985-void set_monster_mode(
1986- short monster_index,
1987- short new_mode,
1988- short target_index)
1989-{
1990- struct monster_data *monster= get_monster_data(monster_index);
1991-
1992- /* if we were locked on a monster in our own polygon and we lost him then we donユt have a path
1993- and going anywhere would be dangerous so we need to ask for a new path */
1994- if (monster->mode==_monster_locked&&new_mode!=_monster_locked&&monster->path==NONE) monster_needs_path(monster_index, false);
1995-
1996- switch (new_mode)
1997- {
1998- case _monster_locked:
1999- (void)get_monster_data(target_index); /* for bounds checking only */
2000- monster->target_index= target_index;
2001- CLEAR_TARGET_DAMAGE_FLAG(monster);
2002-// if (target_index==local_player->monster_index)
2003-// dprintf("monster #%d is locked on new target #%d;g;", monster_index, target_index);
2004-// switch (monster->type)
2005-// {
2006-// case _civilian_crew: case _civilian_engineering: case _civilian_science: case _civilian_security:
2007-// dprintf("monster #%d is locked on new target #%d;g;", monster_index, target_index);
2008-// }
2009- break;
2010-
2011- case _monster_losing_lock: /* target_index ignored, but still valid */
2012- case _monster_lost_lock:
2013- (void)get_monster_data(monster->target_index); /* for bounds checking only */
2014- break;
2015-
2016- case _monster_unlocked:
2017- monster->target_index= NONE;
2018- break;
2019-
2020- default:
2021- assert(false);
2022- break;
2023- }
2024-
2025- monster->mode= new_mode;
2026-}
2027-
2028-/* this function decides what the given monster actually wants to do, and then generates a path
2029- to get him there; if a monster who has lost lock calls this function, he will be forced to
2030- wander randomly or follow a guard path. */
2031-static void generate_new_path_for_monster(
2032- short monster_index)
2033-{
2034- struct monster_data *monster= get_monster_data(monster_index);
2035- struct object_data *object= get_object_data(monster->object_index);
2036- struct monster_definition *definition= get_monster_definition(monster->type);
2037- struct monster_pathfinding_data data;
2038- short destination_polygon_index;
2039- world_point2d *destination;
2040- world_vector2d bias;
2041-
2042- /* delete this monsterユs old path, if one exists, and clear the need path flag */
2043- if (monster->path!=NONE) delete_path(monster->path), monster->path= NONE;
2044- SET_MONSTER_NEEDS_PATH_STATUS(monster, false);
2045-
2046- switch (monster->mode)
2047- {
2048- case _monster_losing_lock:
2049- /* our target is out of sight, but weユre still zen-ing his position until we run out
2050- of intelligence points */
2051- case _monster_locked:
2052- {
2053- struct monster_data *target= get_monster_data(monster->target_index);
2054- struct object_data *target_object= get_object_data(target->object_index);
2055-
2056- if (definition->random_sound_mask && !(global_random()&definition->random_sound_mask)) play_object_sound(monster->object_index, definition->random_sound);
2057-
2058- /* if we canユt attack, run away, otherwise go for the target */
2059- if (definition->flags&_monster_cannot_attack)
2060- {
2061- // LP changed: unnecessary to interrupt for this
2062- // dprintf("%p", monster);
2063- destination= (world_point2d *) &bias;
2064- bias.i= object->location.x - target_object->location.x;
2065- bias.j= object->location.y - target_object->location.y;
2066- destination_polygon_index= NONE;
2067- }
2068- else
2069- {
2070- /* if we still have lock, just build a new path and keep charging */
2071- destination= (world_point2d *) &target_object->location;
2072- destination_polygon_index= MONSTER_IS_PLAYER(target) ?
2073- get_polygon_index_supporting_player(monster->target_index) :
2074- target_object->polygon;
2075- }
2076- break;
2077- }
2078-
2079- case _monster_lost_lock:
2080- /* if we lost lock during this path and we went as far as we could go, unlock */
2081- set_monster_mode(monster_index, _monster_unlocked, NONE);
2082-// dprintf("monster #%d lost lock and reached end of path;g;", monster_index);
2083- case _monster_unlocked:
2084- /* if weユre unlocked and need a new path, follow our guard path if we have one and
2085- run around randomly if we donユt */
2086- if ((destination_polygon_index= monster->goal_polygon_index)!=NONE)
2087- {
2088- destination= &get_polygon_data(destination_polygon_index)->center;
2089- }
2090- else
2091- {
2092- destination= (world_point2d *) NULL;
2093- }
2094- break;
2095-
2096- default:
2097- assert(false);
2098- break;
2099- }
2100-
2101-// dprintf("#%d: generating new %spath for monster #%d;g;", dynamic_world->tick_count, destination?"":"random ", monster_index);
2102-
2103- data.definition= definition;
2104- data.monster= monster;
2105- data.cross_zone_boundaries= destination_polygon_index==NONE ? false : true;
2106-
2107- monster->path= new_path((world_point2d *)&object->location, object->polygon, destination,
2108- destination_polygon_index, 3*definition->radius, monster_pathfinding_cost_function, &data);
2109- if (monster->path==NONE)
2110- {
2111- if (monster->action!=_monster_is_being_hit || MONSTER_IS_DYING(monster)) set_monster_action(monster_index, _monster_is_stationary);
2112- set_monster_mode(monster_index, _monster_unlocked, NONE);
2113- }
2114- else
2115- {
2116- advance_monster_path(monster_index);
2117- }
2118-}
2119-
2120-
2121-/* somebody just did damage to us; see if we should start attacking them or not. berserk
2122- monsters always switch targets. this is where we check to see if we go berserk, right?
2123- monster->vitality has already been changed (a monster who just bumped into another monster
2124- also calls this, with a delta_vitality of zero). returns true if an attack was started. */
2125-static bool switch_target_check(
2126- short monster_index,
2127- short attacker_index,
2128- short delta_vitality)
2129-{
2130- struct monster_data *monster= get_monster_data(monster_index);
2131- bool switched_target= false;
2132-
2133- if (!MONSTER_IS_PLAYER(monster) && !MONSTER_IS_DYING(monster)) /* donユt mess with players or dying monsters */
2134- {
2135- if (MONSTER_HAS_VALID_TARGET(monster) && monster->target_index==attacker_index)
2136- {
2137- /* if we didnユt know where our target was and he just shot us, we sort of like, know
2138- where he is now */
2139- if (monster->mode==_monster_losing_lock)
2140- {
2141- set_monster_mode(monster_index, _monster_locked, attacker_index);
2142- monster_needs_path(monster_index, false);
2143- }
2144-
2145- /* if weユre already after this guy and he just did damage to us, remember that */
2146- if (delta_vitality)
2147- SET_TARGET_DAMAGE_FLAG(monster);
2148-
2149- switched_target= true;
2150- }
2151- else
2152- {
2153- struct monster_definition *definition= get_monster_definition(monster->type);
2154- short target_index= MONSTER_HAS_VALID_TARGET(monster) ? monster->target_index : NONE;
2155- struct monster_data *attacker= get_monster_data(attacker_index);
2156- short attacker_attitude, target_attitude;
2157-
2158- CLEAR_TARGET_DAMAGE_FLAG(monster);
2159-
2160- if (!MONSTER_IS_DYING(attacker) && !(definition->flags&_monster_cannot_attack))
2161- {
2162- /* if our attacker is an enemy (or a neutral doing non-zero damage or we are berserk) and
2163- a) weユre inactive, or,
2164- b) idle, or,
2165- c) unlocked, or,
2166- d) our current target has not done any damage, or,
2167-// e) attacker is an enemy and our current target is neutral or friendly, or,
2168-// f) attacker is a neutral and our current target is friendly, or,
2169- g) we canユt attack and somebody just did damage to us
2170- then go kick his ass. */
2171- attacker_attitude= get_monster_attitude(monster_index, attacker_index);
2172- if (target_index!=NONE) target_attitude= get_monster_attitude(monster_index, target_index);
2173- if (TYPE_IS_ENEMY(definition, attacker->type) ||
2174- (TYPE_IS_NEUTRAL(definition, attacker->type)&&delta_vitality) ||
2175- MONSTER_IS_BERSERK(monster))
2176- {
2177- if (!MONSTER_IS_ACTIVE(monster) ||
2178- MONSTER_IS_IDLE(monster) ||
2179- monster->mode!=_monster_locked ||
2180- !TARGET_HAS_DONE_DAMAGE(monster))
2181-// (attacker_attitude==_hostile&&target_attitude!=_hostile) ||
2182-// (attacker_attitude==_neutral&&target_attitude==_friendly) ||
2183-// (delta_vitality&&(definition->flags&_monster_cannot_attack)))
2184- {
2185- change_monster_target(monster_index, attacker_index);
2186- if (delta_vitality) SET_TARGET_DAMAGE_FLAG(monster);
2187- switched_target= true;
2188- }
2189- }
2190- }
2191- }
2192- }
2193-
2194- return switched_target;
2195-}
2196-
2197-static short get_monster_attitude(
2198- short monster_index,
2199- short target_index)
2200-{
2201- struct monster_data *monster= get_monster_data(monster_index);
2202- struct monster_definition *definition= get_monster_definition(monster->type);
2203- struct monster_data *target= get_monster_data(target_index);
2204- short target_type= target->type;
2205- short attitude;
2206-
2207- /* berserk monsters are hostile toward everything */
2208- if (TYPE_IS_ENEMY(definition, target_type) || MONSTER_IS_BERSERK(monster) ||
2209- (MONSTER_HAS_VALID_TARGET(monster) && monster->target_index==target_index) ||
2210- ((definition->_class&_class_human_civilian) && MONSTER_IS_PLAYER(target) && dynamic_world->civilians_killed_by_players>=CIVILIANS_KILLED_BY_PLAYER_THRESHHOLD))
2211- {
2212- attitude= _hostile;
2213- }
2214- else
2215- {
2216- attitude= (TYPE_IS_FRIEND(definition, target_type)) ? _friendly : _neutral;
2217- }
2218-
2219-// if ((definition->_class&_class_human_civilian) && MONSTER_IS_PLAYER(target))
2220-// {
2221-// dprintf("#%d vs. #%d ==> #%d", monster_index, target_index, attitude);
2222-// }
2223-
2224- return attitude;
2225-}
2226-
2227-/* find_closest_appropriate_target() tries to do just that. it is a little broken in that it
2228- treats all monsters in a given polygon as if they were the same distance away, which could
2229- result in strange behavior. the assumption is that if there is a more accessable hostile monster
2230- nearby, that monster will attack and thus end a possible wild goose chase. if there is a
2231- closer hostile target which the aggressor subsequently attempts to move through, he will
2232- change lock and attack the obstruction instead, which will help minimize weirdness.
2233- full_circle is passed directly to clear_line_of_sight(). */
2234-short find_closest_appropriate_target(
2235- short aggressor_index,
2236- bool full_circle)
2237-{
2238- struct monster_data *aggressor= get_monster_data(aggressor_index);
2239- struct monster_definition *definition= get_monster_definition(aggressor->type);
2240- short closest_hostile_target_index= NONE;
2241-
2242- if (MONSTER_IS_ACTIVE(aggressor))
2243- {
2244- int32 flood_flags= _pass_one_zone_border;
2245- short polygon_index= get_object_data(get_monster_data(aggressor_index)->object_index)->polygon;
2246-
2247- /* flood out from the aggressor monsterユs polygon, searching through the object lists of all
2248- polygons we encounter */
2249- polygon_index= flood_map(polygon_index, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
2250- while (polygon_index!=NONE && closest_hostile_target_index==NONE)
2251- {
2252- short object_index;
2253- struct object_data *object;
2254-
2255- /* loop through all objects in this polygon looking for hostile monsters we can see */
2256- for (object_index= get_polygon_data(polygon_index)->first_object; object_index!=NONE; object_index= object->next_object)
2257- {
2258- object= get_object_data(object_index);
2259- if (GET_OBJECT_OWNER(object)==_object_is_monster && OBJECT_IS_VISIBLE(object))
2260- {
2261- short target_monster_index= object->permutation;
2262- struct monster_data *target_monster= get_monster_data(target_monster_index);
2263-
2264- if (!MONSTER_IS_DYING(target_monster) && target_monster_index!=aggressor_index)
2265- {
2266- if (get_monster_attitude(aggressor_index, target_monster_index)==_hostile)
2267- {
2268- if (((definition->flags&_monster_is_omniscent) || clear_line_of_sight(aggressor_index, target_monster_index, full_circle)) &&
2269- (MONSTER_IS_ACTIVE(target_monster) || MONSTER_IS_PLAYER(target_monster) || (static_world->environment_flags&_environment_rebellion)))
2270- {
2271- /* found hostile, live, visible monster */
2272- closest_hostile_target_index= target_monster_index;
2273- break;
2274- }
2275- }
2276- }
2277- }
2278- }
2279-
2280- polygon_index= flood_map(NONE, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
2281- }
2282- }
2283- else
2284- {
2285- short player_index;
2286-
2287- /* if this monster is deactivated, only seeing a player will activate him */
2288-
2289- for (player_index= 0; player_index<dynamic_world->player_count; ++player_index)
2290- {
2291- struct player_data *player= get_player_data(player_index);
2292-
2293- if (get_monster_attitude(aggressor_index, player->monster_index)==_hostile &&
2294- clear_line_of_sight(aggressor_index, player->monster_index, full_circle))
2295- {
2296- closest_hostile_target_index= player->monster_index;
2297-
2298- break;
2299- }
2300- }
2301- }
2302-
2303- return closest_hostile_target_index;
2304-}
2305-
2306-/* if ヤfull_circleユ is true, the monster can see in all directions. if ヤfull_circleユ is false
2307- the monster respects his visual_arc and current facing. clear_line_of_sight() is implemented
2308- wholly in 2D and only attempts to connect the centers of the two monsters by a line. */
2309-static bool clear_line_of_sight(
2310- short viewer_index,
2311- short target_index,
2312- bool full_circle)
2313-{
2314- struct monster_data *viewer= get_monster_data(viewer_index);
2315- struct object_data *viewer_object= get_object_data(viewer->object_index);
2316- struct monster_definition *viewer_definition= get_monster_definition(viewer->type);
2317- struct monster_data *target= get_monster_data(target_index);
2318- struct object_data *target_object= get_object_data(target->object_index);
2319- bool target_visible= true;
2320-
2321- {
2322- world_point3d *origin= &viewer_object->location;
2323- world_point3d *destination= &target_object->location;
2324- // LP change: made this long-distance friendly
2325- int32 dx= int32(destination->x)-int32(origin->x);
2326- int32 dy= int32(destination->y)-int32(origin->y);
2327- world_distance dz= destination->z-origin->z;
2328- int32 distance2d= GUESS_HYPOTENUSE(ABS(dx), ABS(dy));
2329-
2330- /* if we canユt see full circle, make sure the target is in our visual arc */
2331- if (!full_circle)
2332- {
2333- angle theta= arctangent(dx, dy)-viewer_object->facing;
2334- angle phi= arctangent(distance2d, ABS(dz));
2335-
2336- if (ABS(theta)>viewer_definition->half_visual_arc) target_visible= false;
2337- if (phi>=viewer_definition->half_vertical_visual_arc&&phi<FULL_CIRCLE-viewer_definition->half_vertical_visual_arc) target_visible= false;
2338- }
2339-
2340- /* we canユt see some transfer modes */
2341- switch (target_object->transfer_mode)
2342- {
2343- case _xfer_invisibility:
2344- case _xfer_subtle_invisibility:
2345- if (distance2d>viewer_definition->dark_visual_range) target_visible= false;
2346- break;
2347- }
2348-
2349- /* make sure the target is within our visual_range (taking any of his active
2350- effects, i.e. invisibility, into account) and that he isnユt standing in a
2351- dark polygon beyond our dark_visual_range. */
2352- if (target_visible)
2353- {
2354- if (distance2d>viewer_definition->visual_range) // || (distance2d>viewer_definition->dark_visual_range&&get_object_light_intensity(target->object_index)<=LOW_LIGHT_INTENSITY))
2355- {
2356- target_visible= false;
2357- }
2358- }
2359-
2360- /* make sure there are no non-transparent lines between the viewer and the target */
2361- if (target_visible)
2362- {
2363- short polygon_index= viewer_object->polygon;
2364- short line_index;
2365-
2366- do
2367- {
2368- line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)origin, (world_point2d *)destination);
2369- if (line_index!=NONE)
2370- {
2371- if (LINE_IS_TRANSPARENT(get_line_data(line_index)))
2372- {
2373- /* transparent line, find adjacent polygon */
2374- polygon_index= find_adjacent_polygon(polygon_index, line_index);
2375- // LP change: make no polygon act like a non-transparent line
2376- if (polygon_index == NONE) target_visible= false;
2377- }
2378- else
2379- {
2380- /* non-transparent line, target not visible */
2381- target_visible= false;
2382- }
2383- }
2384- else
2385- {
2386- /* we got to the targetユs (x,y) location, but weユre in a different polygon;
2387- heユs invisible */
2388- if (polygon_index!=target_object->polygon) target_visible= false;
2389- }
2390- }
2391- while (target_visible&&line_index!=NONE);
2392- }
2393- }
2394-
2395- return target_visible;
2396-}
2397-
2398-/* lock the given monster onto the given target, playing a locking sound if the monster
2399- previously didnユt have a lock */
2400-void change_monster_target(
2401- short monster_index,
2402- short target_index)
2403-{
2404- /* locking on ourselves would be cool, but ... */
2405- if (monster_index!=target_index)
2406- {
2407- struct monster_data *monster= get_monster_data(monster_index);
2408- struct monster_definition *definition= get_monster_definition(monster->type);
2409-
2410- if (target_index!=NONE)
2411- {
2412- /* only active monsters can have lock, so activate inactive monsters */
2413- if (!MONSTER_IS_ACTIVE(monster)) activate_monster(monster_index);
2414-
2415- /* play activation sounds (including activating on a friendly) */
2416- if (monster->target_index!=target_index && TYPE_IS_FRIEND(definition, get_monster_data(target_index)->type))
2417- {
2418- play_object_sound(monster->object_index, definition->friendly_activation_sound);
2419- }
2420- else
2421- {
2422- if (monster->mode==_monster_unlocked) play_object_sound(monster->object_index, definition->activation_sound);
2423- }
2424-
2425- /* instantiate the new target and ask for a new path */
2426- if (MONSTER_HAS_VALID_TARGET(monster) && target_index!=monster->target_index) CLEAR_TARGET_DAMAGE_FLAG(monster);
2427- monster_needs_path(monster_index, false);
2428- set_monster_mode(monster_index, _monster_locked, target_index);
2429- }
2430- else
2431- {
2432- if (MONSTER_IS_ACTIVE(monster))
2433- {
2434- /* no target, if weユre not unlocked mark us as unlocked and ask for a new path */
2435- if (monster->mode!=_monster_unlocked)
2436- {
2437-// dprintf("monster #%d was locked on NONE;g;", monster_index);
2438-
2439- set_monster_mode(monster_index, _monster_unlocked, NONE);
2440- monster_needs_path(monster_index, false);
2441- }
2442- }
2443- }
2444- }
2445-}
2446-
2447-static void handle_moving_or_stationary_monster(
2448- short monster_index)
2449-{
2450- struct monster_data *monster= get_monster_data(monster_index);
2451- struct object_data *object= get_object_data(monster->object_index);
2452- struct monster_definition *definition= get_monster_definition(monster->type);
2453-
2454- if (monster->path==NONE && monster->mode!=_monster_locked && monster->action==_monster_is_stationary)
2455- {
2456- /* stationary, unlocked monsters without paths cannot move */
2457- monster_needs_path(monster_index, false);
2458- }
2459- else
2460- {
2461- world_distance distance_moved= definition->speed;
2462-
2463- /* base speed on difficulty level (for aliens) and berserk status */
2464- if (definition->flags&_monster_is_alien)
2465- {
2466- switch (dynamic_world->game_information.difficulty_level)
2467- {
2468- case _wuss_level: distance_moved-= distance_moved>>3; break;
2469- case _easy_level: distance_moved-= distance_moved>>4; break;
2470- case _major_damage_level: distance_moved+= distance_moved>>3; break;
2471- case _total_carnage_level: distance_moved+= distance_moved>>2; break;
2472- }
2473- }
2474- if (MONSTER_IS_BERSERK(monster)) distance_moved+= (distance_moved>>1);
2475-
2476- if (monster->action!=_monster_is_waiting_to_attack_again)
2477- {
2478- if (translate_monster(monster_index, distance_moved))
2479- {
2480- /* we moved: _monster_is_stationary becomes _monster_is_moving */
2481- if (monster->action==_monster_is_stationary) set_monster_action(monster_index, _monster_is_moving);
2482- }
2483- else
2484- {
2485- /* we couldnユt move: _monster_is_moving becomes _monster_is_stationary */
2486- if (monster->action==_monster_is_moving) set_monster_action(monster_index, _monster_is_stationary);
2487- monster->ticks_since_attack+= 1; /* attacks occur twice as frequently if we canユt move (damnit!) */
2488- }
2489- }
2490- else
2491- {
2492- monster->ticks_since_attack+= 1;
2493- }
2494-
2495- /* whether we moved or not, see if we can attack if we have lock */
2496- monster->ticks_since_attack+= MONSTER_IS_BERSERK(monster) ? 3 : 1;
2497- if (OBJECT_WAS_ANIMATED(object) && monster->mode==_monster_locked)
2498- {
2499- short attack_frequency= definition->attack_frequency;
2500-
2501- if (definition->flags&_monster_is_alien)
2502- {
2503- switch (dynamic_world->game_information.difficulty_level)
2504- {
2505- case _wuss_level: attack_frequency= 3*attack_frequency; break;
2506- case _easy_level: attack_frequency= 2*attack_frequency; break;
2507- case _major_damage_level: attack_frequency= attack_frequency>>1; break;
2508- case _total_carnage_level: attack_frequency= attack_frequency>>2; break;
2509- }
2510- }
2511-
2512- if (monster->ticks_since_attack>attack_frequency)
2513- {
2514- if (try_monster_attack(monster_index))
2515- {
2516- /* activate with lock nearby monsters on our target */
2517- activate_nearby_monsters(monster->target_index, monster_index, _pass_one_zone_border);
2518- }
2519- else
2520- {
2521- if (monster->action==_monster_is_waiting_to_attack_again)
2522- {
2523- set_monster_action(monster_index, _monster_is_stationary);
2524- monster_needs_path(monster_index, true);
2525- }
2526- }
2527- }
2528- }
2529- }
2530-}
2531-
2532-void set_monster_action(
2533- short monster_index,
2534- short action)
2535-{
2536- struct monster_data *monster= get_monster_data(monster_index);
2537- struct monster_definition *definition= get_monster_definition(monster->type);
2538- shape_descriptor shape;
2539-
2540- /* what shape should we use? */
2541- if (action==_monster_is_dying_flaming)
2542- {
2543- shape= FLAMING_DYING_SHAPE;
2544- }
2545- else
2546- {
2547- switch (action)
2548- {
2549- case _monster_is_waiting_to_attack_again:
2550- case _monster_is_stationary: shape= definition->stationary_shape; break;
2551- case _monster_is_moving: shape= definition->moving_shape; break;
2552- case _monster_is_attacking_close: shape= definition->melee_attack.attack_shape; break;
2553- case _monster_is_attacking_far: shape= definition->ranged_attack.attack_shape; break;
2554- case _monster_is_being_hit: shape= definition->hit_shapes; break;
2555- case _monster_is_dying_hard: shape= definition->hard_dying_shape; break;
2556- case _monster_is_dying_soft: shape= definition->soft_dying_shape; break;
2557- case _monster_is_teleporting_in: shape= definition->teleport_in_shape; break;
2558- case _monster_is_teleporting_out: shape= definition->teleport_out_shape; break;
2559- default: dprintf("what is monster action #%d?", action); assert(false); break;
2560- }
2561-
2562- shape= shape==UNONE ? UNONE : BUILD_DESCRIPTOR(definition->collection, shape);
2563- }
2564-
2565- if (shape!=UNONE)
2566- {
2567- /* only set the action of the shape is not UNONE */
2568- monster->action= action;
2569- set_object_shape_and_transfer_mode(monster->object_index, shape, NONE);
2570-
2571- /* if this monster does shrapnel damage, do it */
2572- if (action==_monster_is_dying_hard && (definition->flags&_monster_has_delayed_hard_death))
2573- {
2574- cause_shrapnel_damage(monster_index);
2575- }
2576-
2577- if ((definition->flags&_monster_has_nuclear_hard_death) && action==_monster_is_dying_hard)
2578- {
2579- start_fade(_fade_long_bright);
2580- SoundManager::instance()->PlayLocalSound(Sound_Exploding());
2581- }
2582- }
2583-}
2584-
2585-/* do whatever needs to be done when this monster dies and remove it from the monster list */
2586-static void kill_monster(
2587- short monster_index)
2588-{
2589- struct monster_data *monster= get_monster_data(monster_index);
2590- struct monster_definition *definition= get_monster_definition(monster->type);
2591- struct object_data *object= get_object_data(monster->object_index);
2592- shape_descriptor shape;
2593-
2594- switch (monster->action)
2595- {
2596- case _monster_is_dying_soft:
2597- shape= definition->soft_dead_shapes==UNONE ? UNONE : BUILD_DESCRIPTOR(definition->collection, definition->soft_dead_shapes);
2598- break;
2599- case _monster_is_dying_hard:
2600- shape= definition->hard_dead_shapes==UNONE ? UNONE : BUILD_DESCRIPTOR(definition->collection, definition->hard_dead_shapes);
2601- break;
2602- case _monster_is_dying_flaming:
2603- shape= FLAMING_DEAD_SHAPE;
2604- break;
2605-
2606- default:
2607- assert(false);
2608- break;
2609- }
2610-
2611- /* add an item if weユre supposed to be carrying something */
2612- if (definition->carrying_item_type!=NONE && monster->action==_monster_is_dying_soft)
2613- {
2614- world_distance radius, height;
2615- world_point3d random_point;
2616- short random_polygon_index;
2617-
2618- get_monster_dimensions(monster_index, &radius, &height);
2619- random_point_on_circle(&object->location, object->polygon, radius, &random_point, &random_polygon_index);
2620- if (random_polygon_index!=NONE)
2621- {
2622- struct polygon_data *random_polygon= get_polygon_data(random_polygon_index);
2623- struct object_location location;
2624-
2625- switch (random_polygon->type)
2626- {
2627- case _polygon_is_platform:
2628- case _polygon_is_item_impassable:
2629- case _polygon_is_monster_impassable:
2630- case _polygon_is_teleporter:
2631- break;
2632-
2633- default:
2634- location.polygon_index= random_polygon_index;
2635- location.p.x= random_point.x, location.p.y= random_point.y, location.p.z= 0;
2636- location.yaw= 0;
2637- location.flags= 0;
2638- new_item(&location, definition->carrying_item_type);
2639- break;
2640- }
2641- }
2642- }
2643-
2644- /* stuff in an appropriate dead shape (or remove our object if we donユt have a dead shape) */
2645- if (shape==UNONE)
2646- {
2647- remove_map_object(monster->object_index);
2648- }
2649- else
2650- {
2651- turn_object_to_shit(monster->object_index);
2652- randomize_object_sequence(monster->object_index, shape);
2653- }
2654-
2655- /* recover original type and notify the object stuff a monster died */
2656- if (monster->flags&_monster_was_promoted) monster->type-= 1;
2657- if (monster->flags&_monster_was_demoted) monster->type+= 1;
2658- object_was_just_destroyed(_object_is_monster, monster->type);
2659-
2660- L_Invalidate_Monster(monster_index);
2661- MARK_SLOT_AS_FREE(monster);
2662-}
2663-
2664-/* move the monster along his current heading; if he reaches the center of his destination square,
2665- then point him at the next square and send him off. this used to chuck if the monster moved
2666- too far during a certain turn (which was completely possible when the player was wearing the
2667- red cloak in Pathways), but that was fixed. i just recoded this for marathon and it looks
2668- a hell of a lot better now. */
2669-static bool translate_monster(
2670- short monster_index,
2671- world_distance distance)
2672-{
2673- struct monster_data *monster= get_monster_data(monster_index);
2674- struct object_data *object= get_object_data(monster->object_index);
2675- struct monster_definition *definition= get_monster_definition(monster->type);
2676- world_point3d new_location;
2677- short obstacle_index;
2678- bool legal_move= false;
2679-
2680- new_location= object->location;
2681- translate_point2d((world_point2d *)&new_location, distance, object->facing);
2682-
2683- /* find out where weユre going and see if we could actually move there */
2684- if ((obstacle_index= legal_monster_move(monster_index, object->facing, &new_location))==NONE)
2685- {
2686- /* legal move: see if there is a platform that we have to open or wait for,
2687- if not move, if so, wait */
2688-
2689- short feature_index;
2690- short relevant_polygon_index;
2691-
2692- legal_move= true;
2693- switch (find_obstructing_terrain_feature(monster_index, &feature_index, &relevant_polygon_index))
2694- {
2695- case _entering_platform_polygon:
2696- switch (monster_can_enter_platform(feature_index, relevant_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
2697- {
2698- case _platform_will_never_be_accessable:
2699- monster_needs_path(monster_index, true);
2700- break;
2701-
2702- case _platform_will_be_accessable:
2703- /* we avoid vidding the door by only trying to open it every door_retry_mask+1 ticks */
2704- if (!(dynamic_world->tick_count&definition->door_retry_mask)) try_and_change_platform_state(feature_index, true);
2705- SET_MONSTER_IDLE_STATUS(monster, true);
2706- legal_move= false;
2707- break;
2708-
2709- /* _platform_is_accessable */
2710- }
2711- break;
2712-
2713- case _leaving_platform_polygon:
2714- switch (monster_can_leave_platform(feature_index, relevant_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
2715- {
2716- case _exit_will_never_be_accessable:
2717- monster_needs_path(monster_index, true);
2718- break;
2719-
2720- case _exit_will_be_accessable:
2721- SET_MONSTER_IDLE_STATUS(monster, true);
2722- legal_move= false;
2723- break;
2724-
2725- /* _exit_is_accessable, ignored */
2726- }
2727- break;
2728-
2729- case _flying_or_floating_transition:
2730- /* there is a wall in our way which we have to rise (or fall) along, so donユt
2731- go anywhere unless weユre over it (or under it) */
2732- if (ABS(object->location.z-monster->desired_height)>MINIMUM_FLOATING_HEIGHT) legal_move= false;
2733- break;
2734-
2735- case _standing_on_sniper_ledge:
2736- /* weユve been told to freeze on a sniper ledge (no saving throw) */
2737- legal_move= false;
2738- break;
2739- }
2740-
2741- if (legal_move)
2742- {
2743- if ((monster->path_segment_length-= distance)<=0)
2744- {
2745- advance_monster_path(monster_index);
2746- }
2747- else
2748- {
2749- short old_polygon_index= object->polygon;
2750-
2751- /* update the monsterユs object to reflect his new position */
2752- if (translate_map_object(monster->object_index, &new_location, NONE)) monster_moved(monster_index, old_polygon_index);
2753- }
2754-
2755- legal_move= true;
2756- }
2757- }
2758- else
2759- {
2760- struct object_data *obstacle_object= get_object_data(obstacle_index);
2761-
2762- if (GET_OBJECT_OWNER(obstacle_object)==_object_is_monster)
2763- {
2764- struct monster_data *obstacle_monster= get_monster_data(obstacle_object->permutation);
2765-
2766- /* we collided with another monster: see if we want to attack him; if not, see if we
2767- can attack his current target (if he is locked or losing_lock); if not, drop lock
2768- and ask for a new path. */
2769-
2770- if (!TYPE_IS_ENEMY(definition, obstacle_monster->type) && !(MONSTER_HAS_VALID_TARGET(monster)&&monster->target_index==obstacle_object->permutation) &&
2771- !MONSTER_IS_BERSERK(monster))
2772- {
2773- if (!MONSTER_IS_PLAYER(obstacle_monster))
2774- {
2775- if (monster->mode!=_monster_locked)
2776- {
2777- if (!MONSTER_HAS_VALID_TARGET(obstacle_monster) || !switch_target_check(monster_index, obstacle_monster->target_index, 0))
2778- {
2779- if (monster->mode==_monster_unlocked && !(global_random()&OBSTRUCTION_DEACTIVATION_MASK) &&
2780- (monster->goal_polygon_index==NONE || monster->goal_polygon_index==object->polygon))
2781- {
2782- deactivate_monster(monster_index);
2783- }
2784- else
2785- {
2786- monster_needs_path(monster_index, false);
2787- if (monster->mode!=_monster_locked)
2788- {
2789- /* if weユre not locked, we might want to think about deactivating here, but
2790- for now we just build a new random path by forcing our state to _unlocked. */
2791- set_monster_mode(monster_index, _monster_unlocked, NONE);
2792- // dprintf("monster #%d going unlocked by obstruction;g;", monster_index);
2793- }
2794- }
2795- }
2796- }
2797- else
2798- {
2799- attempt_evasive_manouvers(monster_index);
2800- }
2801- }
2802-
2803- SET_MONSTER_IDLE_STATUS(monster, true);
2804- }
2805- else
2806- {
2807- struct monster_definition *obstacle_definition= get_monster_definition(obstacle_monster->type);
2808- world_distance key_height= obstacle_object->location.z+(obstacle_definition->height>>1);
2809-
2810- change_monster_target(monster_index, obstacle_object->permutation);
2811-
2812- /* if weユre a kamakazi and weユre within range, pop */
2813- if ((definition->flags&_monster_is_kamakazi) &&
2814- object->location.z<key_height)
2815- {
2816- bool in_range = object->location.z+definition->height>key_height;
2817-
2818- /* if we're short and can't float, take out their knees! */
2819- if (!in_range && film_profile.allow_short_kamikaze && !(definition->flags&_monster_floats))
2820- in_range = object->location.z>=obstacle_object->location.z;
2821-
2822- if (in_range)
2823- {
2824- set_monster_action(monster_index, _monster_is_dying_hard);
2825- monster_died(monster_index);
2826- }
2827- }
2828-
2829- /* if we float and this is our target, go up */
2830- if (definition->flags&_monster_floats)
2831- {
2832- monster->desired_height= obstacle_object->location.z;
2833- }
2834- }
2835- }
2836- else
2837- {
2838- attempt_evasive_manouvers(monster_index); // to avoid the scenery
2839- }
2840- }
2841-
2842- return legal_move;
2843-}
2844-
2845-static bool attempt_evasive_manouvers(
2846- short monster_index)
2847-{
2848- struct monster_data *monster= get_monster_data(monster_index);
2849- struct object_data *object= get_object_data(monster->object_index);
2850- world_point2d destination= *((world_point2d*)&object->location);
2851- angle new_facing= NORMALIZE_ANGLE(object->facing + ((global_random()&1) ? QUARTER_CIRCLE : -QUARTER_CIRCLE));
2852- world_distance original_floor_height= get_polygon_data(object->polygon)->floor_height;
2853- short polygon_index= object->polygon;
2854- bool successful= true;
2855-
2856- translate_point2d(&destination, EVASIVE_MANOUVER_DISTANCE, new_facing);
2857- do
2858- {
2859- short line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)&object->location, &destination);
2860-
2861- if (line_index==NONE)
2862- {
2863- polygon_index= NONE;
2864- }
2865- else
2866- {
2867- /* if we ran off the map, we failed */
2868- if (LINE_IS_SOLID(get_line_data(line_index)) || (polygon_index= find_adjacent_polygon(polygon_index, line_index))==NONE)
2869- {
2870- polygon_index= NONE;
2871- successful= false;
2872- }
2873- else
2874- {
2875- struct polygon_data *polygon= get_polygon_data(polygon_index);
2876- if (polygon->floor_height!=original_floor_height || polygon->type==_polygon_is_monster_impassable)
2877- {
2878- polygon_index= NONE;
2879- successful= false;
2880- }
2881- }
2882- }
2883- }
2884- while (polygon_index!=NONE);
2885-
2886- if (successful)
2887- {
2888- object->facing= new_facing;
2889- if (monster->path!=NONE) delete_path(monster->path), monster->path= NONE;
2890- monster->path_segment_length= EVASIVE_MANOUVER_DISTANCE;
2891- }
2892-
2893- return successful;
2894-}
2895-
2896-void advance_monster_path(
2897- short monster_index)
2898-{
2899- struct monster_data *monster= get_monster_data(monster_index);
2900- struct object_data *object= get_object_data(monster->object_index);
2901- world_point2d path_goal;
2902- bool done= true;
2903-
2904- if (monster->path==NONE)
2905- {
2906- /* only locked monsters in their targetユs polygon can advance without paths */
2907- if (monster->mode!=_monster_locked || object->polygon!=get_object_data(get_monster_data(monster->target_index)->object_index)->polygon)
2908- {
2909- monster_needs_path(monster_index, true);
2910- return;
2911- }
2912- }
2913- else
2914- {
2915- done= move_along_path(monster->path, &path_goal);
2916- if (done)
2917- monster->path= NONE;
2918- }
2919-
2920- /* if weユre locked without a path, head right for the bastard (heユs in our polygon) */
2921- if ((done||monster->path==NONE) && monster->mode==_monster_locked)
2922- {
2923- struct monster_data *target= get_monster_data(monster->target_index);
2924- struct object_data *target_object= get_object_data(target->object_index);
2925-
2926- if (object->polygon==target_object->polygon)
2927- {
2928- path_goal= *(world_point2d *)&get_object_data(get_monster_data(monster->target_index)->object_index)->location;
2929- done= false;
2930- }
2931- }
2932-
2933- if (done)
2934- {
2935- /* ask for a new path (never happens to locked monsters) */
2936- monster_needs_path(monster_index, false);
2937- if (monster->mode==_monster_unlocked)
2938- {
2939- monster->goal_polygon_index= NONE;
2940- if (MONSTER_TELEPORTS_OUT_WHEN_DEACTIVATED(monster)) deactivate_monster(monster_index);
2941- }
2942- }
2943- else
2944- {
2945- /* point ourselves at this new point in the path */
2946- object->facing= arctangent(path_goal.x-object->location.x, path_goal.y-object->location.y);
2947- monster->path_segment_length= distance2d(&path_goal, (world_point2d *)&object->location);
2948- }
2949-}
2950-
2951-static bool try_monster_attack(
2952- short monster_index)
2953-{
2954- struct monster_data *monster= get_monster_data(monster_index);
2955- struct object_data *object= get_object_data(monster->object_index);
2956- struct monster_definition *definition= get_monster_definition(monster->type);
2957- short repetitions= NONE;
2958- short new_action= NONE, obstruction_index= NONE;
2959- angle theta = 0;
2960-
2961- if (MONSTER_HAS_VALID_TARGET(monster))
2962- {
2963- struct object_data *target_object= get_object_data(get_monster_data(monster->target_index)->object_index);
2964- world_point3d origin= object->location, destination= target_object->location;
2965- world_distance range= distance2d((world_point2d *)&origin, (world_point2d *)&destination);
2966- short polygon_index;
2967- world_point3d _vector;
2968-
2969- theta= arctangent(destination.x-origin.x, destination.y-origin.y);
2970- angle delta_theta= NORMALIZE_ANGLE(theta-object->facing);
2971-
2972- if (!(definition->flags&_monster_cant_fire_backwards) || (delta_theta<QUARTER_CIRCLE+QUARTER_CIRCLE/2 || delta_theta>FULL_CIRCLE-QUARTER_CIRCLE-QUARTER_CIRCLE/2))
2973- {
2974- switch (monster->action)
2975- {
2976- case _monster_is_attacking_close:
2977- case _monster_is_attacking_far:
2978- new_action= monster->action;
2979- break;
2980-
2981- default:
2982- if (definition->ranged_attack.type!=NONE && range<definition->ranged_attack.range) new_action= _monster_is_attacking_far;
2983- if (definition->melee_attack.type!=NONE && range<definition->melee_attack.range)
2984- {
2985- new_action= _monster_is_attacking_close;
2986-
2987- if (definition->flags&_monster_chooses_weapons_randomly)
2988- {
2989- bool switch_to_ranged = true;
2990- if (film_profile.validate_random_ranged_attack)
2991- {
2992- if (definition->ranged_attack.type == NONE)
2993- {
2994- logWarning("Monster chooses weapons randomly, but has no ranged attack");
2995- definition->flags &= ~_monster_chooses_weapons_randomly;
2996- switch_to_ranged = false;
2997- }
2998- else
2999- switch_to_ranged = (range<definition->ranged_attack.range);
3000- }
3001- if (switch_to_ranged && global_random()&1)
3002- new_action= _monster_is_attacking_far;
3003- }
3004- }
3005- break;
3006- }
3007-
3008- /* if we have a melee attack and we're at short range, use it */
3009- if (new_action==_monster_is_attacking_close)
3010- {
3011- /* make sure this is a valid projectile, that we donユt hit any walls and that whatever
3012- we did hit is _hostile. */
3013- polygon_index= position_monster_projectile(monster_index, monster->target_index, &definition->melee_attack, &origin, &destination, &_vector, theta);
3014- if (preflight_projectile(&origin, polygon_index, &destination, definition->melee_attack.error,
3015- definition->melee_attack.type, monster_index, monster->type, &obstruction_index))
3016- {
3017- if ((obstruction_index!=NONE && get_monster_attitude(monster_index, obstruction_index)==_hostile) ||
3018- !line_is_obstructed(object->polygon, (world_point2d *) &object->location, target_object->polygon, (world_point2d *) &target_object->location))
3019- {
3020- repetitions= definition->melee_attack.repetitions;
3021- }
3022- }
3023- }
3024- else
3025- {
3026- /* make sure we have a ranged attack and our target is within range */
3027- if (new_action==_monster_is_attacking_far)
3028- {
3029- /* make sure this is a valid projectile, that we donユt hit any walls and that whatever
3030- we did hit is _hostile. */
3031- polygon_index= position_monster_projectile(monster_index, monster->target_index, &definition->ranged_attack, &origin, &destination, &_vector, theta);
3032- if (preflight_projectile(&origin, polygon_index, &destination, definition->ranged_attack.error,
3033- definition->ranged_attack.type, monster_index, monster->type, &obstruction_index))
3034- {
3035- if ((obstruction_index!=NONE && get_monster_attitude(monster_index, obstruction_index)==_hostile) ||
3036- (obstruction_index==NONE && !line_is_obstructed(object->polygon, (world_point2d *) &object->location, target_object->polygon, (world_point2d *) &target_object->location)))
3037- {
3038- repetitions= definition->ranged_attack.repetitions;
3039- }
3040- }
3041- }
3042- }
3043- }
3044- }
3045-
3046- if (repetitions!=NONE)
3047- {
3048- /* we can attack; set monster facing, start the attack action and reset ticks_since_attack */
3049- object->facing= theta;
3050- if (monster->action!=new_action) /* if weユre already attacking, this is a chained attack */
3051- {
3052- switch (dynamic_world->game_information.difficulty_level)
3053- {
3054- case _wuss_level: case _easy_level: repetitions>>= 1;
3055- case _normal_level: repetitions= (repetitions<=1) ? repetitions : repetitions-1; break;
3056- }
3057-
3058- set_monster_action(monster_index, new_action);
3059- monster->attack_repetitions= repetitions;
3060- }
3061-
3062- /* on the highest level, hitting a monster in the middle of an attack doesnユt really
3063- stop him from continuing to attack because ticks_since_attack is never reset */
3064- switch (dynamic_world->game_information.difficulty_level)
3065- {
3066- case _total_carnage_level:
3067- break;
3068-
3069- default:
3070- monster->ticks_since_attack= 0;
3071- break;
3072- }
3073- }
3074- else
3075- {
3076- /* we canユt attack (for whatever reason), halve ticks_since_attack so we try again soon */
3077- monster->ticks_since_attack= 0;
3078-
3079- if (obstruction_index!=NONE && get_monster_attitude(monster_index, obstruction_index)==_friendly &&
3080- MONSTER_IS_PLAYER(get_monster_data(obstruction_index)))
3081- {
3082- play_object_sound(monster->object_index, definition->clear_sound);
3083- }
3084- }
3085-
3086- return new_action==NONE ? false : true;
3087-}
3088-
3089-static void execute_monster_attack(
3090- short monster_index)
3091-{
3092- struct monster_data *monster= get_monster_data(monster_index);
3093-
3094- /* we used to assert that the attacking monster was locked, but monsters can be deactivated
3095- or lose lock during an attack (!) so we just abort if we no longer have a valid target */
3096- if (MONSTER_HAS_VALID_TARGET(monster))
3097- {
3098- struct monster_definition *definition= get_monster_definition(monster->type);
3099- struct object_data *object= get_object_data(monster->object_index);
3100- struct attack_definition *attack= (monster->action==_monster_is_attacking_close) ? &definition->melee_attack : &definition->ranged_attack;
3101- short projectile_polygon_index;
3102- world_point3d origin= object->location;
3103- world_point3d _vector;
3104-
3105- projectile_polygon_index= position_monster_projectile(monster_index, monster->target_index, attack, &origin, (world_point3d *) NULL, &_vector, object->facing);
3106- if (projectile_polygon_index != NONE)
3107- new_projectile(&origin, projectile_polygon_index, &_vector, attack->error, attack->type, monster_index, monster->type, monster->target_index, FIXED_ONE);
3108- if (definition->flags&_monster_fires_symmetrically)
3109- {
3110- attack->dy= -attack->dy;
3111- projectile_polygon_index= position_monster_projectile(monster_index, monster->target_index, attack, &origin, (world_point3d *) NULL, &_vector, object->facing);
3112- if (projectile_polygon_index != NONE)
3113- new_projectile(&origin, projectile_polygon_index, &_vector, attack->error, attack->type, monster_index, monster->type, monster->target_index, FIXED_ONE);
3114- attack->dy= -attack->dy;
3115- }
3116- }
3117-}
3118-
3119-int32 monster_pathfinding_cost_function(
3120- short source_polygon_index,
3121- short line_index,
3122- short destination_polygon_index,
3123- void *vdata)
3124-{
3125- struct monster_pathfinding_data *data=(struct monster_pathfinding_data *)vdata;
3126- struct monster_definition *definition= data->definition;
3127- struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
3128- struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
3129- struct line_data *line= get_line_data(line_index);
3130- bool respect_polygon_heights= true;
3131- struct object_data *object;
3132- short object_index;
3133- int32 cost;
3134-
3135- /* base cost is the area of the polygon weユre leaving */
3136- cost= source_polygon->area;
3137-
3138- /* no solid lines (baby) */
3139- if (LINE_IS_SOLID(line) && !LINE_IS_VARIABLE_ELEVATION(line)) cost= -1;
3140-
3141- /* count up the monsters in destination_polygon and add a constant cost, MONSTER_PATHFINDING_OBSTRUCTION_PENALTY,
3142- for each of them to discourage overcrowding */
3143- for (object_index= destination_polygon->first_object; object_index!=NONE; object_index= object->next_object)
3144- {
3145- object= get_object_data(object_index);
3146- if (GET_OBJECT_OWNER(object)==_object_is_monster) cost+= MONSTER_PATHFINDING_OBSTRUCTION_COST;
3147- }
3148-
3149- /* if weユre trying to move into a polygon with an area smaller than MINIMUM_MONSTER_PATHFINDING_POLYGON_AREA, disallow the move */
3150- if (source_polygon->area<MINIMUM_MONSTER_PATHFINDING_POLYGON_AREA) cost= -1;
3151-
3152- // do platform stuff
3153- if (cost>0)
3154- {
3155- if (destination_polygon->type==_polygon_is_platform)
3156- {
3157- switch (monster_can_enter_platform(destination_polygon->permutation, source_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
3158- {
3159- case _platform_will_never_be_accessable: cost= -1; break;
3160- default: cost+= MONSTER_PATHFINDING_PLATFORM_COST; respect_polygon_heights= false; break;
3161- }
3162- }
3163- if (source_polygon->type==_polygon_is_platform)
3164- {
3165- switch (monster_can_leave_platform(source_polygon->permutation, destination_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
3166- {
3167- case _exit_will_never_be_accessable: cost= -1; break;
3168- default: respect_polygon_heights= false; break;
3169- }
3170- }
3171- }
3172-
3173- /* if the ledge between polygons is too high, the fall is too far, or there just
3174- isnユt enough vertical space, disallow the move (and ignore this if weユre dealing with
3175- platforms or doors) */
3176- if (respect_polygon_heights)
3177- {
3178- world_distance delta_height= destination_polygon->floor_height-source_polygon->floor_height;
3179-
3180- if (delta_height<definition->minimum_ledge_delta||delta_height>definition->maximum_ledge_delta) cost= -1;
3181- if (line->lowest_adjacent_ceiling-line->highest_adjacent_floor<definition->height) cost= -1;
3182-
3183- if (cost>0) cost+= delta_height*delta_height; /* prefer not to change heights */
3184- }
3185-
3186- /* if this line not wide enough, disallow the move */
3187- if (line->length<2*definition->radius) cost= -1;
3188-
3189- if (cost>0)
3190- {
3191- /* if weユre trying to move into an impassable polygon, disallow the move */
3192- switch (destination_polygon->type)
3193- {
3194- case _polygon_is_zone_border:
3195- if (!data->cross_zone_boundaries) cost= -1;
3196- break;
3197-
3198- case _polygon_is_monster_impassable:
3199- case _polygon_is_teleporter:
3200- cost= -1;
3201- break;
3202- }
3203- }
3204-
3205- if (cost>0)
3206- {
3207- /* if weユre trying to move into media, pay the penalty */
3208- if (destination_polygon->media_index!=NONE)
3209- {
3210- struct media_data *media= get_media_data(destination_polygon->media_index);
3211-
3212- // LP change: idiot-proofed this
3213- if (media)
3214- {
3215- if (media->height>destination_polygon->floor_height)
3216- {
3217- cost+= 2*destination_polygon->area;
3218- }
3219- }
3220- }
3221- }
3222-
3223- return cost;
3224-}
3225-
3226-/* returns the type and index of any interesting terrain feature (platform or door) in front
3227- of the given monster in his current direction; this lets us open doors and wait for
3228- platforms. relevant_polygon_index is the polygon_index we have to pass to platform_is_accessable */
3229-static short find_obstructing_terrain_feature(
3230- short monster_index,
3231- short *feature_index,
3232- short *relevant_polygon_index)
3233-{
3234- struct monster_data *monster= get_monster_data(monster_index);
3235- struct monster_definition *definition= get_monster_definition(monster->type);
3236- struct object_data *object= get_object_data(monster->object_index);
3237- short polygon_index, feature_type;
3238- world_point2d p1;
3239-
3240- ray_to_line_segment((world_point2d *)&object->location, &p1, object->facing, MONSTER_PLATFORM_BUFFER_DISTANCE+definition->radius);
3241-
3242- feature_type= NONE;
3243- *relevant_polygon_index= polygon_index= object->polygon;
3244- do
3245- {
3246- struct polygon_data *polygon= get_polygon_data(polygon_index);
3247- short line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)&object->location, &p1);
3248-
3249- switch (polygon->type)
3250- {
3251- case _polygon_is_platform:
3252- if (object->polygon==polygon_index)
3253- {
3254- /* weユre standing on the platform: find out where weユre headed (if weユre
3255- going nowhere then pretend like everything is o.k.) */
3256-
3257- polygon_index= line_index==NONE ? NONE : find_adjacent_polygon(polygon_index, line_index);
3258- if (polygon_index!=NONE)
3259- {
3260- *relevant_polygon_index= polygon_index;
3261- *feature_index= polygon->permutation;
3262- feature_type= _leaving_platform_polygon;
3263- assert(*feature_index!=NONE);
3264- }
3265- }
3266- else
3267- {
3268- feature_type= _entering_platform_polygon;
3269- *feature_index= polygon->permutation;
3270- assert(*feature_index!=NONE);
3271- }
3272- break;
3273-
3274- default:
3275- if (((definition->flags&_monster_floats) && polygon->floor_height>monster->desired_height) ||
3276- object->location.z+definition->height>polygon->ceiling_height)
3277- {
3278- monster->desired_height= polygon->floor_height;
3279- feature_type= _flying_or_floating_transition;
3280- *feature_index= 0;
3281- }
3282- if (definition->flags&_monster_flys)
3283- {
3284- if ((polygon->floor_height>monster->desired_height) ||
3285- (polygon->ceiling_height<monster->desired_height+definition->height))
3286- {
3287- monster->desired_height= (polygon->floor_height>monster->desired_height) ?
3288- polygon->floor_height : (polygon->ceiling_height - definition->height);
3289- feature_type= _flying_or_floating_transition;
3290- *feature_index= 0;
3291- }
3292-
3293- if (object->location.z<polygon->floor_height || object->location.z+definition->height>polygon->ceiling_height)
3294- {
3295- feature_type= _flying_or_floating_transition;
3296- *feature_index= 0;
3297- }
3298- }
3299- if (definition->flags&_monster_uses_sniper_ledges)
3300- {
3301- if ((polygon->floor_height+MINIMUM_SNIPER_ELEVATION<monster->desired_height) &&
3302- monster->mode==_monster_locked)
3303- {
3304- feature_type= _standing_on_sniper_ledge;
3305- }
3306- }
3307- if (!(definition->flags&(_monster_floats|_monster_flys)) && polygon->media_index!=NONE)
3308- {
3309- struct media_data *media= get_media_data(polygon->media_index);
3310- // Monster will normally wade to half-height
3311- world_distance height= definition->height>>1;
3312- // LP change: idiot-proofing
3313- if (media)
3314- {
3315- // In dangerous media, wade only to zero height (that is, don't wade at all)
3316- if (IsMediaDangerous(media->type)) height= 0;
3317-
3318- switch (media->type)
3319- {
3320- case _media_water: if (definition->flags&_monster_is_not_afraid_of_water) media= (struct media_data *) NULL; break;
3321- case _media_jjaro: // LP addition: monsters treat Jjaro goo like sewage
3322- case _media_sewage: if (definition->flags&_monster_is_not_afraid_of_sewage) media= (struct media_data *) NULL; break;
3323- case _media_lava: /* height= 0; */ if (definition->flags&_monster_is_not_afraid_of_lava) media= (struct media_data *) NULL; break;
3324- case _media_goo: /* height= 0; */ if (definition->flags&_monster_is_not_afraid_of_goo) media= (struct media_data *) NULL; break;
3325- }
3326- }
3327- if (media && media->height-polygon->floor_height>height)
3328- {
3329- if (get_polygon_data(object->polygon)->floor_height>polygon->floor_height)
3330- {
3331- feature_type= _standing_on_sniper_ledge;
3332- if (monster->mode!=_monster_locked) monster_needs_path(monster_index, false);
3333- }
3334- }
3335- }
3336- polygon_index= line_index==NONE ? NONE : find_adjacent_polygon(polygon_index, line_index);
3337- break;
3338- }
3339-
3340- if (line_index!=NONE && polygon_index==NONE)
3341- {
3342- if (monster->path_segment_length<MONSTER_PLATFORM_BUFFER_DISTANCE)
3343- {
3344- monster->path_segment_length= 0;
3345- }
3346- else
3347- {
3348- /* weユre headed for a wall solid; freeze and get a new path, pronto */
3349- feature_type= _standing_on_sniper_ledge;
3350- monster_needs_path(monster_index, true);
3351- }
3352- }
3353- }
3354- while (polygon_index!=NONE&&(feature_type==NONE||feature_type==_flying_or_floating_transition));
3355-
3356- return feature_type;
3357-}
3358-
3359-/* returns new polygon index; if destination is NULL then we fire along the monsterユs facing
3360- and elevation, if destination is not NULL then we set it correctly and save the elevation angle */
3361-static short position_monster_projectile(
3362- short aggressor_index,
3363- short target_index,
3364- struct attack_definition *attack,
3365- world_point3d *origin,
3366- world_point3d *destination,
3367- world_point3d *_vector,
3368- angle theta)
3369-{
3370- struct monster_data *aggressor= get_monster_data(aggressor_index);
3371- struct monster_data *target= get_monster_data(target_index);
3372- struct object_data *aggressor_object= get_object_data(aggressor->object_index);
3373- struct object_data *target_object= get_object_data(target->object_index);
3374- world_distance radius, height;
3375-
3376-// dprintf("positioning #%d to #%d", aggressor_index, target_index);
3377-
3378- /* adjust origin */
3379- *origin= aggressor_object->location;
3380- origin->z+= attack->dz;
3381- translate_point2d((world_point2d *)origin, attack->dy, NORMALIZE_ANGLE(theta+QUARTER_CIRCLE));
3382- translate_point2d((world_point2d *)origin, attack->dx, theta);
3383-
3384- if (destination)
3385- {
3386- world_distance distance;
3387-
3388- /* adjust destination */
3389- get_monster_dimensions(target_index, &radius, &height);
3390- *destination= target_object->location;
3391- destination->z+= (height>>1) + (height>>2); /* shoot 3/4ths up the target */
3392-
3393- /* calculate outbound vector */
3394- _vector->x= destination->x-origin->x;
3395- _vector->y= destination->y-origin->y;
3396- _vector->z= destination->z-origin->z;
3397-
3398- distance= isqrt(_vector->x*_vector->x + _vector->y*_vector->y);
3399- aggressor->elevation= distance ? (_vector->z*TRIG_MAGNITUDE)/distance : 0;
3400- }
3401- else
3402- {
3403- _vector->x= cosine_table[theta];
3404- _vector->y= sine_table[theta];
3405- _vector->z= aggressor->elevation;
3406- }
3407-
3408- /* return polygon_index of the new origin point */
3409- return find_new_object_polygon((world_point2d *)&aggressor_object->location,
3410- (world_point2d *)origin, aggressor_object->polygon);
3411-}
3412-
3413-short nearest_goal_polygon_index(
3414- short polygon_index)
3415-{
3416- polygon_index= flood_map(polygon_index, INT32_MAX, nearest_goal_cost_function, _breadth_first, (void *) NULL);
3417- while (polygon_index!=NONE)
3418- {
3419- struct polygon_data *polygon= get_polygon_data(polygon_index);
3420-
3421- if (polygon->type==_polygon_is_goal) break;
3422-
3423- polygon_index= flood_map(NONE, INT32_MAX, nearest_goal_cost_function, _breadth_first, (void *) NULL);
3424- }
3425-
3426- return polygon_index;
3427-}
3428-
3429-static int32 nearest_goal_cost_function(
3430- short source_polygon_index,
3431- short line_index,
3432- short destination_polygon_index,
3433- void *unused)
3434-{
3435- struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
3436-// struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
3437-// struct line_data *line= get_line_data(line_index);
3438- int32 cost= 1;
3439-
3440- (void) (unused);
3441- (void) (source_polygon_index);
3442- (void) (line_index);
3443-
3444- if (destination_polygon->type==_polygon_is_zone_border) cost= -1;
3445-
3446- return cost;
3447-}
3448-
3449-
3450-// LP: will set player view attributes when trying to shoot a guided projectile.
3451-void SetPlayerViewAttribs(int16 half_visual_arc, int16 half_vertical_visual_arc,
3452- world_distance visual_range, world_distance dark_visual_range)
3453-{
3454- // Added a modified version of AlexJS's changes: change only if necessary
3455- // Restoring AlexJLS's changes
3456- monster_definition& PlayerAsMonster = monster_definitions[_monster_marine];
3457- if (half_visual_arc > 0 || PlayerAsMonster.half_visual_arc > 0)
3458- PlayerAsMonster.half_visual_arc = half_visual_arc;
3459- if (half_vertical_visual_arc > 0 || PlayerAsMonster.half_vertical_visual_arc > 0)
3460- PlayerAsMonster.half_vertical_visual_arc = half_vertical_visual_arc;
3461- if (visual_range > 0 || PlayerAsMonster.visual_range > 0)
3462- PlayerAsMonster.visual_range = visual_range;
3463- if (dark_visual_range > 0 || PlayerAsMonster.dark_visual_range > 0)
3464- PlayerAsMonster.dark_visual_range = dark_visual_range;
3465-}
3466-
3467-
3468-uint8 *unpack_monster_data(uint8 *Stream, monster_data *Objects, size_t Count)
3469-{
3470- uint8* S = Stream;
3471- monster_data* ObjPtr = Objects;
3472-
3473- for (size_t k = 0; k < Count; k++, ObjPtr++)
3474- {
3475- StreamToValue(S,ObjPtr->type);
3476- StreamToValue(S,ObjPtr->vitality);
3477- StreamToValue(S,ObjPtr->flags);
3478-
3479- StreamToValue(S,ObjPtr->path);
3480- StreamToValue(S,ObjPtr->path_segment_length);
3481- StreamToValue(S,ObjPtr->desired_height);
3482-
3483- StreamToValue(S,ObjPtr->mode);
3484- StreamToValue(S,ObjPtr->action);
3485- StreamToValue(S,ObjPtr->target_index);
3486- StreamToValue(S,ObjPtr->external_velocity);
3487- StreamToValue(S,ObjPtr->vertical_velocity);
3488- StreamToValue(S,ObjPtr->ticks_since_attack);
3489- StreamToValue(S,ObjPtr->attack_repetitions);
3490- StreamToValue(S,ObjPtr->changes_until_lock_lost);
3491-
3492- StreamToValue(S,ObjPtr->elevation);
3493-
3494- StreamToValue(S,ObjPtr->object_index);
3495-
3496- StreamToValue(S,ObjPtr->ticks_since_last_activation);
3497-
3498- StreamToValue(S,ObjPtr->activation_bias);
3499-
3500- StreamToValue(S,ObjPtr->goal_polygon_index);
3501-
3502- StreamToValue(S,ObjPtr->sound_location.x);
3503- StreamToValue(S,ObjPtr->sound_location.y);
3504- StreamToValue(S,ObjPtr->sound_location.z);
3505- StreamToValue(S,ObjPtr->sound_polygon_index);
3506-
3507- StreamToValue(S,ObjPtr->random_desired_height);
3508-
3509- S += 7*2;
3510- }
3511-
3512- assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_data));
3513- return S;
3514-}
3515-
3516-uint8 *pack_monster_data(uint8 *Stream, monster_data *Objects, size_t Count)
3517-{
3518- uint8* S = Stream;
3519- monster_data* ObjPtr = Objects;
3520-
3521- for (size_t k = 0; k < Count; k++, ObjPtr++)
3522- {
3523- ValueToStream(S,ObjPtr->type);
3524- ValueToStream(S,ObjPtr->vitality);
3525- ValueToStream(S,ObjPtr->flags);
3526-
3527- ValueToStream(S,ObjPtr->path);
3528- ValueToStream(S,ObjPtr->path_segment_length);
3529- ValueToStream(S,ObjPtr->desired_height);
3530-
3531- ValueToStream(S,ObjPtr->mode);
3532- ValueToStream(S,ObjPtr->action);
3533- ValueToStream(S,ObjPtr->target_index);
3534- ValueToStream(S,ObjPtr->external_velocity);
3535- ValueToStream(S,ObjPtr->vertical_velocity);
3536- ValueToStream(S,ObjPtr->ticks_since_attack);
3537- ValueToStream(S,ObjPtr->attack_repetitions);
3538- ValueToStream(S,ObjPtr->changes_until_lock_lost);
3539-
3540- ValueToStream(S,ObjPtr->elevation);
3541-
3542- ValueToStream(S,ObjPtr->object_index);
3543-
3544- ValueToStream(S,ObjPtr->ticks_since_last_activation);
3545-
3546- ValueToStream(S,ObjPtr->activation_bias);
3547-
3548- ValueToStream(S,ObjPtr->goal_polygon_index);
3549-
3550- ValueToStream(S,ObjPtr->sound_location.x);
3551- ValueToStream(S,ObjPtr->sound_location.y);
3552- ValueToStream(S,ObjPtr->sound_location.z);
3553- ValueToStream(S,ObjPtr->sound_polygon_index);
3554-
3555- ValueToStream(S,ObjPtr->random_desired_height);
3556-
3557- S += 7*2;
3558- }
3559-
3560- assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_data));
3561- return S;
3562-}
3563-
3564-
3565-inline void StreamToAttackDef(uint8* &S, attack_definition& Object)
3566-{
3567- StreamToValue(S,Object.type);
3568- StreamToValue(S,Object.repetitions);
3569- StreamToValue(S,Object.error);
3570- StreamToValue(S,Object.range);
3571- StreamToValue(S,Object.attack_shape);
3572-
3573- StreamToValue(S,Object.dx);
3574- StreamToValue(S,Object.dy);
3575- StreamToValue(S,Object.dz);
3576-}
3577-
3578-inline void AttackDefToStream(uint8* &S, attack_definition& Object)
3579-{
3580- ValueToStream(S,Object.type);
3581- ValueToStream(S,Object.repetitions);
3582- ValueToStream(S,Object.error);
3583- ValueToStream(S,Object.range);
3584- ValueToStream(S,Object.attack_shape);
3585-
3586- ValueToStream(S,Object.dx);
3587- ValueToStream(S,Object.dy);
3588- ValueToStream(S,Object.dz);
3589-}
3590-
3591-
3592-uint8 *unpack_monster_definition(uint8 *Stream, size_t Count)
3593-{
3594- return unpack_monster_definition(Stream,monster_definitions,Count);
3595-}
3596-
3597-uint8 *unpack_monster_definition(uint8 *Stream, monster_definition* Objects, size_t Count)
3598-{
3599- uint8* S = Stream;
3600- monster_definition* ObjPtr = Objects;
3601-
3602- for (size_t k = 0; k < Count; k++, ObjPtr++)
3603- {
3604- StreamToValue(S,ObjPtr->collection);
3605-
3606- StreamToValue(S,ObjPtr->vitality);
3607- StreamToValue(S,ObjPtr->immunities);
3608- StreamToValue(S,ObjPtr->weaknesses);
3609- StreamToValue(S,ObjPtr->flags);
3610-
3611- StreamToValue(S,ObjPtr->_class);
3612- StreamToValue(S,ObjPtr->friends);
3613- StreamToValue(S,ObjPtr->enemies);
3614-
3615- StreamToValue(S,ObjPtr->sound_pitch);
3616- StreamToValue(S,ObjPtr->activation_sound);
3617- StreamToValue(S,ObjPtr->friendly_activation_sound);
3618- StreamToValue(S,ObjPtr->clear_sound);
3619- StreamToValue(S,ObjPtr->kill_sound);
3620- StreamToValue(S,ObjPtr->apology_sound);
3621- StreamToValue(S,ObjPtr->friendly_fire_sound);
3622- StreamToValue(S,ObjPtr->flaming_sound);
3623- StreamToValue(S,ObjPtr->random_sound);
3624- StreamToValue(S,ObjPtr->random_sound_mask);
3625-
3626- StreamToValue(S,ObjPtr->carrying_item_type);
3627-
3628- StreamToValue(S,ObjPtr->radius);
3629- StreamToValue(S,ObjPtr->height);
3630- StreamToValue(S,ObjPtr->preferred_hover_height);
3631- StreamToValue(S,ObjPtr->minimum_ledge_delta);
3632- StreamToValue(S,ObjPtr->maximum_ledge_delta);
3633- StreamToValue(S,ObjPtr->external_velocity_scale);
3634- StreamToValue(S,ObjPtr->impact_effect);
3635- StreamToValue(S,ObjPtr->melee_impact_effect);
3636- StreamToValue(S,ObjPtr->contrail_effect);
3637-
3638- StreamToValue(S,ObjPtr->half_visual_arc);
3639- StreamToValue(S,ObjPtr->half_vertical_visual_arc);
3640- StreamToValue(S,ObjPtr->visual_range);
3641- StreamToValue(S,ObjPtr->dark_visual_range);
3642- StreamToValue(S,ObjPtr->intelligence);
3643- StreamToValue(S,ObjPtr->speed);
3644- StreamToValue(S,ObjPtr->gravity);
3645- StreamToValue(S,ObjPtr->terminal_velocity);
3646- StreamToValue(S,ObjPtr->door_retry_mask);
3647- StreamToValue(S,ObjPtr->shrapnel_radius);
3648- S = unpack_damage_definition(S,&ObjPtr->shrapnel_damage,1);
3649-
3650- StreamToValue(S,ObjPtr->hit_shapes);
3651- StreamToValue(S,ObjPtr->hard_dying_shape);
3652- StreamToValue(S,ObjPtr->soft_dying_shape);
3653- StreamToValue(S,ObjPtr->hard_dead_shapes);
3654- StreamToValue(S,ObjPtr->soft_dead_shapes);
3655- StreamToValue(S,ObjPtr->stationary_shape);
3656- StreamToValue(S,ObjPtr->moving_shape);
3657- StreamToValue(S,ObjPtr->teleport_in_shape);
3658- StreamToValue(S,ObjPtr->teleport_out_shape);
3659-
3660- StreamToValue(S,ObjPtr->attack_frequency);
3661- StreamToAttackDef(S,ObjPtr->melee_attack);
3662- StreamToAttackDef(S,ObjPtr->ranged_attack);
3663- }
3664-
3665- assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_definition));
3666- return S;
3667-}
3668-
3669-uint8* unpack_m1_monster_definition(uint8 *Stream, size_t Count)
3670-{
3671- uint8* S = Stream;
3672- monster_definition* ObjPtr = monster_definitions;
3673-
3674- for (size_t k = 0; k < Count; k++, ObjPtr++)
3675- {
3676- StreamToValue(S, ObjPtr->collection);
3677-
3678- StreamToValue(S, ObjPtr->vitality);
3679- StreamToValue(S, ObjPtr->immunities);
3680- StreamToValue(S, ObjPtr->weaknesses);
3681- StreamToValue(S, ObjPtr->flags);
3682-
3683- StreamToValue(S, ObjPtr->_class);
3684- StreamToValue(S, ObjPtr->friends);
3685- StreamToValue(S, ObjPtr->enemies);
3686-
3687- ObjPtr->sound_pitch = FIXED_ONE;
3688- StreamToValue(S, ObjPtr->activation_sound);
3689- S += 2; // ignore conversation sound
3690-
3691- // Marathon doesn't have these
3692- ObjPtr->friendly_activation_sound = NONE;
3693- ObjPtr->clear_sound = NONE;
3694- ObjPtr->kill_sound = NONE;
3695- ObjPtr->apology_sound = NONE;
3696- ObjPtr->friendly_fire_sound = NONE;
3697-
3698- StreamToValue(S, ObjPtr->flaming_sound);
3699- StreamToValue(S, ObjPtr->random_sound);
3700- StreamToValue(S, ObjPtr->random_sound_mask);
3701-
3702- StreamToValue(S, ObjPtr->carrying_item_type);
3703-
3704- StreamToValue(S, ObjPtr->radius);
3705- StreamToValue(S, ObjPtr->height);
3706- StreamToValue(S, ObjPtr->preferred_hover_height);
3707- StreamToValue(S, ObjPtr->minimum_ledge_delta);
3708- StreamToValue(S, ObjPtr->maximum_ledge_delta);
3709- StreamToValue(S, ObjPtr->external_velocity_scale);
3710-
3711- StreamToValue(S, ObjPtr->impact_effect);
3712- StreamToValue(S, ObjPtr->melee_impact_effect);
3713- ObjPtr->contrail_effect = NONE;
3714-
3715- StreamToValue(S,ObjPtr->half_visual_arc);
3716- StreamToValue(S,ObjPtr->half_vertical_visual_arc);
3717- StreamToValue(S,ObjPtr->visual_range);
3718- StreamToValue(S,ObjPtr->dark_visual_range);
3719- StreamToValue(S,ObjPtr->intelligence);
3720- StreamToValue(S,ObjPtr->speed);
3721- StreamToValue(S,ObjPtr->gravity);
3722- StreamToValue(S,ObjPtr->terminal_velocity);
3723- StreamToValue(S,ObjPtr->door_retry_mask);
3724- StreamToValue(S,ObjPtr->shrapnel_radius);
3725-
3726- S = unpack_damage_definition(S, &ObjPtr->shrapnel_damage, 1);
3727-
3728- StreamToValue(S,ObjPtr->hit_shapes);
3729- StreamToValue(S,ObjPtr->hard_dying_shape);
3730- StreamToValue(S,ObjPtr->soft_dying_shape);
3731- StreamToValue(S,ObjPtr->hard_dead_shapes);
3732- StreamToValue(S,ObjPtr->soft_dead_shapes);
3733- StreamToValue(S,ObjPtr->stationary_shape);
3734- StreamToValue(S,ObjPtr->moving_shape);
3735-
3736- ObjPtr->teleport_in_shape = ObjPtr->stationary_shape;
3737- ObjPtr->teleport_out_shape = ObjPtr->teleport_out_shape;
3738-
3739- StreamToValue(S, ObjPtr->attack_frequency);
3740- StreamToAttackDef(S, ObjPtr->melee_attack);
3741- StreamToAttackDef(S, ObjPtr->ranged_attack);
3742- }
3743-
3744- return S;
3745-}
3746-
3747-uint8 *pack_monster_definition(uint8 *Stream, size_t Count)
3748-{
3749- return pack_monster_definition(Stream,monster_definitions,Count);
3750-}
3751-
3752-uint8 *pack_monster_definition(uint8 *Stream, monster_definition *Objects, size_t Count)
3753-{
3754- uint8* S = Stream;
3755- monster_definition* ObjPtr = Objects;
3756-
3757- for (size_t k = 0; k < Count; k++, ObjPtr++)
3758- {
3759- ValueToStream(S,ObjPtr->collection);
3760-
3761- ValueToStream(S,ObjPtr->vitality);
3762- ValueToStream(S,ObjPtr->immunities);
3763- ValueToStream(S,ObjPtr->weaknesses);
3764- ValueToStream(S,ObjPtr->flags);
3765-
3766- ValueToStream(S,ObjPtr->_class);
3767- ValueToStream(S,ObjPtr->friends);
3768- ValueToStream(S,ObjPtr->enemies);
3769-
3770- ValueToStream(S,ObjPtr->sound_pitch);
3771- ValueToStream(S,ObjPtr->activation_sound);
3772- ValueToStream(S,ObjPtr->friendly_activation_sound);
3773- ValueToStream(S,ObjPtr->clear_sound);
3774- ValueToStream(S,ObjPtr->kill_sound);
3775- ValueToStream(S,ObjPtr->apology_sound);
3776- ValueToStream(S,ObjPtr->friendly_fire_sound);
3777- ValueToStream(S,ObjPtr->flaming_sound);
3778- ValueToStream(S,ObjPtr->random_sound);
3779- ValueToStream(S,ObjPtr->random_sound_mask);
3780-
3781- ValueToStream(S,ObjPtr->carrying_item_type);
3782-
3783- ValueToStream(S,ObjPtr->radius);
3784- ValueToStream(S,ObjPtr->height);
3785- ValueToStream(S,ObjPtr->preferred_hover_height);
3786- ValueToStream(S,ObjPtr->minimum_ledge_delta);
3787- ValueToStream(S,ObjPtr->maximum_ledge_delta);
3788- ValueToStream(S,ObjPtr->external_velocity_scale);
3789- ValueToStream(S,ObjPtr->impact_effect);
3790- ValueToStream(S,ObjPtr->melee_impact_effect);
3791- ValueToStream(S,ObjPtr->contrail_effect);
3792-
3793- ValueToStream(S,ObjPtr->half_visual_arc);
3794- ValueToStream(S,ObjPtr->half_vertical_visual_arc);
3795- ValueToStream(S,ObjPtr->visual_range);
3796- ValueToStream(S,ObjPtr->dark_visual_range);
3797- ValueToStream(S,ObjPtr->intelligence);
3798- ValueToStream(S,ObjPtr->speed);
3799- ValueToStream(S,ObjPtr->gravity);
3800- ValueToStream(S,ObjPtr->terminal_velocity);
3801- ValueToStream(S,ObjPtr->door_retry_mask);
3802- ValueToStream(S,ObjPtr->shrapnel_radius);
3803- S = pack_damage_definition(S,&ObjPtr->shrapnel_damage,1);
3804-
3805- ValueToStream(S,ObjPtr->hit_shapes);
3806- ValueToStream(S,ObjPtr->hard_dying_shape);
3807- ValueToStream(S,ObjPtr->soft_dying_shape);
3808- ValueToStream(S,ObjPtr->hard_dead_shapes);
3809- ValueToStream(S,ObjPtr->soft_dead_shapes);
3810- ValueToStream(S,ObjPtr->stationary_shape);
3811- ValueToStream(S,ObjPtr->moving_shape);
3812- ValueToStream(S,ObjPtr->teleport_in_shape);
3813- ValueToStream(S,ObjPtr->teleport_out_shape);
3814-
3815- ValueToStream(S,ObjPtr->attack_frequency);
3816- AttackDefToStream(S,ObjPtr->melee_attack);
3817- AttackDefToStream(S,ObjPtr->ranged_attack);
3818- }
3819-
3820- assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_definition));
3821- return S;
3822-}
3823-
3824-void init_monster_definitions()
3825-{
3826- memcpy(monster_definitions, original_monster_definitions, sizeof(monster_definitions));
3827-}
3828-
3829-struct damage_kick_definition *original_damage_kick_definitions = NULL;
3830-class XML_DamageKickParser: public XML_ElementParser
3831-{
3832- short Index;
3833- damage_kick_definition Data;
3834-
3835- // What is present?
3836- bool IndexPresent;
3837- enum {NumberOfValues = 3};
3838- bool IsPresent[NumberOfValues];
3839-
3840-public:
3841- bool Start();
3842- bool HandleAttribute(const char *Tag, const char *Value);
3843- bool AttributesDone();
3844- bool ResetValues();
3845-
3846- XML_DamageKickParser(): XML_ElementParser("kick") {}
3847-};
3848-
3849-bool XML_DamageKickParser::Start()
3850-{
3851- // back up old values first
3852- if (!original_damage_kick_definitions) {
3853- original_damage_kick_definitions = (struct damage_kick_definition *) malloc(sizeof(struct damage_kick_definition) * NUMBER_OF_DAMAGE_TYPES);
3854- assert(original_damage_kick_definitions);
3855- for (unsigned i = 0; i < NUMBER_OF_DAMAGE_TYPES; i++)
3856- original_damage_kick_definitions[i] = damage_kick_definitions[i];
3857- }
3858-
3859- IndexPresent = false;
3860- for (int k=0; k<NumberOfValues; k++)
3861- IsPresent[k] = false;
3862-
3863- return true;
3864-}
3865-
3866-bool XML_DamageKickParser::HandleAttribute(const char *Tag, const char *Value)
3867-{
3868- if (StringsEqual(Tag,"index"))
3869- {
3870- if (ReadBoundedInt16Value(Value,Index,0,NUMBER_OF_DAMAGE_TYPES-1))
3871- {
3872- IndexPresent = true;
3873- return true;
3874- }
3875- else return false;
3876- }
3877- else if (StringsEqual(Tag,"base"))
3878- {
3879- if (ReadInt16Value(Value,Data.base_value))
3880- {
3881- IsPresent[0] = true;
3882- return true;
3883- }
3884- else return false;
3885- }
3886- else if (StringsEqual(Tag,"mult"))
3887- {
3888- if (ReadFloatValue(Value,Data.delta_vitality_multiplier))
3889- {
3890- IsPresent[1] = true;
3891- return true;
3892- }
3893- else return false;
3894- }
3895- else if (StringsEqual(Tag,"vertical"))
3896- {
3897- if (ReadBooleanValue(Value,Data.is_also_vertical))
3898- {
3899- IsPresent[2] = true;
3900- return true;
3901- }
3902- else return false;
3903- }
3904- UnrecognizedTag();
3905- return false;
3906-}
3907-
3908-bool XML_DamageKickParser::AttributesDone()
3909-{
3910- // Verify...
3911- if (!IndexPresent)
3912- {
3913- AttribsMissing();
3914- return false;
3915- }
3916- damage_kick_definition& OrigData = damage_kick_definitions[Index];
3917-
3918- if (IsPresent[0]) OrigData.base_value = Data.base_value;
3919- if (IsPresent[1]) OrigData.delta_vitality_multiplier = Data.delta_vitality_multiplier;
3920- if (IsPresent[2]) OrigData.is_also_vertical = Data.is_also_vertical;
3921-
3922- return true;
3923-}
3924-
3925-bool XML_DamageKickParser::ResetValues()
3926-{
3927- if (original_damage_kick_definitions) {
3928- for (unsigned i = 0; i < NUMBER_OF_DAMAGE_TYPES; i++)
3929- damage_kick_definitions[i] = original_damage_kick_definitions[i];
3930- free(original_damage_kick_definitions);
3931- original_damage_kick_definitions = NULL;
3932- }
3933- return true;
3934-}
3935-
3936-static XML_DamageKickParser DamageKickParser;
3937-
3938-
3939-static XML_ElementParser DamageKicksParser("damage_kicks");
3940-
3941-
3942-// XML-parser support
3943-XML_ElementParser *DamageKicks_GetParser()
3944-{
3945- DamageKicksParser.AddChild(&DamageKickParser);
3946-
3947- return &DamageKicksParser;
3948-}
1+/*
2+MONSTERS.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+Tuesday, November 10, 1992 1:10:20 PM
22+
23+Friday, May 27, 1994 11:21:07 AM
24+ split into MONSTERS.C, PROJECTILES.C and EFFECTS.C; unified active_monster and monster array.
25+Friday, September 30, 1994 5:48:25 PM (Jason)
26+ started adding comments again. damage_monsters_in_radius() is less forgiving in z now.
27+Monday, December 5, 1994 9:07:37 PM (Jason)
28+ rebellion environment function (all _clients hate all _pfhor).
29+Wednesday, February 1, 1995 2:29:01 AM (Jason')
30+ kill_sounds; invisible monsters donユt move
31+Wednesday, June 14, 1995 10:14:24 AM (Jason)
32+ rewrite for marathon2 (halfway done).
33+Monday, July 10, 1995 11:49:06 AM (Jason)
34+ rewrite for marathon2 done. my bobs wonユt listen to your fucking whining.
35+
36+Jan 30, 2000 (Loren Petrich):
37+ Added some typecasts
38+ Removed some "static" declarations that conflict with "extern"
39+
40+Feb 3, 2000 (Loren Petrich):
41+ Treating Jjaro goo like sewage
42+
43+Feb. 4, 2000 (Loren Petrich):
44+ Changed halt() to assert(false) for better debugging
45+
46+Feb 6, 2000 (Loren Petrich):
47+ Added access to size of monster-definition structure
48+
49+Feb 12, 2000 (Loren Petrich):
50+ Suppressed an exposed "dprintf" as an unnecessary interrupt.
51+
52+Feb 16, 2000 (Loren Petrich):
53+ Added a check on the polygon index after a line-transparency check;
54+ this is in case there is no polygon on the other side.
55+
56+Feb 17, 2000 (Loren Petrich):
57+ Fixed stuff near GUESS_HYPOTENUSE() to be long-distance-friendly
58+
59+Feb 19, 2000 (Loren Petrich):
60+ Added growable lists of indices of objects to be checked for collisions
61+
62+Feb 24, 2000 (Loren Petrich):
63+ Suppressed some asserts about monster speeds
64+
65+Apr 27, 2000 (Loren Petrich):
66+ Added some behavior in the case of a monster both floating and flying
67+ to handle the map "Aqualung" correctly.
68+
69+May 29, 2000 (Loren Petirch):
70+ Fixed side effect of fixing keyframe-never-zero bug:
71+ if the keyframe is zero, then a sequence never triggers shrapnel damage.
72+ Thus, Hunters die a soft death more harmlessly.
73+
74+Jun 11, 2000 (Loren Petrich):
75+ Pegging health and oxygen to maximum values when damaged;
76+ takes into account negative damage from healing projectiles.
77+
78+Jul 1, 2000 (Loren Petrich):
79+ Inlined the accessors
80+
81+Aug 30, 2000 (Loren Petrich):
82+ Added stuff for unpacking and packing
83+
84+Oct 13, 2000 (Loren Petrich)
85+ Converted the intersected-objects list into a Standard Template Library vector
86+
87+Oct 26, 2000 (Mark Levin)
88+ Revealed a few functions needed by Pfhortran
89+
90+Jan 12, 2003 (Loren Petrich)
91+ Added controllable damage kicks
92+*/
93+
94+#include <string.h>
95+#include <limits.h>
96+
97+#include "cseries.h"
98+#include "map.h"
99+#include "render.h"
100+#include "interface.h"
101+#include "FilmProfile.h"
102+#include "flood_map.h"
103+#include "effects.h"
104+#include "monsters.h"
105+#include "projectiles.h"
106+#include "player.h"
107+#include "platforms.h"
108+#include "scenery.h"
109+#include "SoundManager.h"
110+#include "fades.h"
111+#include "items.h"
112+#include "media.h"
113+#include "Packing.h"
114+#include "lua_script.h"
115+#include "Logging.h"
116+
117+
118+#ifdef env68k
119+#pragma segment objects
120+#endif
121+
122+/*
123+//explosive deaths should cause damage during their key frame
124+*/
125+
126+/* ---------- sounds */
127+
128+/* ---------- constants */
129+
130+#define OBSTRUCTION_DEACTIVATION_MASK 0x7
131+
132+#define EVASIVE_MANOUVER_DISTANCE WORLD_ONE_HALF
133+
134+#define MONSTER_EXTERNAL_DECELERATION (WORLD_ONE/200)
135+#define MONSTER_MINIMUM_EXTERNAL_VELOCITY (10*MONSTER_EXTERNAL_DECELERATION)
136+#define MONSTER_MAXIMUM_EXTERNAL_VELOCITY (TICKS_PER_SECOND*MONSTER_EXTERNAL_DECELERATION)
137+
138+/* the height below which we donユt bother to float up a ledge (we just run right over it) */
139+#define MINIMUM_FLOATING_HEIGHT WORLD_ONE_FOURTH
140+
141+#define MINIMUM_ACTIVATION_SEPARATION TICKS_PER_SECOND
142+
143+/* when looking for things under or at this light intensity the monster must use his dark visual range */
144+#define LOW_LIGHT_INTENSITY 0
145+
146+/* maximum area we will search out to find a new target */
147+#define MAXIMUM_TARGET_SEARCH_AREA (7*WORLD_ONE*WORLD_ONE)
148+
149+#define MONSTER_PLATFORM_BUFFER_DISTANCE (WORLD_ONE/8)
150+
151+#define GLUE_TRIGGER_ACTIVATION_RANGE (8*WORLD_ONE)
152+#define MONSTER_ALERT_ACTIVATION_RANGE (5*WORLD_ONE)
153+
154+#define MONSTER_PATHFINDING_OBSTRUCTION_COST (2*WORLD_ONE*WORLD_ONE)
155+#define MONSTER_PATHFINDING_PLATFORM_COST (4*WORLD_ONE*WORLD_ONE)
156+#define MINIMUM_MONSTER_PATHFINDING_POLYGON_AREA (WORLD_ONE)
157+
158+#define TERMINAL_VERTICAL_MONSTER_VELOCITY (WORLD_ONE/5)
159+
160+#define MINIMUM_DYING_EXTERNAL_VELOCITY (WORLD_ONE/8)
161+
162+#define CIVILIANS_KILLED_BY_PLAYER_THRESHHOLD 3
163+#define CIVILIANS_KILLED_DECREMENT_MASK 0x1ff
164+
165+enum /* monster attitudes, extracted from enemies and friends bitfields by get_monster_attitude() */
166+{
167+ _neutral,
168+ _friendly,
169+ _hostile
170+};
171+
172+enum /* returned by find_obstructing_terrain_feature() */
173+{
174+ _standing_on_sniper_ledge,
175+ _entering_platform_polygon,
176+ _leaving_platform_polygon,
177+ _flying_or_floating_transition
178+};
179+
180+#define MINIMUM_SNIPER_ELEVATION WORLD_ONE_HALF
181+
182+/* ---------- structures */
183+
184+struct monster_pathfinding_data
185+{
186+ struct monster_definition *definition;
187+ struct monster_data *monster;
188+
189+ bool cross_zone_boundaries;
190+};
191+
192+// How much external velocity is imparted by some damage?
193+struct damage_kick_definition
194+{
195+ short base_value;
196+ float delta_vitality_multiplier;
197+ bool is_also_vertical;
198+
199+ // if non-zero, will enable vertical_component if
200+ // delta_vitality is greater than threshold
201+ short vertical_threshold;
202+};
203+
204+/* ---------- definitions */
205+
206+// LP: implements commented-out damage-kick code
207+struct damage_kick_definition damage_kick_definitions[NUMBER_OF_DAMAGE_TYPES] =
208+{
209+ {0, 1, true, 0}, // _damage_explosion,
210+ {0, 3, true, 0}, // _damage_electrical_staff,
211+ {0, 1, false, 0}, // _damage_projectile,
212+ {0, 1, false, 0}, // _damage_absorbed,
213+ {0, 1, false, 0}, // _damage_flame,
214+ {0, 1, false, 0}, // _damage_hound_claws,
215+ {0, 1, false, 0}, // _damage_alien_projectile,
216+ {0, 1, false, 0}, // _damage_hulk_slap,
217+ {0, 3, true, 0}, // _damage_compiler_bolt,
218+ {0, 0, false, 100}, // _damage_fusion_bolt,
219+ {0, 1, false, 0}, // _damage_hunter_bolt,
220+ {0, 1, false, 0}, // _damage_fist,
221+ {250, 0, false, 0}, // _damage_teleporter,
222+ {0, 1, false, 0}, // _damage_defender,
223+ {0, 3, true, 0}, // _damage_yeti_claws,
224+ {0, 1, false, 0}, // _damage_yeti_projectile,
225+ {0, 1, false, 0}, // _damage_crushing,
226+ {0, 1, false, 0}, // _damage_lava,
227+ {0, 1, false, 0}, // _damage_suffocation,
228+ {0, 1, false, 0}, // _damage_goo,
229+ {0, 1, false, 0}, // _damage_energy_drain,
230+ {0, 1, false, 0}, // _damage_oxygen_drain,
231+ {0, 1, false, 0}, // _damage_hummer_bolt,
232+ {0, 0, true, 0} // _damage_shotgun_projectile,
233+};
234+
235+/* ---------- globals */
236+
237+/* import monster definition constants, structures and globals */
238+#include "monster_definitions.h"
239+
240+// LP addition: growable list of intersected objects
241+static vector<short> IntersectedObjects;
242+
243+/* ---------- private prototypes */
244+
245+static monster_definition *get_monster_definition(
246+ const short type);
247+
248+static void monster_needs_path(short monster_index, bool immediately);
249+static void generate_new_path_for_monster(short monster_index);
250+void advance_monster_path(short monster_index);
251+
252+static short get_monster_attitude(short monster_index, short target_index);
253+void change_monster_target(short monster_index, short target_index);
254+static bool switch_target_check(short monster_index, short attacker_index, short delta_vitality);
255+static bool clear_line_of_sight(short viewer_index, short target_index, bool full_circle);
256+
257+static void handle_moving_or_stationary_monster(short monster_index);
258+static void execute_monster_attack(short monster_index);
259+static void kill_monster(short monster_index);
260+static bool translate_monster(short monster_index, world_distance distance);
261+static bool try_monster_attack(short monster_index);
262+
263+int32 monster_pathfinding_cost_function(short source_polygon_index, short line_index,
264+ short destination_polygon_index, void *data);
265+
266+void set_monster_action(short monster_index, short action);
267+void set_monster_mode(short monster_index, short new_mode, short target_index);
268+
269+static short find_obstructing_terrain_feature(short monster_index, short *feature_index, short *relevant_polygon_index);
270+
271+static short position_monster_projectile(short aggressor_index, short target_index, struct attack_definition *attack,
272+ world_point3d *origin, world_point3d *destination, world_point3d *_vector, angle theta);
273+
274+static void update_monster_vertical_physics_model(short monster_index);
275+static void update_monster_physics_model(short monster_index);
276+
277+static int32 monster_activation_flood_proc(short source_polygon_index, short line_index,
278+ short destination_polygon_index, void *data);
279+
280+static bool attempt_evasive_manouvers(short monster_index);
281+
282+static short nearest_goal_polygon_index(short polygon_index);
283+static int32 nearest_goal_cost_function(short source_polygon_index, short line_index,
284+ short destination_polygon_index, void *unused);
285+
286+static void cause_shrapnel_damage(short monster_index);
287+
288+// For external use
289+monster_definition *get_monster_definition_external(const short type);
290+
291+/* ---------- code */
292+
293+monster_data *get_monster_data(
294+ short monster_index)
295+{
296+ struct monster_data *monster = GetMemberWithBounds(monsters,monster_index,MAXIMUM_MONSTERS_PER_MAP);
297+
298+ vassert(monster, csprintf(temporary, "monster index #%d is out of range", monster_index));
299+ vassert(SLOT_IS_USED(monster), csprintf(temporary, "monster index #%d (%p) is unused", monster_index, monster));
300+
301+ return monster;
302+}
303+
304+monster_definition *get_monster_definition(
305+ const short type)
306+{
307+ monster_definition *definition = GetMemberWithBounds(monster_definitions,type,NUMBER_OF_MONSTER_TYPES);
308+ assert(definition);
309+
310+ return definition;
311+}
312+
313+//a non-inlined version for external use
314+monster_definition *get_monster_definition_external(
315+ const short type)
316+{
317+ return get_monster_definition(type);
318+}
319+
320+
321+/* returns new monster index if successful, NONE otherwise */
322+short new_monster(
323+ struct object_location *location,
324+ short monster_type)
325+{
326+ struct monster_definition *definition= get_monster_definition(monster_type);
327+ short original_monster_type= monster_type;
328+ struct monster_data *monster;
329+ short drop_mask= NONE;
330+ short monster_index= NONE;
331+ short flags= _monster_has_never_been_activated;
332+
333+ switch (dynamic_world->game_information.difficulty_level)
334+ {
335+ case _wuss_level: drop_mask= 3; break; /* drop every fourth monster */
336+ case _easy_level: drop_mask= 7; break; /* drop every eighth monster */
337+ /* otherwise, drop no monsters */
338+ }
339+
340+ if ((definition->flags&_monster_cannot_be_dropped) || !(definition->flags&_monster_is_alien) || drop_mask==NONE || (++dynamic_world->new_monster_vanishing_cookie&drop_mask))
341+ {
342+ /* check to see if we should promote or demote this monster based on difficulty level */
343+ if (definition->flags&_monster_major)
344+ {
345+ short demote_mask= NONE;
346+
347+ switch (dynamic_world->game_information.difficulty_level)
348+ {
349+ case _wuss_level: demote_mask= 1; break; /* demote every other major */
350+ case _easy_level: demote_mask= 3; break; /* demote every fourth major */
351+ /* otherwise, demote no monsters */
352+ }
353+
354+ if (demote_mask!=NONE && !(++dynamic_world->new_monster_mangler_cookie&demote_mask)) definition= get_monster_definition(monster_type-= 1), flags|= _monster_was_demoted;
355+ }
356+ else
357+ {
358+ if (definition->flags&_monster_minor)
359+ {
360+ short promote_mask= NONE;
361+
362+ switch (dynamic_world->game_information.difficulty_level)
363+ {
364+ case _major_damage_level: promote_mask= 1; break; /* promote every other minor */
365+ case _total_carnage_level: promote_mask= 0; break; /* promote every minor */
366+ /* otherwise, promote no monsters */
367+ }
368+ if (promote_mask!=NONE && !(++dynamic_world->new_monster_mangler_cookie&promote_mask)) definition= get_monster_definition(monster_type+= 1), flags|= _monster_was_promoted;
369+ }
370+ }
371+
372+ for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster_index,++monster)
373+ {
374+ if (SLOT_IS_FREE(monster))
375+ {
376+ short object_index= new_map_object(location, BUILD_DESCRIPTOR(definition->collection, definition->stationary_shape));
377+
378+ if (object_index!=NONE)
379+ {
380+ struct object_data *object= get_object_data(object_index);
381+
382+ /* not doing this in !DEBUG resulted in sync errors; mmm... random data, so tasty */
383+ obj_set(*monster, 0x80);
384+
385+ if (location->flags&_map_object_is_blind) flags|= _monster_is_blind;
386+ if (location->flags&_map_object_is_deaf) flags|= _monster_is_deaf;
387+ if (location->flags&_map_object_floats) flags|= _monster_teleports_out_when_deactivated;
388+
389+ /* initialize the monster_data structure; we donユt touch most of the fields here
390+ because the monster is initially inactive (and they will be initialized when the
391+ monster is activated) */
392+ monster->type= monster_type;
393+ monster->activation_bias= DECODE_ACTIVATION_BIAS(location->flags);
394+ monster->vitality= NONE; /* if a monster is activated with vitality==NONE, it will be properly initialized */
395+ monster->object_index= object_index;
396+ monster->flags= flags;
397+ monster->goal_polygon_index= monster->activation_bias==_activate_on_goal ?
398+ nearest_goal_polygon_index(location->polygon_index) : NONE;
399+ monster->sound_polygon_index= object->polygon;
400+ monster->sound_location= object->location;
401+ MARK_SLOT_AS_USED(monster);
402+
403+ /* initialize the monsterユs object */
404+ if (definition->flags&_monster_is_invisible) object->transfer_mode= _xfer_invisibility;
405+ if (definition->flags&_monster_is_subtly_invisible) object->transfer_mode= _xfer_subtle_invisibility;
406+ if (definition->flags&_monster_is_enlarged) object->flags|= _object_is_enlarged;
407+ if (definition->flags&_monster_is_tiny) object->flags|= _object_is_tiny;
408+ SET_OBJECT_SOLIDITY(object, true);
409+ SET_OBJECT_OWNER(object, _object_is_monster);
410+ object->permutation= monster_index;
411+ object->sound_pitch= definition->sound_pitch;
412+
413+ /* make sure the object frequency stuff keeps track of how many monsters are
414+ on the map */
415+ object_was_just_added(_object_is_monster, original_monster_type);
416+ }
417+ else
418+ {
419+ monster_index= NONE;
420+ }
421+
422+ break;
423+ }
424+ }
425+ if (monster_index==MAXIMUM_MONSTERS_PER_MAP) monster_index= NONE;
426+ }
427+
428+ /* keep track of how many civilians we drop on this level */
429+// if (monster_index!=NONE && (definition->_class&_class_human_civilian)) dynamic_world->current_civilian_count+= 1;
430+
431+ return monster_index;
432+}
433+
434+/* assumes カt==1 tick */
435+void move_monsters(
436+ void)
437+{
438+ struct monster_data *monster;
439+ bool monster_got_time= false;
440+ bool monster_built_path= (dynamic_world->tick_count&3) ? true : false;
441+ short monster_index;
442+
443+ for (monster_index= 0, monster= monsters; monster_index<MAXIMUM_MONSTERS_PER_MAP; ++monster_index, ++monster)
444+ {
445+ if (SLOT_IS_USED(monster) && !MONSTER_IS_PLAYER(monster))
446+ {
447+ struct object_data *object= get_object_data(monster->object_index);
448+
449+ if (MONSTER_IS_ACTIVE(monster))
450+ {
451+ if (!OBJECT_IS_INVISIBLE(object))
452+ {
453+ struct monster_definition *definition= get_monster_definition(monster->type);
454+ short animation_flags;
455+
456+ // AlexJLS patch: effect of dangerous polygons
457+ cause_polygon_damage(object->polygon,monster_index);
458+
459+ /* clear the recovering from hit flag, mark the monster as not idle */
460+ SET_MONSTER_IDLE_STATUS(monster, false);
461+
462+ update_monster_vertical_physics_model(monster_index);
463+
464+ /* update our objectユs animation unless weユre ヤsufferingユ from an external velocity
465+ or weユre airborne (if weユre a flying or floating monster, ignore both of these */
466+ if ((!monster->external_velocity&&!monster->vertical_velocity) ||
467+ (film_profile.ketchup_fix && (monster->action==_monster_is_attacking_close||monster->action==_monster_is_attacking_far)) ||
468+ ((monster->action!=_monster_is_being_hit||!monster->external_velocity) && (definition->flags&(_monster_floats|_monster_flys))))
469+ {
470+ animate_object(monster->object_index);
471+ }
472+ animation_flags= GET_OBJECT_ANIMATION_FLAGS(object);
473+
474+ /* give this monster time, if we can and he needs it */
475+ if (!monster_got_time && monster_index>dynamic_world->last_monster_index_to_get_time && !MONSTER_IS_DYING(monster))
476+ {
477+ switch (monster->mode)
478+ {
479+ case _monster_unlocked:
480+ /* if this monster is unlocked and we havenユt already given a monster time,
481+ call find_closest_appropriate_target() */
482+ change_monster_target(monster_index, find_closest_appropriate_target(monster_index, false));
483+ monster_got_time= true;
484+ break;
485+
486+ case _monster_lost_lock:
487+ case _monster_losing_lock:
488+ /* if this monster has lost or is losing lock and we havenユt already given a monster
489+ time, check to see if his target has become visible again */
490+ if (clear_line_of_sight(monster_index, monster->target_index, false))
491+ {
492+ change_monster_target(monster_index, monster->target_index);
493+ }
494+ monster_got_time= true;
495+ break;
496+ }
497+
498+ /* if we gave this guy time, make room for the next guy */
499+ if (monster_got_time) dynamic_world->last_monster_index_to_get_time= monster_index;
500+ }
501+
502+ /* if this monster needs a path, generate one (unless weユve already generated a
503+ path this frame in which case weユll wait until next frame, UNLESS the monster
504+ has no path in which case it needs one regardless) */
505+ if (MONSTER_NEEDS_PATH(monster) && !MONSTER_IS_DYING(monster) && !MONSTER_IS_ATTACKING(monster) &&
506+ ((!monster_built_path && monster_index>dynamic_world->last_monster_index_to_build_path) || monster->path==NONE))
507+ {
508+ generate_new_path_for_monster(monster_index);
509+ if (!monster_built_path)
510+ {
511+ monster_built_path= true;
512+ dynamic_world->last_monster_index_to_build_path= monster_index;
513+ }
514+ }
515+
516+ /* itユs possible that we couldnユt get where we wanted to go, or that we arrived there
517+ and deactivated ourselves; if this happens we donユt want to continue processing
518+ the monster as if it were active */
519+ if (MONSTER_IS_ACTIVE(monster))
520+ {
521+ /* move the monster; check to see if we can attack; resolve modes ending; etc. */
522+ switch (monster->action)
523+ {
524+ case _monster_is_waiting_to_attack_again:
525+ case _monster_is_stationary:
526+ case _monster_is_moving:
527+ handle_moving_or_stationary_monster(monster_index);
528+ break;
529+
530+ case _monster_is_attacking_close:
531+ case _monster_is_attacking_far:
532+ if (animation_flags&_obj_keyframe_started) execute_monster_attack(monster_index);
533+ if (animation_flags&_obj_last_frame_animated)
534+ {
535+ if (((monster->attack_repetitions-=1)<0) || !try_monster_attack(monster_index))
536+ {
537+ /* after an attack has been initiated successfully we need to return to
538+ _monster_is_moving action, kill our path and ask for a new one
539+ (because weユre pointed in the wrong direction now) */
540+ set_monster_action(monster_index,
541+ (monster->attack_repetitions<0 && (definition->flags&_monster_waits_with_clear_shot) && MONSTER_IS_LOCKED(monster)) ?
542+ _monster_is_waiting_to_attack_again : _monster_is_moving);
543+ monster_needs_path(monster_index, true);
544+ monster->ticks_since_attack= 0;
545+ }
546+ }
547+ break;
548+
549+ case _monster_is_teleporting_in:
550+ if (animation_flags&_obj_last_frame_animated)
551+ {
552+ monster->action= _monster_is_moving;
553+ set_monster_action(monster_index, _monster_is_moving);
554+ change_monster_target(monster_index, find_closest_appropriate_target(monster_index, false));
555+ }
556+ break;
557+ case _monster_is_teleporting_out:
558+ if (animation_flags&_obj_keyframe_started)
559+ {
560+ monster->action= _monster_is_dying_soft; // to prevent aggressors from relocking
561+ monster_died(monster_index);
562+ teleport_object_out(monster->object_index);
563+ remove_map_object(monster->object_index);
564+ L_Invalidate_Monster(monster_index);
565+ MARK_SLOT_AS_FREE(monster);
566+ }
567+ break;
568+
569+ case _monster_is_being_hit:
570+ update_monster_physics_model(monster_index);
571+ if (animation_flags&_obj_last_frame_animated)
572+ {
573+ monster_needs_path(monster_index, true);
574+ set_monster_action(monster_index, _monster_is_moving);
575+ monster->external_velocity= 0;
576+ }
577+ break;
578+
579+ case _monster_is_dying_soft:
580+ case _monster_is_dying_hard:
581+ case _monster_is_dying_flaming:
582+ update_monster_physics_model(monster_index);
583+ if ((definition->flags&_monster_has_delayed_hard_death) && monster->action==_monster_is_dying_soft)
584+ {
585+ if (!monster->external_velocity && object->location.z==monster->desired_height) //&& !monster->vertical_velocity)
586+ {
587+ set_monster_action(monster_index, _monster_is_dying_hard);
588+ }
589+ else
590+ {
591+ if (definition->contrail_effect!=NONE) new_effect(&object->location, object->polygon, definition->contrail_effect, object->facing);
592+ }
593+ }
594+ else
595+ {
596+ // LP change: if keyframe is zero, then a monster should not produce shrapnel damage.
597+ // This fixes a side effect of a fix of the keyframe-never-zero bug,
598+ // which is that Hunters injure those nearby when they die a soft death.
599+ if (animation_flags&_obj_keyframe_started && (!film_profile.keyframe_fix || GET_SEQUENCE_FRAME(object->sequence) != 0))
600+ cause_shrapnel_damage(monster_index);
601+ if (animation_flags&_obj_last_frame_animated) kill_monster(monster_index);
602+ }
603+ break;
604+
605+ default:
606+ assert(false);
607+ break;
608+ }
609+ }
610+ }
611+ }
612+ else
613+ {
614+ /* all inactive monsters get time to scan for targets */
615+ if (!monster_got_time && !MONSTER_IS_BLIND(monster) && monster_index>dynamic_world->last_monster_index_to_get_time)
616+ {
617+ change_monster_target(monster_index, find_closest_appropriate_target(monster_index, false));
618+ if (MONSTER_HAS_VALID_TARGET(monster)) activate_nearby_monsters(monster->target_index, monster_index, _pass_one_zone_border);
619+
620+ monster_got_time= true;
621+ dynamic_world->last_monster_index_to_get_time= monster_index;
622+ }
623+ }
624+ }
625+
626+ /* WARNING: a large number of unusual things could have happened here, including the monster
627+ being dead, his slot being free, and his object having been removed from the map; in other
628+ words, itユs probably not a good idea to do any postprocessing here */
629+ }
630+
631+ /* either there are no unlocked monsters or ヤdynamic_world->last_monster_index_to_get_timeユ is higher than
632+ all of them (so we reset it to zero) ... same for paths */
633+ if (!monster_got_time) dynamic_world->last_monster_index_to_get_time= -1;
634+ if (!monster_built_path) dynamic_world->last_monster_index_to_build_path= -1;
635+
636+ if (dynamic_world->civilians_killed_by_players)
637+ {
638+ uint32 mask = 0;
639+
640+ switch (dynamic_world->game_information.difficulty_level)
641+ {
642+ case _wuss_level: mask= 0x7f; break;
643+ case _easy_level: mask= 0xff; break;
644+ case _normal_level: mask= 0x1ff; break;
645+ case _major_damage_level: mask= 0x3ff; break;
646+ case _total_carnage_level: mask= 0x7ff; break;
647+ }
648+
649+ if (!(dynamic_world->tick_count&mask))
650+ {
651+ dynamic_world->civilians_killed_by_players-= 1;
652+ }
653+ }
654+}
655+
656+/* when a monster dies, all monsters locked on it need to find something better to do; this
657+ function should be called before the given target is expunged from the monster list but
658+ after it is marked as dying */
659+void monster_died(
660+ short target_index)
661+{
662+ struct monster_data *monster= get_monster_data(target_index);
663+ short monster_index;
664+
665+// dprintf("monster #%d is dead;g;", target_index);
666+
667+ /* orphan this monsterユs projectiles if they donユt belong to a player (playerユs monster
668+ slots are always valid and we want to correctly attribute damage and kills that ocurr
669+ after a player dies) */
670+ if (!MONSTER_IS_PLAYER(monster)) orphan_projectiles(target_index);
671+
672+ /* active monsters need extant paths deleted and should be marked as unlocked */
673+ if (MONSTER_IS_ACTIVE(monster))
674+ {
675+ set_monster_mode(target_index, _monster_unlocked, NONE);
676+ if (monster->path!=NONE) delete_path(monster->path);
677+ SET_MONSTER_NEEDS_PATH_STATUS(monster, false);
678+ monster->path= NONE;
679+ }
680+
681+ /* anyone locked on this monster needs a clue */
682+ for (monster_index= 0, monster= monsters; monster_index<MAXIMUM_MONSTERS_PER_MAP; ++monster_index, ++monster)
683+ {
684+ if (SLOT_IS_USED(monster) && MONSTER_IS_ACTIVE(monster) && monster->target_index==target_index)
685+ {
686+ short closest_target_index= find_closest_appropriate_target(monster_index, true);
687+
688+ monster->target_index= NONE;
689+ monster_needs_path(monster_index, false);
690+
691+ play_object_sound(monster->object_index, get_monster_definition(monster->type)->kill_sound);
692+ if (closest_target_index!=NONE)
693+ {
694+ change_monster_target(monster_index, closest_target_index);
695+ }
696+ else
697+ {
698+ if (monster->action==_monster_is_waiting_to_attack_again) set_monster_action(monster_index, _monster_is_moving);
699+ set_monster_mode(monster_index, _monster_unlocked, NONE);
700+ }
701+ }
702+ }
703+}
704+
705+void initialize_monsters(
706+ void)
707+{
708+ /* initialize our globals to be the same thing on all machines */
709+ dynamic_world->civilians_killed_by_players= 0;
710+ dynamic_world->last_monster_index_to_get_time= -1;
711+ dynamic_world->last_monster_index_to_build_path= -1;
712+ dynamic_world->new_monster_mangler_cookie= global_random();
713+ dynamic_world->new_monster_vanishing_cookie= global_random();
714+}
715+
716+/* call this when a new level is loaded from disk so the monsters can cope with their new world */
717+void initialize_monsters_for_new_level(
718+ void)
719+{
720+ struct monster_data *monster;
721+ short monster_index;
722+
723+ /* when a level is loaded after being saved all of an active monsterユs data is still intact,
724+ but itユs path no longer exists. this function resets all monsters so that they recalculate
725+ their paths, first thing. */
726+ for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster_index,++monster)
727+ {
728+ if (SLOT_IS_USED(monster)&&MONSTER_IS_ACTIVE(monster))
729+ {
730+ SET_MONSTER_NEEDS_PATH_STATUS(monster, true);
731+ monster->path= NONE;
732+ }
733+ }
734+}
735+
736+static void load_sound(short sound_index)
737+{
738+ SoundManager::instance()->LoadSound(sound_index);
739+}
740+
741+void load_monster_sounds(
742+ short monster_type)
743+{
744+ if (monster_type!=NONE)
745+ {
746+ struct monster_definition *definition= get_monster_definition(monster_type);
747+
748+ process_collection_sounds(definition->collection, load_sound);
749+
750+ load_projectile_sounds(definition->ranged_attack.type);
751+ load_projectile_sounds(definition->melee_attack.type);
752+
753+ SoundManager::instance()->LoadSounds(&definition->activation_sound, 8);
754+ }
755+}
756+
757+void mark_monster_collections(
758+ short monster_type,
759+ bool loading)
760+{
761+ if (monster_type!=NONE)
762+ {
763+ struct monster_definition *definition= get_monster_definition(monster_type);
764+
765+ /* mark the monster collection */
766+ mark_collection(definition->collection, loading);
767+
768+ /* mark the monsterユs projectileユs collection */
769+ mark_projectile_collections(definition->ranged_attack.type, loading);
770+ mark_projectile_collections(definition->melee_attack.type, loading);
771+ }
772+}
773+
774+enum
775+{
776+ MAXIMUM_NEED_TARGET_INDEXES= 32
777+};
778+
779+void activate_nearby_monsters(
780+ short target_index, /* activate with lock on this target (or NONE for lock-less activation) */
781+ short caller_index, /* start the flood from here */
782+ short flags)
783+{
784+ struct monster_data *caller= get_monster_data(caller_index);
785+
786+ if (dynamic_world->tick_count-caller->ticks_since_last_activation>MINIMUM_ACTIVATION_SEPARATION ||
787+ (flags&_activation_cannot_be_avoided))
788+ {
789+ short polygon_index= get_object_data(caller->object_index)->polygon;
790+ short need_target_indexes[MAXIMUM_NEED_TARGET_INDEXES];
791+ short need_target_count= 0;
792+ int32 flood_flags= flags;
793+
794+ /* flood out from the target monsterユs polygon, searching through the object lists of all
795+ polygons we encounter */
796+ polygon_index= flood_map(polygon_index, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
797+ while (polygon_index!=NONE)
798+ {
799+ short object_index;
800+ struct object_data *object;
801+
802+ /* loop through all objects in this polygon looking for _hostile inactive or unlocked monsters */
803+ for (object_index= get_polygon_data(polygon_index)->first_object; object_index!=NONE; object_index= object->next_object)
804+ {
805+ object= get_object_data(object_index);
806+ if (GET_OBJECT_OWNER(object)==_object_is_monster &&
807+ (!OBJECT_IS_INVISIBLE(object) || (flags&_activate_invisible_monsters)))
808+ {
809+ short aggressor_index= object->permutation;
810+ struct monster_data *aggressor= get_monster_data(aggressor_index);
811+// bool target_hostile= get_monster_attitude(aggressor_index, target_index)==_hostile;
812+// bool caller_hostile= get_monster_attitude(aggressor_index, caller_index)==_hostile;
813+
814+// deaf monsters are only deaf to players which have always been hostile, so:
815+// bobs are deaf to friendly players but not hostile ones
816+// monsters are deaf to all players
817+// deaf monsters ignore friendly monsters activating on other friendly monsters but
818+// non-deaf ones DO NOT
819+
820+// !MONSTER_IS_PLAYER(caller) || TYPE_IS_FRIEND(get_monster_definition(aggressor->type), caller->type) || caller_hostile
821+
822+ /* donユt activate players or ourselves, and only activate monsters on glue polygons
823+ if they have previously been activated or weユve been explicitly told to */
824+ if (!MONSTER_IS_PLAYER(aggressor) && caller_index!=aggressor_index && target_index!=aggressor_index &&
825+ (!(flood_flags&_passed_zone_border) || (!(aggressor->flags&_monster_has_never_been_activated))) &&
826+ ((flood_flags&_activate_deaf_monsters) || !MONSTER_IS_DEAF(aggressor)) && // || !MONSTER_IS_PLAYER(caller) || !TYPE_IS_FRIEND(get_monster_definition(aggressor->type), caller->type) || !caller_hostile) &&
827+ aggressor->mode!=_monster_locked)
828+ {
829+ bool monster_was_active= true;
830+
831+ /* activate the monster if heユs inactive */
832+ if (!MONSTER_IS_ACTIVE(aggressor))
833+ {
834+ activate_monster(aggressor_index);
835+ monster_was_active= false;
836+ }
837+
838+ if (monster_was_active || !(flags&_use_activation_biases) ||
839+ (aggressor->activation_bias!=_activate_on_goal && aggressor->activation_bias!=_activate_randomly))
840+ {
841+ if (monster_was_active || aggressor->activation_bias!=_activate_on_nearest_hostile)
842+ {
843+ /* if we have valid target and this monster thinks that target is hostile, lock on */
844+ if (get_monster_attitude(aggressor_index, target_index)==_hostile)
845+ {
846+ switch_target_check(aggressor_index, target_index, 0);
847+ }
848+ else
849+ {
850+ /* but hey, if the target isnユt hostile, maybe the caller is ...
851+ (mostly for the automated defenses and the civilians on the ship) */
852+ if (get_monster_attitude(aggressor_index, caller_index)==_hostile)
853+ {
854+ switch_target_check(aggressor_index, caller_index, 0);
855+ }
856+ }
857+ }
858+ else
859+ {
860+ // must defer find_closest_appropriate_target; pathfinding is not reentrant
861+ if (need_target_count<MAXIMUM_NEED_TARGET_INDEXES)
862+ {
863+ need_target_indexes[need_target_count++]= aggressor_index;
864+ }
865+ }
866+ }
867+ }
868+ }
869+ }
870+
871+ polygon_index= flood_map(NONE, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
872+ }
873+
874+ // deferred find_closest_appropriate_target() calls
875+ while (--need_target_count>=0)
876+ {
877+ change_monster_target(need_target_indexes[need_target_count],
878+ find_closest_appropriate_target(need_target_indexes[need_target_count], true));
879+ }
880+
881+ caller->ticks_since_last_activation= dynamic_world->tick_count;
882+ }
883+}
884+
885+static int32 monster_activation_flood_proc(
886+ short source_polygon_index,
887+ short line_index,
888+ short destination_polygon_index,
889+ void *data)
890+{
891+ int32 *flags=(int32 *)data;
892+ struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
893+ struct line_data *line= get_line_data(line_index);
894+ int32 cost= 1;
895+
896+// dprintf("P#%d==>P#%d by L#%d", source_polygon_index, destination_polygon_index, line_index);
897+
898+ (void) (source_polygon_index);
899+
900+ if (destination_polygon->type==_polygon_is_zone_border)
901+ {
902+ if (((*flags)&_pass_one_zone_border) && !((*flags)&_passed_zone_border))
903+ {
904+ *flags|= _passed_zone_border;
905+ }
906+ else
907+ {
908+ // canユt pass this zone border
909+ cost= -1;
910+ }
911+ }
912+
913+ if (!((*flags)&_pass_solid_lines) && LINE_IS_SOLID(line)) cost= -1;
914+
915+ return cost;
916+}
917+
918+#define LIVE_ALIEN_THRESHHOLD 8
919+
920+bool live_aliens_on_map(
921+ void)
922+{
923+ bool found_alien_which_must_be_killed= false;
924+ struct monster_data *monster;
925+ short live_alien_count= 0;
926+ short threshhold= LIVE_ALIEN_THRESHHOLD;
927+ short monster_index;
928+
929+ for (monster_index= 0, monster= monsters; monster_index<MAXIMUM_MONSTERS_PER_MAP; ++monster_index, ++monster)
930+ {
931+ if (SLOT_IS_USED(monster))
932+ {
933+ struct monster_definition *definition= get_monster_definition(monster->type);
934+
935+#if 0
936+ switch (monster->type)
937+ {
938+ case _monster_juggernaut_minor:
939+ case _monster_juggernaut_major:
940+ case _monster_alien_leader:
941+ found_alien_which_must_be_killed= true;
942+ break;
943+ }
944+#endif
945+
946+ if ((definition->flags&_monster_is_alien) ||
947+ ((static_world->environment_flags&_environment_rebellion) && !MONSTER_IS_PLAYER(monster)))
948+ {
949+ live_alien_count+= 1;
950+ }
951+ }
952+ }
953+
954+ if (static_world->environment_flags&_environment_rebellion) threshhold= 0;
955+
956+ return live_alien_count<=threshhold ? found_alien_which_must_be_killed : true;
957+}
958+
959+/* activate the given monster (initially unlocked) */
960+void activate_monster(
961+ short monster_index)
962+{
963+ struct monster_data *monster= get_monster_data(monster_index);
964+ struct object_data *object= get_object_data(monster->object_index);
965+ struct monster_definition *definition= get_monster_definition(monster->type);
966+
967+// dprintf("monster #%d activated;g;", monster_index);
968+
969+ assert(!MONSTER_IS_ACTIVE(monster));
970+ assert(!MONSTER_IS_PLAYER(monster));
971+
972+ if (OBJECT_IS_INVISIBLE(object))
973+ {
974+ struct polygon_data *polygon= get_polygon_data(object->polygon);
975+
976+ if (polygon->media_index!=NONE)
977+ {
978+ struct media_data *media= get_media_data(polygon->media_index);
979+
980+ // LP change: idiot-proofed this
981+ if (media)
982+ {
983+ if (media->height>object->location.z+definition->height &&
984+ !(definition->flags&_monster_can_teleport_under_media))
985+ {
986+ return;
987+ }
988+ }
989+ }
990+ }
991+
992+ CLEAR_MONSTER_RECOVERING_FROM_HIT(monster);
993+ SET_MONSTER_IDLE_STATUS(monster, false);
994+ SET_MONSTER_ACTIVE_STATUS(monster, true);
995+ SET_MONSTER_BERSERK_STATUS(monster, false);
996+ SET_MONSTER_HAS_BEEN_ACTIVATED(monster);
997+ monster->flags&= ~(_monster_is_blind|_monster_is_deaf);
998+
999+ monster->path= NONE;
1000+ /* we used to set monster->target_index here, but it is invalid when mode==_monster_unlocked */
1001+ monster->mode= _monster_unlocked, monster->target_index= NONE;
1002+
1003+ if (definition->attack_frequency == 0) // IP: Avoid division by zero
1004+ definition->attack_frequency = 1;
1005+
1006+ monster->ticks_since_attack= (definition->flags&_monster_attacks_immediately) ?
1007+ definition->attack_frequency : global_random()%definition->attack_frequency;
1008+ monster->desired_height= object->location.z; /* best guess */
1009+ monster->random_desired_height= INT16_MAX; // to be out of range and recalculated
1010+ monster->external_velocity= monster->vertical_velocity= 0;
1011+ monster->ticks_since_last_activation= 0;
1012+
1013+ /* if vitality is NONE (-1) initialize it from the monster_definition, respecting
1014+ the difficulty level if necessary */
1015+ if (monster->vitality==NONE)
1016+ {
1017+ short vitality= definition->vitality;
1018+
1019+ if (definition->flags&_monster_is_alien)
1020+ {
1021+ switch (dynamic_world->game_information.difficulty_level)
1022+ {
1023+ case _wuss_level: vitality-= vitality>>1; break;
1024+ case _easy_level: vitality-= vitality>>2; break;
1025+ case _major_damage_level: vitality+= vitality>>2; break;
1026+ case _total_carnage_level: vitality+= vitality>>1; break;
1027+ }
1028+ }
1029+
1030+ monster->vitality= vitality;
1031+ }
1032+
1033+ set_monster_action(monster_index, _monster_is_stationary);
1034+ monster_needs_path(monster_index, true);
1035+
1036+ if (OBJECT_IS_INVISIBLE(object))
1037+ {
1038+ teleport_object_in(monster->object_index);
1039+ }
1040+
1041+ changed_polygon(object->polygon, object->polygon, NONE);
1042+}
1043+
1044+void deactivate_monster(
1045+ short monster_index)
1046+{
1047+ struct monster_data *monster= get_monster_data(monster_index);
1048+
1049+// dprintf("monster #%d deactivated;g;", monster_index);
1050+
1051+ assert(MONSTER_IS_ACTIVE(monster));
1052+
1053+ if (MONSTER_TELEPORTS_OUT_WHEN_DEACTIVATED(monster)) monster->vertical_velocity= monster->external_velocity= 0;
1054+
1055+ if (!monster->vertical_velocity && !monster->external_velocity)
1056+ {
1057+ if (MONSTER_TELEPORTS_OUT_WHEN_DEACTIVATED(monster) && monster->action!=_monster_is_teleporting_out)
1058+ {
1059+ set_monster_action(monster_index, _monster_is_teleporting_out);
1060+ }
1061+ else
1062+ {
1063+ /* assume stationary shape before deactivation */
1064+ set_monster_action(monster_index, _monster_is_stationary);
1065+
1066+ /* get rid of this monsterユs path if he has one */
1067+ if (monster->path!=NONE) delete_path(monster->path);
1068+
1069+ SET_MONSTER_ACTIVE_STATUS(monster, false);
1070+ }
1071+ }
1072+}
1073+
1074+/* returns a list of object indexes of all monsters in or adjacent to the given polygon,
1075+ up to maximum_object_count. */
1076+// LP change: called with growable list
1077+bool possible_intersecting_monsters(
1078+ vector<short> *IntersectedObjectsPtr,
1079+ unsigned maximum_object_count,
1080+ short polygon_index,
1081+ bool include_scenery)
1082+{
1083+ struct polygon_data *polygon= get_polygon_data(polygon_index);
1084+ short *neighbor_indexes= get_map_indexes(polygon->first_neighbor_index, polygon->neighbor_count);
1085+ bool found_solid_object= false;
1086+
1087+ // Skip this step if neighbor indexes were not found
1088+ if (!neighbor_indexes) return found_solid_object;
1089+
1090+ for (short i=0;i<polygon->neighbor_count;++i)
1091+ {
1092+ struct polygon_data *neighboring_polygon= get_polygon_data(*neighbor_indexes++);
1093+
1094+ if (!POLYGON_IS_DETACHED(neighboring_polygon))
1095+ {
1096+ short object_index= neighboring_polygon->first_object;
1097+
1098+ while (object_index!=NONE)
1099+ {
1100+ struct object_data *object= get_object_data(object_index);
1101+ bool solid_object= false;
1102+
1103+ if (!OBJECT_IS_INVISIBLE(object))
1104+ {
1105+ switch (GET_OBJECT_OWNER(object))
1106+ {
1107+ case _object_is_monster:
1108+ {
1109+ struct monster_data *monster= get_monster_data(object->permutation);
1110+
1111+ if (!MONSTER_IS_DYING(monster) && !MONSTER_IS_TELEPORTING(monster))
1112+ {
1113+ solid_object= true;
1114+ }
1115+
1116+ break;
1117+ }
1118+
1119+ case _object_is_scenery:
1120+ if (include_scenery && OBJECT_IS_SOLID(object)) solid_object= true;
1121+ break;
1122+ }
1123+
1124+ if (solid_object)
1125+ {
1126+ found_solid_object= true;
1127+
1128+ // LP change:
1129+ if (IntersectedObjectsPtr && IntersectedObjectsPtr->size()<maximum_object_count) /* do we have enough space to add it? */
1130+ {
1131+ unsigned j;
1132+
1133+ /* only add this object_index if it's not already in the list */
1134+ vector<short>& IntersectedObjects = *IntersectedObjectsPtr;
1135+ for (j=0; j<IntersectedObjects.size() && IntersectedObjects[j]!=object_index; ++j)
1136+ ;
1137+ if (j==IntersectedObjects.size())
1138+ IntersectedObjects.push_back(object_index);
1139+ }
1140+ }
1141+ }
1142+
1143+ object_index= object->next_object;
1144+ }
1145+ }
1146+ }
1147+
1148+ return found_solid_object;
1149+}
1150+
1151+/* when a target changes polygons, all monsters locked on it must recalculate their paths.
1152+ target is an index into the monster list. */
1153+void monster_moved(
1154+ short target_index,
1155+ short old_polygon_index)
1156+{
1157+ struct monster_data *monster= get_monster_data(target_index);
1158+ struct object_data *object= get_object_data(monster->object_index);
1159+ short monster_index;
1160+
1161+ if (!MONSTER_IS_PLAYER(monster))
1162+ {
1163+ /* cause lights to light, platforms to trigger, etc.; the player does this differently */
1164+ changed_polygon(old_polygon_index, object->polygon, NONE);
1165+ }
1166+
1167+ for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster_index,++monster)
1168+ {
1169+ /* look for active monsters locked (or losing lock) on the given target_index */
1170+ if (SLOT_IS_USED(monster) && MONSTER_HAS_VALID_TARGET(monster) && monster->target_index==target_index)
1171+ {
1172+ if (clear_line_of_sight(monster_index, target_index, true))
1173+ {
1174+ if (monster->mode==_monster_losing_lock) set_monster_mode(monster_index, _monster_locked, monster->target_index);
1175+ }
1176+ else
1177+ {
1178+ struct monster_definition *definition= get_monster_definition(monster->type);
1179+ short intelligence= definition->intelligence;
1180+
1181+ switch (dynamic_world->game_information.difficulty_level)
1182+ {
1183+ case _wuss_level: intelligence= intelligence>>2; break;
1184+ case _easy_level: intelligence= intelligence>>1; break;
1185+ case _major_damage_level: intelligence= 2*intelligence; break;
1186+ case _total_carnage_level: intelligence= 4*intelligence; break;
1187+ }
1188+
1189+ /* we canユt see our target: if this is first time, change from _monster_locked
1190+ to _monster_losing_lock, if this isnユt the first time and our target has
1191+ switched polygons more times out of our sight than we have intelligence points,
1192+ go to _lost_lock (which means we wonユt get any more new paths when our target
1193+ switches polygons, but we wonユt clear our last one until we reach the end). */
1194+ if (monster->mode==_monster_locked) monster->changes_until_lock_lost= 0;
1195+ if (monster->mode==_monster_losing_lock) monster->changes_until_lock_lost+= 1;
1196+ set_monster_mode(monster_index, (monster->changes_until_lock_lost>=definition->intelligence) ?
1197+ _monster_lost_lock : _monster_losing_lock, NONE);
1198+ }
1199+
1200+ /* if weユre losing lock, donユt recalculate our path (weユre headed towards the targetユs
1201+ last-known location) */
1202+ if (monster->mode!=_monster_losing_lock) monster_needs_path(monster_index, false);
1203+ }
1204+ }
1205+}
1206+
1207+/* returns NONE or a monster_index that prevented us from moving */
1208+short legal_player_move(
1209+ short monster_index,
1210+ world_point3d *new_location,
1211+ world_distance *object_floor) /* must be set on entry */
1212+{
1213+ struct monster_data *monster= get_monster_data(monster_index);
1214+ struct object_data *object= get_object_data(monster->object_index);
1215+ world_point3d *old_location= &object->location;
1216+ size_t monster_count;
1217+ world_distance radius, height;
1218+ short obstacle_index= NONE;
1219+
1220+ get_monster_dimensions(monster_index, &radius, &height);
1221+
1222+ IntersectedObjects.clear();
1223+ possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, object->polygon, true);
1224+ monster_count = IntersectedObjects.size();
1225+ for (size_t i=0;i<monster_count;++i)
1226+ {
1227+ struct object_data *obstacle= get_object_data(IntersectedObjects[i]);
1228+ world_distance obstacle_radius, obstacle_height;
1229+
1230+ switch (GET_OBJECT_OWNER(obstacle))
1231+ {
1232+ case _object_is_monster: get_monster_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1233+ case _object_is_scenery: get_scenery_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1234+ default:
1235+ assert(false);
1236+ break;
1237+ }
1238+
1239+ if (IntersectedObjects[i]!=monster->object_index) /* no self-intersection */
1240+ {
1241+ world_point3d *obstacle_location= &obstacle->location;
1242+
1243+ world_distance separation= radius+obstacle_radius;
1244+ int32 separation_squared= separation*separation;
1245+
1246+ world_distance new_dx= obstacle_location->x-new_location->x;
1247+ world_distance new_dy= obstacle_location->y-new_location->y;
1248+ int32 new_distance_squared= new_dx*new_dx+new_dy*new_dy;
1249+
1250+ if (new_distance_squared<separation_squared)
1251+ {
1252+ world_distance old_dx= obstacle_location->x-old_location->x;
1253+ world_distance old_dy= obstacle_location->y-old_location->y;
1254+ int32 old_distance_squared= old_dx*old_dx+old_dy*old_dy;
1255+
1256+ if (old_distance_squared>new_distance_squared)
1257+ {
1258+ world_distance this_object_floor= obstacle_location->z+obstacle_height;
1259+
1260+ /* itユs possible we donユt intersect in z */
1261+ if (new_location->z+height<obstacle_location->z) continue;
1262+ if (new_location->z>this_object_floor)
1263+ {
1264+ if (this_object_floor>*object_floor) *object_floor= this_object_floor;
1265+ continue;
1266+ }
1267+
1268+// dprintf("#%d (%d,%d) hit #%d (%d,%d) moving to (%d,%d)", monster_index, old_location->x, old_location->y, obstacle->permutation, obstacle_location->x, obstacle_location->y, new_location->x, new_location->y);
1269+ obstacle_index= IntersectedObjects[i];
1270+ break;
1271+ }
1272+ }
1273+ }
1274+ }
1275+
1276+ return obstacle_index;
1277+}
1278+
1279+/* returns NONE or a monster_index that prevented us from moving */
1280+short legal_monster_move(
1281+ short monster_index,
1282+ angle facing, /* could be different than object->facing for players and ヤflyingユ (heh heh) monsters */
1283+ world_point3d *new_location)
1284+{
1285+ struct monster_data *monster= get_monster_data(monster_index);
1286+ struct object_data *object= get_object_data(monster->object_index);
1287+// world_point2d *old_location= (world_point2d *) &object->location;
1288+ size_t monster_count;
1289+ world_distance radius, height;
1290+ short obstacle_index= NONE;
1291+
1292+ get_monster_dimensions(monster_index, &radius, &height);
1293+
1294+ IntersectedObjects.clear();
1295+ possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, object->polygon, true);
1296+ monster_count= IntersectedObjects.size();
1297+ for (size_t i=0;i<monster_count;++i)
1298+ {
1299+ struct object_data *obstacle= get_object_data(IntersectedObjects[i]);
1300+ world_distance obstacle_radius, obstacle_height;
1301+
1302+ switch (GET_OBJECT_OWNER(obstacle))
1303+ {
1304+ case _object_is_monster: get_monster_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1305+ case _object_is_scenery: get_scenery_dimensions(obstacle->permutation, &obstacle_radius, &obstacle_height); break;
1306+ default:
1307+ assert(false);
1308+ break;
1309+ }
1310+
1311+ // LP change:
1312+ if (IntersectedObjects[i]!=monster->object_index) /* no self-intersection */
1313+ {
1314+ world_point3d *obstacle_location= &obstacle->location;
1315+
1316+ if (obstacle_location->z<new_location->z+height && obstacle_location->z+obstacle_height>new_location->z)
1317+ {
1318+ world_distance separation= radius+obstacle_radius;
1319+ world_distance dx= obstacle_location->x-new_location->x;
1320+ world_distance dy= obstacle_location->y-new_location->y;
1321+
1322+ if (GET_OBJECT_OWNER(obstacle)!=_object_is_scenery && obstacle->permutation>monster_index && !MONSTER_IS_PLAYER(get_monster_data(obstacle->permutation))) separation= (separation>>1) + (separation>>2);
1323+ if (dx>-separation && dx<separation && dy>-separation && dy<separation)
1324+ {
1325+ /* we intersect sloppily; get arctan to be sure */
1326+ angle theta= NORMALIZE_ANGLE(arctangent(dx, dy)-facing);
1327+
1328+ if (theta<EIGHTH_CIRCLE||theta>FULL_CIRCLE-EIGHTH_CIRCLE)
1329+ {
1330+// dprintf("#%d (%d,%d) hit #%d (%d,%d) moving to (%d,%d)", monster_index, old_location->x, old_location->y, obstacle->permutation, obstacle_location->x, obstacle_location->y, new_location->x, new_location->y);
1331+ obstacle_index= IntersectedObjects[i];
1332+ break;
1333+ }
1334+ }
1335+ }
1336+ }
1337+ }
1338+
1339+ return obstacle_index;
1340+}
1341+
1342+void get_monster_dimensions(
1343+ short monster_index,
1344+ world_distance *radius,
1345+ world_distance *height)
1346+{
1347+ struct monster_data *monster= get_monster_data(monster_index);
1348+ struct monster_definition *definition= get_monster_definition(monster->type);
1349+
1350+ *radius= definition->radius;
1351+ *height= definition->height;
1352+}
1353+
1354+void damage_monsters_in_radius(
1355+ short primary_target_index,
1356+ short aggressor_index,
1357+ short aggressor_type,
1358+ world_point3d *epicenter,
1359+ short epicenter_polygon_index,
1360+ world_distance radius,
1361+ struct damage_definition *damage,
1362+ short projectile_index)
1363+{
1364+ size_t object_count;
1365+
1366+ bool aggressor_is_live_player = false;
1367+
1368+ (void) (primary_target_index);
1369+
1370+ IntersectedObjects.clear();
1371+ possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, epicenter_polygon_index, false);
1372+ object_count= IntersectedObjects.size();
1373+ struct object_data *aggressor = NULL;
1374+ if (film_profile.infinity_tag_fix && aggressor_index != NONE)
1375+ {
1376+ monster_data* monster = get_monster_data(aggressor_index);
1377+ if (MONSTER_IS_PLAYER(monster))
1378+ {
1379+ player_data* player = get_player_data(monster_index_to_player_index(aggressor_index));
1380+
1381+ if (!PLAYER_IS_DEAD(player)) aggressor_is_live_player = true;
1382+ }
1383+ }
1384+
1385+ for (size_t i=0;i<object_count;++i)
1386+ {
1387+ struct object_data *object= get_object_data(IntersectedObjects[i]);
1388+ if (film_profile.damage_aggressor_last_in_tag &&
1389+ GET_GAME_TYPE() == _game_of_tag &&
1390+ object->permutation == aggressor_index) {
1391+ // damage the aggressor last, so tag suicides are handled correctly
1392+ aggressor = object;
1393+ } else {
1394+ world_distance distance= distance2d((world_point2d*)epicenter, (world_point2d*)&object->location);
1395+ world_distance monster_radius, monster_height;
1396+
1397+ get_monster_dimensions(object->permutation, &monster_radius, &monster_height);
1398+
1399+ /* make sure we intersect the monsterユs radius in the x,y-plane and that we intersect
1400+ his cylinder in z */
1401+ if (distance<radius+monster_radius)
1402+ {
1403+ if (epicenter->z+radius-distance>object->location.z && epicenter->z-radius+distance<object->location.z+monster_height)
1404+ {
1405+ if (!line_is_obstructed(epicenter_polygon_index, (world_point2d*)epicenter, object->polygon, (world_point2d*)&object->location))
1406+ {
1407+ damage_monster(object->permutation, aggressor_index, aggressor_type, epicenter, damage, projectile_index);
1408+ }
1409+ }
1410+ }
1411+ }
1412+ }
1413+
1414+ // damage the aggressor
1415+ if (film_profile.damage_aggressor_last_in_tag && aggressor != NULL)
1416+ {
1417+ world_distance distance= distance2d((world_point2d*)epicenter, (world_point2d*)&aggressor->location);
1418+ world_distance monster_radius, monster_height;
1419+
1420+ get_monster_dimensions(aggressor->permutation, &monster_radius, &monster_height);
1421+ if (distance<radius+monster_radius)
1422+ {
1423+ if (epicenter->z+radius-distance>aggressor->location.z && epicenter->z-radius+distance<aggressor->location.z+monster_height)
1424+ {
1425+ if (!line_is_obstructed(epicenter_polygon_index, (world_point2d*)epicenter, aggressor->polygon, (world_point2d*)&aggressor->location))
1426+ {
1427+ damage_monster(aggressor->permutation, aggressor_index, aggressor_type, epicenter, damage, projectile_index);
1428+ }
1429+ }
1430+ }
1431+ }
1432+
1433+ // or, just make him it
1434+ if (GET_GAME_TYPE() == _game_of_tag && aggressor_is_live_player)
1435+ {
1436+ monster_data* monster = get_monster_data(aggressor_index);
1437+ if (MONSTER_IS_PLAYER(monster))
1438+ {
1439+ short player_index = monster_index_to_player_index(aggressor_index);
1440+ player_data* player = get_player_data(player_index);
1441+
1442+ // he blew himself up, so make sure he's it
1443+ if (PLAYER_IS_DEAD(player))
1444+ {
1445+ dynamic_world->game_player_index = player_index;
1446+ }
1447+ }
1448+ }
1449+}
1450+
1451+void damage_monster(
1452+ short target_index,
1453+ short aggressor_index,
1454+ short aggressor_type,
1455+ world_point3d *epicenter,
1456+ struct damage_definition *damage,
1457+ short projectile_index)
1458+{
1459+ struct monster_data *monster= get_monster_data(target_index);
1460+ struct monster_definition *definition= get_monster_definition(monster->type);
1461+ struct monster_data *aggressor_monster= aggressor_index!=NONE ? get_monster_data(aggressor_index) : (struct monster_data *) NULL;
1462+// dprintf("%d base, %d random, %d scale.\n", damage->base, damage->random, damage->scale);
1463+ short delta_vitality= calculate_damage(damage);
1464+// dprintf("we are doing %d damage\n", delta_vitality);
1465+ world_distance external_velocity= 0;
1466+ bool vertical_component= false;
1467+
1468+ if (!(definition->immunities&FLAG(damage->type)))
1469+ {
1470+ // double damage for weaknesses
1471+ if (definition->weaknesses&FLAG(damage->type)) delta_vitality<<= 1;
1472+
1473+ // if this player was shot by a friendly, make him apologise
1474+ if (aggressor_index!=NONE && get_monster_attitude(aggressor_index, target_index)==_friendly)
1475+ {
1476+ play_object_sound(aggressor_monster->object_index, get_monster_definition(aggressor_monster->type)->apology_sound);
1477+ }
1478+
1479+ if (MONSTER_IS_PLAYER(monster))
1480+ {
1481+ damage_player(target_index, aggressor_index, aggressor_type, damage, projectile_index);
1482+ }
1483+ else
1484+ {
1485+ struct player_data *aggressor_player= (struct player_data *) NULL;
1486+
1487+ /* only active monsters can take damage */
1488+ if (!MONSTER_IS_ACTIVE(monster)) activate_monster(target_index);
1489+
1490+ /* convert aggressor monster index to a player index, if possible, to record damage */
1491+ if (aggressor_index!=NONE)
1492+ {
1493+ if (MONSTER_IS_PLAYER(aggressor_monster))
1494+ {
1495+ aggressor_player= get_player_data(monster_index_to_player_index(aggressor_index));
1496+ aggressor_player->monster_damage_given.damage+= MAX(monster->vitality, delta_vitality);
1497+ team_monster_damage_given[aggressor_player->team].damage += MAX(monster->vitality, delta_vitality);
1498+ }
1499+ }
1500+
1501+ // LP change: pegging to maximum value
1502+ monster->vitality = MIN(int32(monster->vitality) - int32(delta_vitality), int32(INT16_MAX));
1503+ L_Call_Monster_Damaged(target_index, aggressor_index, damage->type, delta_vitality, projectile_index);
1504+
1505+ if (monster->vitality > 0)
1506+ {
1507+ set_monster_action(target_index, _monster_is_being_hit);
1508+ if ((definition->flags&_monster_is_berserker) && monster->vitality<(definition->vitality>>2))
1509+ SET_MONSTER_BERSERK_STATUS(monster, true);
1510+ if (aggressor_index!=NONE) switch_target_check(target_index, aggressor_index, delta_vitality);
1511+
1512+ // if a player shoots a monster who thinks the player is friendly; ask him what the fuck is up
1513+ if (aggressor_player && get_monster_attitude(target_index, aggressor_index)==_friendly) play_object_sound(monster->object_index, definition->friendly_fire_sound);
1514+ }
1515+ else
1516+ {
1517+ if (!MONSTER_IS_DYING(monster))
1518+ {
1519+ short action;
1520+
1521+ if ((damage->type==_damage_flame||damage->type==_damage_lava||damage->type==_damage_alien_projectile) && (definition->flags&_monster_can_die_in_flames))
1522+ {
1523+ action= _monster_is_dying_flaming;
1524+ }
1525+ else
1526+ {
1527+ if ((damage->type==_damage_explosion || damage->type==_damage_crushing || (FLAG(damage->type)&definition->weaknesses) ||
1528+ definition->soft_dying_shape==UNONE) && definition->hard_dying_shape!=UNONE && !(definition->flags&_monster_has_delayed_hard_death))
1529+ {
1530+ action= _monster_is_dying_hard;
1531+ }
1532+ else
1533+ {
1534+ action= _monster_is_dying_soft;
1535+ }
1536+ if (definition->flags&_monster_has_delayed_hard_death) monster->vertical_velocity= -1;
1537+ }
1538+
1539+ if (action==_monster_is_dying_flaming) play_object_sound(monster->object_index, definition->flaming_sound);
1540+ set_monster_action(target_index, action);
1541+ monster_died(target_index); /* orphan projectile, recalculate aggressor paths */
1542+
1543+ if (aggressor_player)
1544+ {
1545+ aggressor_player->monster_damage_given.kills+= 1;
1546+ team_monster_damage_given[aggressor_player->team].kills += 1;
1547+
1548+ if (definition->_class&_class_human_civilian) dynamic_world->civilians_killed_by_players+= 1;
1549+ }
1550+ }
1551+
1552+ // Lua script hook
1553+ int aggressor_player_index = -1;
1554+ if (aggressor_index!=NONE)
1555+ if (MONSTER_IS_PLAYER(aggressor_monster))
1556+ aggressor_player_index = monster_index_to_player_index(aggressor_index);
1557+ L_Call_Monster_Killed (target_index, aggressor_player_index, projectile_index);
1558+ }
1559+ }
1560+
1561+
1562+ if (damage->type >= 0 && damage->type < NUMBER_OF_DAMAGE_TYPES)
1563+ {
1564+ damage_kick_definition& kick_def = damage_kick_definitions[damage->type];
1565+
1566+ external_velocity = (world_distance)(kick_def.base_value + kick_def.delta_vitality_multiplier*delta_vitality);
1567+ vertical_component = kick_def.is_also_vertical;
1568+ if (film_profile.use_vertical_kick_threshold
1569+ && kick_def.vertical_threshold
1570+ && delta_vitality > kick_def.vertical_threshold)
1571+ {
1572+ vertical_component = true;
1573+ }
1574+ }
1575+
1576+/*
1577+ switch (damage->type)
1578+ {
1579+ case _damage_teleporter:
1580+ external_velocity= 250;
1581+ break;
1582+
1583+ case _damage_fusion_bolt:
1584+ if (delta_vitality>100) vertical_component= true;
1585+ break;
1586+
1587+ case _damage_electrical_staff:
1588+ case _damage_yeti_claws:
1589+ case _damage_compiler_bolt:
1590+ vertical_component= true;
1591+ external_velocity= 3*delta_vitality;
1592+ break;
1593+
1594+ case _damage_shotgun_projectile:
1595+ vertical_component= true;
1596+ break;
1597+
1598+ case _damage_explosion:
1599+ vertical_component= true;
1600+ external_velocity= delta_vitality;
1601+ break;
1602+
1603+ default:
1604+ external_velocity= delta_vitality;
1605+ break;
1606+ }
1607+*/
1608+
1609+ if (MONSTER_IS_DYING(monster) && external_velocity<MINIMUM_DYING_EXTERNAL_VELOCITY) external_velocity= MINIMUM_DYING_EXTERNAL_VELOCITY;
1610+ external_velocity= (external_velocity*definition->external_velocity_scale)>>FIXED_FRACTIONAL_BITS;
1611+ if (external_velocity && epicenter)
1612+ {
1613+ struct object_data *object= get_object_data(monster->object_index);
1614+ world_distance dx= object->location.x - epicenter->x;
1615+ world_distance dy= object->location.y - epicenter->y;
1616+ world_distance dz= object->location.z + (definition->height>>1) - epicenter->z;
1617+ angle direction= arctangent(dx, dy);
1618+ world_distance radius= isqrt(dx*dx+dy*dy+dz*dz);
1619+ world_distance vertical_velocity= (vertical_component&&radius) ? ((external_velocity*dz)/radius) : 0;
1620+
1621+ accelerate_monster(target_index, vertical_velocity, direction, external_velocity);
1622+ }
1623+ }
1624+}
1625+
1626+bool bump_monster(
1627+ short aggressor_index,
1628+ short monster_index)
1629+{
1630+#if 0
1631+#ifdef DEBUG
1632+#ifdef env68k
1633+ if (MONSTER_IS_PLAYER(get_monster_data(aggressor_index)))
1634+ {
1635+ dprintf("bumped monster @%p;dm #%d #%d;", get_monster_data(monster_index),
1636+ get_monster_data(monster_index), sizeof(struct monster_data));
1637+ }
1638+#endif
1639+#endif
1640+#endif
1641+
1642+ return switch_target_check(monster_index, aggressor_index, 0);
1643+}
1644+
1645+
1646+bool legal_polygon_height_change(
1647+ short polygon_index,
1648+ world_distance new_floor_height,
1649+ world_distance new_ceiling_height,
1650+ struct damage_definition *damage)
1651+{
1652+ world_distance new_polygon_height= new_ceiling_height-new_floor_height;
1653+ struct polygon_data *polygon= get_polygon_data(polygon_index);
1654+ short object_index= polygon->first_object;
1655+ world_distance minimum_height= dead_player_minimum_polygon_height(polygon_index);
1656+ bool legal_change= true;
1657+
1658+ while (object_index!=NONE)
1659+ {
1660+ struct object_data *object= get_object_data(object_index);
1661+
1662+ if (GET_OBJECT_OWNER(object)==_object_is_monster && OBJECT_IS_VISIBLE(object))
1663+ {
1664+ world_distance radius, height;
1665+
1666+ get_monster_dimensions(object->permutation, &radius, &height);
1667+ if (height>=new_polygon_height)
1668+ {
1669+ if (damage)
1670+ {
1671+ damage_monster(object->permutation, NONE, NONE, (world_point3d *) NULL, damage, NONE);
1672+ play_object_sound(object_index, Sound_Crunched());
1673+ }
1674+ legal_change= false;
1675+ }
1676+ }
1677+
1678+ object_index= object->next_object;
1679+ }
1680+
1681+ return new_polygon_height<minimum_height ? false : legal_change;
1682+}
1683+
1684+/* weユve already checked and this monster is not obstructing the polygon from changing heights */
1685+void adjust_monster_for_polygon_height_change(
1686+ short monster_index,
1687+ short polygon_index,
1688+ world_distance new_floor_height,
1689+ world_distance new_ceiling_height)
1690+{
1691+ struct polygon_data *polygon= get_polygon_data(polygon_index);
1692+ struct monster_data *monster= get_monster_data(monster_index);
1693+ world_distance radius, height;
1694+
1695+ get_monster_dimensions(monster_index, &radius, &height);
1696+
1697+ if (MONSTER_IS_PLAYER(monster))
1698+ {
1699+ adjust_player_for_polygon_height_change(monster_index, polygon_index, new_floor_height, new_ceiling_height);
1700+ }
1701+ else
1702+ {
1703+ struct object_data *object= get_object_data(monster->object_index);
1704+
1705+ if (object->location.z==polygon->floor_height) object->location.z= new_floor_height;
1706+ }
1707+}
1708+
1709+void accelerate_monster(
1710+ short monster_index,
1711+ world_distance vertical_velocity,
1712+ angle direction,
1713+ world_distance velocity)
1714+{
1715+ struct monster_data *monster= get_monster_data(monster_index);
1716+
1717+ if (MONSTER_IS_PLAYER(monster))
1718+ {
1719+ accelerate_player(monster_index, vertical_velocity, direction, velocity);
1720+ }
1721+ else
1722+ {
1723+ struct object_data *object= get_object_data(monster->object_index);
1724+
1725+ object->facing= NORMALIZE_ANGLE(direction+HALF_CIRCLE);
1726+ monster->external_velocity+= velocity;
1727+ monster->vertical_velocity+= PIN(monster->vertical_velocity+vertical_velocity, -TERMINAL_VERTICAL_MONSTER_VELOCITY, TERMINAL_VERTICAL_MONSTER_VELOCITY);
1728+ }
1729+}
1730+
1731+short get_monster_impact_effect(
1732+ short monster_index)
1733+{
1734+ struct monster_data *monster= get_monster_data(monster_index);
1735+ struct monster_definition *definition= get_monster_definition(monster->type);
1736+ short impact_effect_index= definition->impact_effect;
1737+
1738+ if (MONSTER_IS_PLAYER(monster))
1739+ {
1740+ struct object_data *object= get_object_data(monster->object_index);
1741+
1742+ switch (object->transfer_mode)
1743+ {
1744+ case _xfer_static:
1745+ impact_effect_index= NONE;
1746+ break;
1747+ }
1748+ }
1749+
1750+ return impact_effect_index;
1751+}
1752+
1753+short get_monster_melee_impact_effect(
1754+ short monster_index)
1755+{
1756+ struct monster_data *monster= get_monster_data(monster_index);
1757+ struct monster_definition *definition= get_monster_definition(monster->type);
1758+
1759+ return definition->melee_impact_effect;
1760+}
1761+
1762+#if 0
1763+/* pick a random player; flood out from that player until we find a polygon legal for a monster
1764+ drop whose center is not player visible. */
1765+void pick_nearby_random_monster_position(
1766+ world_point2d *p,
1767+ short *polygon_index)
1768+{
1769+ short player_index= global_random()%dynamic_world->player_count;
1770+ short flood_polygon_index= get_player_data(player_index)->camera_polygon_index;
1771+
1772+ polygon_index= flood_map(polygon_index, area, monster_activation_flood_proc, _breadth_first, &flood_flags);
1773+ while (polygon_index!=NONE)
1774+ {
1775+ short object_index;
1776+ struct object_data *object;
1777+ struct polygon_data *polygon= get_polygon_data(polygon_index);
1778+
1779+ polygon_index= flood_map(NONE, area, monster_activation_flood_proc, _breadth_first, &flood_flags);
1780+ }
1781+}
1782+#endif
1783+
1784+/* ---------- private code */
1785+
1786+static void cause_shrapnel_damage(
1787+ short monster_index)
1788+{
1789+ struct monster_data *monster= get_monster_data(monster_index);
1790+ struct object_data *object= get_object_data(monster->object_index);
1791+ struct monster_definition *definition= get_monster_definition(monster->type);
1792+
1793+ if (definition->shrapnel_radius!=NONE)
1794+ {
1795+ damage_monsters_in_radius(NONE, NONE, NONE, &object->location, object->polygon,
1796+ definition->shrapnel_radius, &definition->shrapnel_damage, NONE);
1797+ }
1798+}
1799+
1800+static void update_monster_vertical_physics_model(
1801+ short monster_index)
1802+{
1803+ struct monster_data *monster= get_monster_data(monster_index);
1804+ struct monster_definition *definition= get_monster_definition(monster->type);
1805+ struct object_data *object= get_object_data(monster->object_index);
1806+ struct polygon_data *polygon= get_polygon_data(object->polygon);
1807+ struct media_data *media= polygon->media_index==NONE ? (struct media_data *) NULL : get_media_data(polygon->media_index);
1808+ uint32 moving_flags= MONSTER_IS_DYING(monster) ? 0 : (definition->flags&(_monster_flys|_monster_floats));
1809+ world_distance gravity= (static_world->environment_flags&_environment_low_gravity) ? (definition->gravity>>1) : definition->gravity;
1810+ world_distance floor_height= polygon->floor_height;
1811+ world_distance desired_height;
1812+ world_distance old_height= object->location.z;
1813+ bool above_ground, below_ground;
1814+
1815+ if (media)
1816+ {
1817+ // flying and floating monsters treat media as the floor
1818+ if (moving_flags && media->height>floor_height) floor_height= media->height + WORLD_ONE/16;
1819+
1820+ // take damage if necessary
1821+ if (media->height>object->location.z)
1822+ {
1823+ struct damage_definition *damage= get_media_damage(polygon->media_index, FIXED_ONE);
1824+
1825+ if (damage) damage_monster(monster_index, NONE, NONE, (world_point3d *) NULL, damage, NONE);
1826+ }
1827+ }
1828+ desired_height= (monster->desired_height==NONE||MONSTER_IS_DYING(monster)) ? polygon->floor_height : monster->desired_height;
1829+ above_ground= object->location.z>desired_height;
1830+ below_ground= object->location.z<desired_height;
1831+
1832+ switch (moving_flags)
1833+ {
1834+ case 0:
1835+ /* if weユre above the floor, adjust vertical velocity */
1836+ if (above_ground) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1837+ if (below_ground) monster->vertical_velocity= 0, object->location.z= desired_height;
1838+ break;
1839+
1840+ case _monster_flys:
1841+ if (above_ground && !MONSTER_IS_ATTACKING(monster)) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1842+ if (below_ground) monster->vertical_velocity= CEILING(monster->vertical_velocity+gravity, definition->terminal_velocity);
1843+ break;
1844+
1845+ case _monster_floats:
1846+ if (above_ground && !MONSTER_IS_ATTACKING(monster)) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1847+ if (below_ground) monster->vertical_velocity= CEILING(monster->vertical_velocity+gravity, definition->terminal_velocity);
1848+ break;
1849+
1850+ default:
1851+ /* canユt fly and float, beavis */
1852+ // LP change: this stuff put in to handle the map "Aqualung" correctly
1853+ if (above_ground && !MONSTER_IS_ATTACKING(monster)) monster->vertical_velocity= FLOOR(monster->vertical_velocity-gravity, -definition->terminal_velocity);
1854+ if (below_ground) monster->vertical_velocity= CEILING(monster->vertical_velocity+gravity, definition->terminal_velocity);
1855+ break;
1856+ }
1857+
1858+ /* add our vertical velocity to z */
1859+ object->location.z= PIN(object->location.z+monster->vertical_velocity, polygon->floor_height, polygon->ceiling_height-definition->height);
1860+
1861+ /* if weユre under the floor moving down, put us on the floor and clear our velocity;
1862+ if weユre above the floor moving up, put us on the floor and clear our velocity if we were previously below ground */
1863+ switch (moving_flags)
1864+ {
1865+ case 0:
1866+ case _monster_floats:
1867+ if (object->location.z<=desired_height && monster->vertical_velocity<0) monster->vertical_velocity= 0, object->location.z= desired_height;
1868+ if (object->location.z>=desired_height && monster->vertical_velocity>0 && below_ground) monster->vertical_velocity= 0, object->location.z= desired_height;
1869+ break;
1870+
1871+ case _monster_flys:
1872+ default: // LP: added this case to handle "Aqualung" correctly
1873+ if (object->location.z<=desired_height && above_ground) monster->vertical_velocity>>= 1, object->location.z= desired_height;
1874+ if (object->location.z>=desired_height && below_ground) monster->vertical_velocity>>= 1, object->location.z= desired_height;
1875+ break;
1876+ }
1877+
1878+ /* reset desired height (flying and floating monsters often change this later) */
1879+ if (moving_flags&_monster_flys)
1880+ {
1881+ /* weユre flying!: if we have no target, take the middle ground; if we have a target aim
1882+ for his midsection */
1883+ if (MONSTER_HAS_VALID_TARGET(monster))
1884+ {
1885+ struct monster_data *target= get_monster_data(monster->target_index);
1886+ struct monster_definition *target_definition= get_monster_definition(target->type);
1887+
1888+ monster->desired_height= get_object_data(target->object_index)->location.z + ((target_definition->height-definition->height)>>1) + definition->preferred_hover_height;
1889+ monster->desired_height= PIN(monster->desired_height, floor_height+(definition->height>>2), polygon->ceiling_height-definition->height);
1890+ }
1891+ else
1892+ {
1893+ if (monster->random_desired_height<floor_height || monster->random_desired_height>polygon->ceiling_height)
1894+ {
1895+ world_distance delta= polygon->ceiling_height-floor_height-definition->height;
1896+
1897+ monster->random_desired_height= floor_height + ((delta>0) ? (global_random()%delta) : 0);
1898+ }
1899+
1900+ monster->desired_height= MONSTER_IS_DYING(monster) ? polygon->floor_height : monster->random_desired_height;
1901+ }
1902+ }
1903+ else
1904+ {
1905+ monster->desired_height= floor_height;
1906+ }
1907+
1908+ monster->sound_location= object->location;
1909+ monster->sound_polygon_index= object->polygon;
1910+ monster->sound_location.z+= definition->height - (definition->height>>1);
1911+
1912+ if (media)
1913+ {
1914+ world_point3d location= object->location;
1915+ short media_effect_type= NONE;
1916+
1917+ location.z= media->height;
1918+ if (old_height>=media->height && object->location.z<media->height)
1919+ {
1920+ media_effect_type= _large_media_detonation_effect;
1921+ }
1922+ if (old_height<media->height && object->location.z>=media->height)
1923+ {
1924+ media_effect_type= _large_media_emergence_effect;
1925+ }
1926+
1927+ if (media_effect_type!=NONE)
1928+ {
1929+ short effect_type= NONE;
1930+
1931+ get_media_detonation_effect(polygon->media_index, media_effect_type, &effect_type);
1932+ new_effect(&location, object->polygon, effect_type, object->facing);
1933+ }
1934+ }
1935+}
1936+
1937+static void update_monster_physics_model(
1938+ short monster_index)
1939+{
1940+ struct monster_data *monster= get_monster_data(monster_index);
1941+ struct monster_definition *definition= get_monster_definition(monster->type);
1942+ struct object_data *object= get_object_data(monster->object_index);
1943+
1944+ if (monster->external_velocity)
1945+ {
1946+ world_point3d new_location= object->location;
1947+ world_distance adjusted_floor_height, adjusted_ceiling_height;
1948+ angle negative_facing= NORMALIZE_ANGLE(HALF_CIRCLE+object->facing);
1949+ struct polygon_data *polygon;
1950+ short supporting_polygon_index;
1951+
1952+ /* move the monster */
1953+ translate_point2d((world_point2d*)&new_location, monster->external_velocity, negative_facing);
1954+ keep_line_segment_out_of_walls(object->polygon, &object->location, &new_location,
1955+ 0, definition->height, &adjusted_floor_height, &adjusted_ceiling_height, &supporting_polygon_index);
1956+ if (legal_monster_move(monster_index, negative_facing, &new_location)==NONE)
1957+ {
1958+ short old_polygon_index= object->polygon;
1959+
1960+ if (translate_map_object(monster->object_index, &new_location, NONE)) monster_moved(monster_index, old_polygon_index);
1961+ }
1962+
1963+ /* slow him down if heユs touching the ground or flying */
1964+ polygon= get_polygon_data(object->polygon);
1965+ if (object->location.z<=polygon->floor_height || (definition->flags&(_monster_flys|_monster_floats)))
1966+ {
1967+ if ((monster->external_velocity-= MONSTER_EXTERNAL_DECELERATION)<MONSTER_MINIMUM_EXTERNAL_VELOCITY)
1968+ {
1969+ monster->external_velocity= 0;
1970+ }
1971+ }
1972+ }
1973+}
1974+
1975+static void monster_needs_path(
1976+ short monster_index,
1977+ bool immediately)
1978+{
1979+ struct monster_data *monster= get_monster_data(monster_index);
1980+
1981+ if (monster->path!=NONE && immediately) delete_path(monster->path), monster->path= NONE;
1982+ if (monster->action==_monster_is_moving && immediately) set_monster_action(monster_index, _monster_is_stationary);
1983+ SET_MONSTER_NEEDS_PATH_STATUS(monster, true);
1984+}
1985+
1986+void set_monster_mode(
1987+ short monster_index,
1988+ short new_mode,
1989+ short target_index)
1990+{
1991+ struct monster_data *monster= get_monster_data(monster_index);
1992+
1993+ /* if we were locked on a monster in our own polygon and we lost him then we donユt have a path
1994+ and going anywhere would be dangerous so we need to ask for a new path */
1995+ if (monster->mode==_monster_locked&&new_mode!=_monster_locked&&monster->path==NONE) monster_needs_path(monster_index, false);
1996+
1997+ switch (new_mode)
1998+ {
1999+ case _monster_locked:
2000+ (void)get_monster_data(target_index); /* for bounds checking only */
2001+ monster->target_index= target_index;
2002+ CLEAR_TARGET_DAMAGE_FLAG(monster);
2003+// if (target_index==local_player->monster_index)
2004+// dprintf("monster #%d is locked on new target #%d;g;", monster_index, target_index);
2005+// switch (monster->type)
2006+// {
2007+// case _civilian_crew: case _civilian_engineering: case _civilian_science: case _civilian_security:
2008+// dprintf("monster #%d is locked on new target #%d;g;", monster_index, target_index);
2009+// }
2010+ break;
2011+
2012+ case _monster_losing_lock: /* target_index ignored, but still valid */
2013+ case _monster_lost_lock:
2014+ (void)get_monster_data(monster->target_index); /* for bounds checking only */
2015+ break;
2016+
2017+ case _monster_unlocked:
2018+ monster->target_index= NONE;
2019+ break;
2020+
2021+ default:
2022+ assert(false);
2023+ break;
2024+ }
2025+
2026+ monster->mode= new_mode;
2027+}
2028+
2029+/* this function decides what the given monster actually wants to do, and then generates a path
2030+ to get him there; if a monster who has lost lock calls this function, he will be forced to
2031+ wander randomly or follow a guard path. */
2032+static void generate_new_path_for_monster(
2033+ short monster_index)
2034+{
2035+ struct monster_data *monster= get_monster_data(monster_index);
2036+ struct object_data *object= get_object_data(monster->object_index);
2037+ struct monster_definition *definition= get_monster_definition(monster->type);
2038+ struct monster_pathfinding_data data;
2039+ short destination_polygon_index;
2040+ world_point2d *destination;
2041+ world_vector2d bias;
2042+
2043+ /* delete this monsterユs old path, if one exists, and clear the need path flag */
2044+ if (monster->path!=NONE) delete_path(monster->path), monster->path= NONE;
2045+ SET_MONSTER_NEEDS_PATH_STATUS(monster, false);
2046+
2047+ switch (monster->mode)
2048+ {
2049+ case _monster_losing_lock:
2050+ /* our target is out of sight, but weユre still zen-ing his position until we run out
2051+ of intelligence points */
2052+ case _monster_locked:
2053+ {
2054+ struct monster_data *target= get_monster_data(monster->target_index);
2055+ struct object_data *target_object= get_object_data(target->object_index);
2056+
2057+ if (definition->random_sound_mask && !(global_random()&definition->random_sound_mask)) play_object_sound(monster->object_index, definition->random_sound);
2058+
2059+ /* if we canユt attack, run away, otherwise go for the target */
2060+ if (definition->flags&_monster_cannot_attack)
2061+ {
2062+ // LP changed: unnecessary to interrupt for this
2063+ // dprintf("%p", monster);
2064+ destination= (world_point2d *) &bias;
2065+ bias.i= object->location.x - target_object->location.x;
2066+ bias.j= object->location.y - target_object->location.y;
2067+ destination_polygon_index= NONE;
2068+ }
2069+ else
2070+ {
2071+ /* if we still have lock, just build a new path and keep charging */
2072+ destination= (world_point2d *) &target_object->location;
2073+ destination_polygon_index= MONSTER_IS_PLAYER(target) ?
2074+ get_polygon_index_supporting_player(monster->target_index) :
2075+ target_object->polygon;
2076+ }
2077+ break;
2078+ }
2079+
2080+ case _monster_lost_lock:
2081+ /* if we lost lock during this path and we went as far as we could go, unlock */
2082+ set_monster_mode(monster_index, _monster_unlocked, NONE);
2083+// dprintf("monster #%d lost lock and reached end of path;g;", monster_index);
2084+ case _monster_unlocked:
2085+ /* if weユre unlocked and need a new path, follow our guard path if we have one and
2086+ run around randomly if we donユt */
2087+ if ((destination_polygon_index= monster->goal_polygon_index)!=NONE)
2088+ {
2089+ destination= &get_polygon_data(destination_polygon_index)->center;
2090+ }
2091+ else
2092+ {
2093+ destination= (world_point2d *) NULL;
2094+ }
2095+ break;
2096+
2097+ default:
2098+ assert(false);
2099+ break;
2100+ }
2101+
2102+// dprintf("#%d: generating new %spath for monster #%d;g;", dynamic_world->tick_count, destination?"":"random ", monster_index);
2103+
2104+ data.definition= definition;
2105+ data.monster= monster;
2106+ data.cross_zone_boundaries= destination_polygon_index==NONE ? false : true;
2107+
2108+ monster->path= new_path((world_point2d *)&object->location, object->polygon, destination,
2109+ destination_polygon_index, 3*definition->radius, monster_pathfinding_cost_function, &data);
2110+ if (monster->path==NONE)
2111+ {
2112+ if (monster->action!=_monster_is_being_hit || MONSTER_IS_DYING(monster)) set_monster_action(monster_index, _monster_is_stationary);
2113+ set_monster_mode(monster_index, _monster_unlocked, NONE);
2114+ }
2115+ else
2116+ {
2117+ advance_monster_path(monster_index);
2118+ }
2119+}
2120+
2121+
2122+/* somebody just did damage to us; see if we should start attacking them or not. berserk
2123+ monsters always switch targets. this is where we check to see if we go berserk, right?
2124+ monster->vitality has already been changed (a monster who just bumped into another monster
2125+ also calls this, with a delta_vitality of zero). returns true if an attack was started. */
2126+static bool switch_target_check(
2127+ short monster_index,
2128+ short attacker_index,
2129+ short delta_vitality)
2130+{
2131+ struct monster_data *monster= get_monster_data(monster_index);
2132+ bool switched_target= false;
2133+
2134+ if (!MONSTER_IS_PLAYER(monster) && !MONSTER_IS_DYING(monster)) /* donユt mess with players or dying monsters */
2135+ {
2136+ if (MONSTER_HAS_VALID_TARGET(monster) && monster->target_index==attacker_index)
2137+ {
2138+ /* if we didnユt know where our target was and he just shot us, we sort of like, know
2139+ where he is now */
2140+ if (monster->mode==_monster_losing_lock)
2141+ {
2142+ set_monster_mode(monster_index, _monster_locked, attacker_index);
2143+ monster_needs_path(monster_index, false);
2144+ }
2145+
2146+ /* if weユre already after this guy and he just did damage to us, remember that */
2147+ if (delta_vitality)
2148+ SET_TARGET_DAMAGE_FLAG(monster);
2149+
2150+ switched_target= true;
2151+ }
2152+ else
2153+ {
2154+ struct monster_definition *definition= get_monster_definition(monster->type);
2155+ short target_index= MONSTER_HAS_VALID_TARGET(monster) ? monster->target_index : NONE;
2156+ struct monster_data *attacker= get_monster_data(attacker_index);
2157+ short attacker_attitude, target_attitude;
2158+
2159+ CLEAR_TARGET_DAMAGE_FLAG(monster);
2160+
2161+ if (!MONSTER_IS_DYING(attacker) && !(definition->flags&_monster_cannot_attack))
2162+ {
2163+ /* if our attacker is an enemy (or a neutral doing non-zero damage or we are berserk) and
2164+ a) weユre inactive, or,
2165+ b) idle, or,
2166+ c) unlocked, or,
2167+ d) our current target has not done any damage, or,
2168+// e) attacker is an enemy and our current target is neutral or friendly, or,
2169+// f) attacker is a neutral and our current target is friendly, or,
2170+ g) we canユt attack and somebody just did damage to us
2171+ then go kick his ass. */
2172+ attacker_attitude= get_monster_attitude(monster_index, attacker_index);
2173+ if (target_index!=NONE) target_attitude= get_monster_attitude(monster_index, target_index);
2174+ if (TYPE_IS_ENEMY(definition, attacker->type) ||
2175+ (TYPE_IS_NEUTRAL(definition, attacker->type)&&delta_vitality) ||
2176+ MONSTER_IS_BERSERK(monster))
2177+ {
2178+ if (!MONSTER_IS_ACTIVE(monster) ||
2179+ MONSTER_IS_IDLE(monster) ||
2180+ monster->mode!=_monster_locked ||
2181+ !TARGET_HAS_DONE_DAMAGE(monster))
2182+// (attacker_attitude==_hostile&&target_attitude!=_hostile) ||
2183+// (attacker_attitude==_neutral&&target_attitude==_friendly) ||
2184+// (delta_vitality&&(definition->flags&_monster_cannot_attack)))
2185+ {
2186+ change_monster_target(monster_index, attacker_index);
2187+ if (delta_vitality) SET_TARGET_DAMAGE_FLAG(monster);
2188+ switched_target= true;
2189+ }
2190+ }
2191+ }
2192+ }
2193+ }
2194+
2195+ return switched_target;
2196+}
2197+
2198+static short get_monster_attitude(
2199+ short monster_index,
2200+ short target_index)
2201+{
2202+ struct monster_data *monster= get_monster_data(monster_index);
2203+ struct monster_definition *definition= get_monster_definition(monster->type);
2204+ struct monster_data *target= get_monster_data(target_index);
2205+ short target_type= target->type;
2206+ short attitude;
2207+
2208+ /* berserk monsters are hostile toward everything */
2209+ if (TYPE_IS_ENEMY(definition, target_type) || MONSTER_IS_BERSERK(monster) ||
2210+ (MONSTER_HAS_VALID_TARGET(monster) && monster->target_index==target_index) ||
2211+ ((definition->_class&_class_human_civilian) && MONSTER_IS_PLAYER(target) && dynamic_world->civilians_killed_by_players>=CIVILIANS_KILLED_BY_PLAYER_THRESHHOLD))
2212+ {
2213+ attitude= _hostile;
2214+ }
2215+ else
2216+ {
2217+ attitude= (TYPE_IS_FRIEND(definition, target_type)) ? _friendly : _neutral;
2218+ }
2219+
2220+// if ((definition->_class&_class_human_civilian) && MONSTER_IS_PLAYER(target))
2221+// {
2222+// dprintf("#%d vs. #%d ==> #%d", monster_index, target_index, attitude);
2223+// }
2224+
2225+ return attitude;
2226+}
2227+
2228+/* find_closest_appropriate_target() tries to do just that. it is a little broken in that it
2229+ treats all monsters in a given polygon as if they were the same distance away, which could
2230+ result in strange behavior. the assumption is that if there is a more accessable hostile monster
2231+ nearby, that monster will attack and thus end a possible wild goose chase. if there is a
2232+ closer hostile target which the aggressor subsequently attempts to move through, he will
2233+ change lock and attack the obstruction instead, which will help minimize weirdness.
2234+ full_circle is passed directly to clear_line_of_sight(). */
2235+short find_closest_appropriate_target(
2236+ short aggressor_index,
2237+ bool full_circle)
2238+{
2239+ struct monster_data *aggressor= get_monster_data(aggressor_index);
2240+ struct monster_definition *definition= get_monster_definition(aggressor->type);
2241+ short closest_hostile_target_index= NONE;
2242+
2243+ if (MONSTER_IS_ACTIVE(aggressor))
2244+ {
2245+ int32 flood_flags= _pass_one_zone_border;
2246+ short polygon_index= get_object_data(get_monster_data(aggressor_index)->object_index)->polygon;
2247+
2248+ /* flood out from the aggressor monsterユs polygon, searching through the object lists of all
2249+ polygons we encounter */
2250+ polygon_index= flood_map(polygon_index, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
2251+ while (polygon_index!=NONE && closest_hostile_target_index==NONE)
2252+ {
2253+ short object_index;
2254+ struct object_data *object;
2255+
2256+ /* loop through all objects in this polygon looking for hostile monsters we can see */
2257+ for (object_index= get_polygon_data(polygon_index)->first_object; object_index!=NONE; object_index= object->next_object)
2258+ {
2259+ object= get_object_data(object_index);
2260+ if (GET_OBJECT_OWNER(object)==_object_is_monster && OBJECT_IS_VISIBLE(object))
2261+ {
2262+ short target_monster_index= object->permutation;
2263+ struct monster_data *target_monster= get_monster_data(target_monster_index);
2264+
2265+ if (!MONSTER_IS_DYING(target_monster) && target_monster_index!=aggressor_index)
2266+ {
2267+ if (get_monster_attitude(aggressor_index, target_monster_index)==_hostile)
2268+ {
2269+ if (((definition->flags&_monster_is_omniscent) || clear_line_of_sight(aggressor_index, target_monster_index, full_circle)) &&
2270+ (MONSTER_IS_ACTIVE(target_monster) || MONSTER_IS_PLAYER(target_monster) || (static_world->environment_flags&_environment_rebellion)))
2271+ {
2272+ /* found hostile, live, visible monster */
2273+ closest_hostile_target_index= target_monster_index;
2274+ break;
2275+ }
2276+ }
2277+ }
2278+ }
2279+ }
2280+
2281+ polygon_index= flood_map(NONE, INT32_MAX, monster_activation_flood_proc, _flagged_breadth_first, &flood_flags);
2282+ }
2283+ }
2284+ else
2285+ {
2286+ short player_index;
2287+
2288+ /* if this monster is deactivated, only seeing a player will activate him */
2289+
2290+ for (player_index= 0; player_index<dynamic_world->player_count; ++player_index)
2291+ {
2292+ struct player_data *player= get_player_data(player_index);
2293+
2294+ if (get_monster_attitude(aggressor_index, player->monster_index)==_hostile &&
2295+ clear_line_of_sight(aggressor_index, player->monster_index, full_circle))
2296+ {
2297+ closest_hostile_target_index= player->monster_index;
2298+
2299+ break;
2300+ }
2301+ }
2302+ }
2303+
2304+ return closest_hostile_target_index;
2305+}
2306+
2307+/* if ヤfull_circleユ is true, the monster can see in all directions. if ヤfull_circleユ is false
2308+ the monster respects his visual_arc and current facing. clear_line_of_sight() is implemented
2309+ wholly in 2D and only attempts to connect the centers of the two monsters by a line. */
2310+static bool clear_line_of_sight(
2311+ short viewer_index,
2312+ short target_index,
2313+ bool full_circle)
2314+{
2315+ struct monster_data *viewer= get_monster_data(viewer_index);
2316+ struct object_data *viewer_object= get_object_data(viewer->object_index);
2317+ struct monster_definition *viewer_definition= get_monster_definition(viewer->type);
2318+ struct monster_data *target= get_monster_data(target_index);
2319+ struct object_data *target_object= get_object_data(target->object_index);
2320+ bool target_visible= true;
2321+
2322+ {
2323+ world_point3d *origin= &viewer_object->location;
2324+ world_point3d *destination= &target_object->location;
2325+ // LP change: made this long-distance friendly
2326+ int32 dx= int32(destination->x)-int32(origin->x);
2327+ int32 dy= int32(destination->y)-int32(origin->y);
2328+ world_distance dz= destination->z-origin->z;
2329+ int32 distance2d= GUESS_HYPOTENUSE(ABS(dx), ABS(dy));
2330+
2331+ /* if we canユt see full circle, make sure the target is in our visual arc */
2332+ if (!full_circle)
2333+ {
2334+ angle theta= arctangent(dx, dy)-viewer_object->facing;
2335+ angle phi= arctangent(distance2d, ABS(dz));
2336+
2337+ if (ABS(theta)>viewer_definition->half_visual_arc) target_visible= false;
2338+ if (phi>=viewer_definition->half_vertical_visual_arc&&phi<FULL_CIRCLE-viewer_definition->half_vertical_visual_arc) target_visible= false;
2339+ }
2340+
2341+ /* we canユt see some transfer modes */
2342+ switch (target_object->transfer_mode)
2343+ {
2344+ case _xfer_invisibility:
2345+ case _xfer_subtle_invisibility:
2346+ if (distance2d>viewer_definition->dark_visual_range) target_visible= false;
2347+ break;
2348+ }
2349+
2350+ /* make sure the target is within our visual_range (taking any of his active
2351+ effects, i.e. invisibility, into account) and that he isnユt standing in a
2352+ dark polygon beyond our dark_visual_range. */
2353+ if (target_visible)
2354+ {
2355+ if (distance2d>viewer_definition->visual_range) // || (distance2d>viewer_definition->dark_visual_range&&get_object_light_intensity(target->object_index)<=LOW_LIGHT_INTENSITY))
2356+ {
2357+ target_visible= false;
2358+ }
2359+ }
2360+
2361+ /* make sure there are no non-transparent lines between the viewer and the target */
2362+ if (target_visible)
2363+ {
2364+ short polygon_index= viewer_object->polygon;
2365+ short line_index;
2366+
2367+ do
2368+ {
2369+ line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)origin, (world_point2d *)destination);
2370+ if (line_index!=NONE)
2371+ {
2372+ if (LINE_IS_TRANSPARENT(get_line_data(line_index)))
2373+ {
2374+ /* transparent line, find adjacent polygon */
2375+ polygon_index= find_adjacent_polygon(polygon_index, line_index);
2376+ // LP change: make no polygon act like a non-transparent line
2377+ if (polygon_index == NONE) target_visible= false;
2378+ }
2379+ else
2380+ {
2381+ /* non-transparent line, target not visible */
2382+ target_visible= false;
2383+ }
2384+ }
2385+ else
2386+ {
2387+ /* we got to the targetユs (x,y) location, but weユre in a different polygon;
2388+ heユs invisible */
2389+ if (polygon_index!=target_object->polygon) target_visible= false;
2390+ }
2391+ }
2392+ while (target_visible&&line_index!=NONE);
2393+ }
2394+ }
2395+
2396+ return target_visible;
2397+}
2398+
2399+/* lock the given monster onto the given target, playing a locking sound if the monster
2400+ previously didnユt have a lock */
2401+void change_monster_target(
2402+ short monster_index,
2403+ short target_index)
2404+{
2405+ /* locking on ourselves would be cool, but ... */
2406+ if (monster_index!=target_index)
2407+ {
2408+ struct monster_data *monster= get_monster_data(monster_index);
2409+ struct monster_definition *definition= get_monster_definition(monster->type);
2410+
2411+ if (target_index!=NONE)
2412+ {
2413+ /* only active monsters can have lock, so activate inactive monsters */
2414+ if (!MONSTER_IS_ACTIVE(monster)) activate_monster(monster_index);
2415+
2416+ /* play activation sounds (including activating on a friendly) */
2417+ if (monster->target_index!=target_index && TYPE_IS_FRIEND(definition, get_monster_data(target_index)->type))
2418+ {
2419+ play_object_sound(monster->object_index, definition->friendly_activation_sound);
2420+ }
2421+ else
2422+ {
2423+ if (monster->mode==_monster_unlocked) play_object_sound(monster->object_index, definition->activation_sound);
2424+ }
2425+
2426+ /* instantiate the new target and ask for a new path */
2427+ if (MONSTER_HAS_VALID_TARGET(monster) && target_index!=monster->target_index) CLEAR_TARGET_DAMAGE_FLAG(monster);
2428+ monster_needs_path(monster_index, false);
2429+ set_monster_mode(monster_index, _monster_locked, target_index);
2430+ }
2431+ else
2432+ {
2433+ if (MONSTER_IS_ACTIVE(monster))
2434+ {
2435+ /* no target, if weユre not unlocked mark us as unlocked and ask for a new path */
2436+ if (monster->mode!=_monster_unlocked)
2437+ {
2438+// dprintf("monster #%d was locked on NONE;g;", monster_index);
2439+
2440+ set_monster_mode(monster_index, _monster_unlocked, NONE);
2441+ monster_needs_path(monster_index, false);
2442+ }
2443+ }
2444+ }
2445+ }
2446+}
2447+
2448+static void handle_moving_or_stationary_monster(
2449+ short monster_index)
2450+{
2451+ struct monster_data *monster= get_monster_data(monster_index);
2452+ struct object_data *object= get_object_data(monster->object_index);
2453+ struct monster_definition *definition= get_monster_definition(monster->type);
2454+
2455+ if (monster->path==NONE && monster->mode!=_monster_locked && monster->action==_monster_is_stationary)
2456+ {
2457+ /* stationary, unlocked monsters without paths cannot move */
2458+ monster_needs_path(monster_index, false);
2459+ }
2460+ else
2461+ {
2462+ world_distance distance_moved= definition->speed;
2463+
2464+ /* base speed on difficulty level (for aliens) and berserk status */
2465+ if (definition->flags&_monster_is_alien)
2466+ {
2467+ switch (dynamic_world->game_information.difficulty_level)
2468+ {
2469+ case _wuss_level: distance_moved-= distance_moved>>3; break;
2470+ case _easy_level: distance_moved-= distance_moved>>4; break;
2471+ case _major_damage_level: distance_moved+= distance_moved>>3; break;
2472+ case _total_carnage_level: distance_moved+= distance_moved>>2; break;
2473+ }
2474+ }
2475+ if (MONSTER_IS_BERSERK(monster)) distance_moved+= (distance_moved>>1);
2476+
2477+ if (monster->action!=_monster_is_waiting_to_attack_again)
2478+ {
2479+ if (translate_monster(monster_index, distance_moved))
2480+ {
2481+ /* we moved: _monster_is_stationary becomes _monster_is_moving */
2482+ if (monster->action==_monster_is_stationary) set_monster_action(monster_index, _monster_is_moving);
2483+ }
2484+ else
2485+ {
2486+ /* we couldnユt move: _monster_is_moving becomes _monster_is_stationary */
2487+ if (monster->action==_monster_is_moving) set_monster_action(monster_index, _monster_is_stationary);
2488+ monster->ticks_since_attack+= 1; /* attacks occur twice as frequently if we canユt move (damnit!) */
2489+ }
2490+ }
2491+ else
2492+ {
2493+ monster->ticks_since_attack+= 1;
2494+ }
2495+
2496+ /* whether we moved or not, see if we can attack if we have lock */
2497+ monster->ticks_since_attack+= MONSTER_IS_BERSERK(monster) ? 3 : 1;
2498+ if (OBJECT_WAS_ANIMATED(object) && monster->mode==_monster_locked)
2499+ {
2500+ short attack_frequency= definition->attack_frequency;
2501+
2502+ if (definition->flags&_monster_is_alien)
2503+ {
2504+ switch (dynamic_world->game_information.difficulty_level)
2505+ {
2506+ case _wuss_level: attack_frequency= 3*attack_frequency; break;
2507+ case _easy_level: attack_frequency= 2*attack_frequency; break;
2508+ case _major_damage_level: attack_frequency= attack_frequency>>1; break;
2509+ case _total_carnage_level: attack_frequency= attack_frequency>>2; break;
2510+ }
2511+ }
2512+
2513+ if (monster->ticks_since_attack>attack_frequency)
2514+ {
2515+ if (try_monster_attack(monster_index))
2516+ {
2517+ /* activate with lock nearby monsters on our target */
2518+ activate_nearby_monsters(monster->target_index, monster_index, _pass_one_zone_border);
2519+ }
2520+ else
2521+ {
2522+ if (monster->action==_monster_is_waiting_to_attack_again)
2523+ {
2524+ set_monster_action(monster_index, _monster_is_stationary);
2525+ monster_needs_path(monster_index, true);
2526+ }
2527+ }
2528+ }
2529+ }
2530+ }
2531+}
2532+
2533+void set_monster_action(
2534+ short monster_index,
2535+ short action)
2536+{
2537+ struct monster_data *monster= get_monster_data(monster_index);
2538+ struct monster_definition *definition= get_monster_definition(monster->type);
2539+ shape_descriptor shape;
2540+
2541+ /* what shape should we use? */
2542+ if (action==_monster_is_dying_flaming)
2543+ {
2544+ shape= FLAMING_DYING_SHAPE;
2545+ }
2546+ else
2547+ {
2548+ switch (action)
2549+ {
2550+ case _monster_is_waiting_to_attack_again:
2551+ case _monster_is_stationary: shape= definition->stationary_shape; break;
2552+ case _monster_is_moving: shape= definition->moving_shape; break;
2553+ case _monster_is_attacking_close: shape= definition->melee_attack.attack_shape; break;
2554+ case _monster_is_attacking_far: shape= definition->ranged_attack.attack_shape; break;
2555+ case _monster_is_being_hit: shape= definition->hit_shapes; break;
2556+ case _monster_is_dying_hard: shape= definition->hard_dying_shape; break;
2557+ case _monster_is_dying_soft: shape= definition->soft_dying_shape; break;
2558+ case _monster_is_teleporting_in: shape= definition->teleport_in_shape; break;
2559+ case _monster_is_teleporting_out: shape= definition->teleport_out_shape; break;
2560+ default: dprintf("what is monster action #%d?", action); assert(false); break;
2561+ }
2562+
2563+ shape= shape==UNONE ? UNONE : BUILD_DESCRIPTOR(definition->collection, shape);
2564+ }
2565+
2566+ if (shape!=UNONE)
2567+ {
2568+ /* only set the action of the shape is not UNONE */
2569+ monster->action= action;
2570+ set_object_shape_and_transfer_mode(monster->object_index, shape, NONE);
2571+
2572+ /* if this monster does shrapnel damage, do it */
2573+ if (action==_monster_is_dying_hard && (definition->flags&_monster_has_delayed_hard_death))
2574+ {
2575+ cause_shrapnel_damage(monster_index);
2576+ }
2577+
2578+ if ((definition->flags&_monster_has_nuclear_hard_death) && action==_monster_is_dying_hard)
2579+ {
2580+ start_fade(_fade_long_bright);
2581+ SoundManager::instance()->PlayLocalSound(Sound_Exploding());
2582+ }
2583+ }
2584+}
2585+
2586+/* do whatever needs to be done when this monster dies and remove it from the monster list */
2587+static void kill_monster(
2588+ short monster_index)
2589+{
2590+ struct monster_data *monster= get_monster_data(monster_index);
2591+ struct monster_definition *definition= get_monster_definition(monster->type);
2592+ struct object_data *object= get_object_data(monster->object_index);
2593+ shape_descriptor shape;
2594+
2595+ switch (monster->action)
2596+ {
2597+ case _monster_is_dying_soft:
2598+ shape= definition->soft_dead_shapes==UNONE ? UNONE : BUILD_DESCRIPTOR(definition->collection, definition->soft_dead_shapes);
2599+ break;
2600+ case _monster_is_dying_hard:
2601+ shape= definition->hard_dead_shapes==UNONE ? UNONE : BUILD_DESCRIPTOR(definition->collection, definition->hard_dead_shapes);
2602+ break;
2603+ case _monster_is_dying_flaming:
2604+ shape= FLAMING_DEAD_SHAPE;
2605+ break;
2606+
2607+ default:
2608+ assert(false);
2609+ break;
2610+ }
2611+
2612+ /* add an item if weユre supposed to be carrying something */
2613+ if (definition->carrying_item_type!=NONE && monster->action==_monster_is_dying_soft)
2614+ {
2615+ world_distance radius, height;
2616+ world_point3d random_point;
2617+ short random_polygon_index;
2618+
2619+ get_monster_dimensions(monster_index, &radius, &height);
2620+ random_point_on_circle(&object->location, object->polygon, radius, &random_point, &random_polygon_index);
2621+ if (random_polygon_index!=NONE)
2622+ {
2623+ struct polygon_data *random_polygon= get_polygon_data(random_polygon_index);
2624+ struct object_location location;
2625+
2626+ switch (random_polygon->type)
2627+ {
2628+ case _polygon_is_platform:
2629+ case _polygon_is_item_impassable:
2630+ case _polygon_is_monster_impassable:
2631+ case _polygon_is_teleporter:
2632+ break;
2633+
2634+ default:
2635+ location.polygon_index= random_polygon_index;
2636+ location.p.x= random_point.x, location.p.y= random_point.y, location.p.z= 0;
2637+ location.yaw= 0;
2638+ location.flags= 0;
2639+ new_item(&location, definition->carrying_item_type);
2640+ break;
2641+ }
2642+ }
2643+ }
2644+
2645+ /* stuff in an appropriate dead shape (or remove our object if we donユt have a dead shape) */
2646+ if (shape==UNONE)
2647+ {
2648+ remove_map_object(monster->object_index);
2649+ }
2650+ else
2651+ {
2652+ turn_object_to_shit(monster->object_index);
2653+ randomize_object_sequence(monster->object_index, shape);
2654+ }
2655+
2656+ /* recover original type and notify the object stuff a monster died */
2657+ if (monster->flags&_monster_was_promoted) monster->type-= 1;
2658+ if (monster->flags&_monster_was_demoted) monster->type+= 1;
2659+ object_was_just_destroyed(_object_is_monster, monster->type);
2660+
2661+ L_Invalidate_Monster(monster_index);
2662+ MARK_SLOT_AS_FREE(monster);
2663+}
2664+
2665+/* move the monster along his current heading; if he reaches the center of his destination square,
2666+ then point him at the next square and send him off. this used to chuck if the monster moved
2667+ too far during a certain turn (which was completely possible when the player was wearing the
2668+ red cloak in Pathways), but that was fixed. i just recoded this for marathon and it looks
2669+ a hell of a lot better now. */
2670+static bool translate_monster(
2671+ short monster_index,
2672+ world_distance distance)
2673+{
2674+ struct monster_data *monster= get_monster_data(monster_index);
2675+ struct object_data *object= get_object_data(monster->object_index);
2676+ struct monster_definition *definition= get_monster_definition(monster->type);
2677+ world_point3d new_location;
2678+ short obstacle_index;
2679+ bool legal_move= false;
2680+
2681+ new_location= object->location;
2682+ translate_point2d((world_point2d *)&new_location, distance, object->facing);
2683+
2684+ /* find out where weユre going and see if we could actually move there */
2685+ if ((obstacle_index= legal_monster_move(monster_index, object->facing, &new_location))==NONE)
2686+ {
2687+ /* legal move: see if there is a platform that we have to open or wait for,
2688+ if not move, if so, wait */
2689+
2690+ short feature_index;
2691+ short relevant_polygon_index;
2692+
2693+ legal_move= true;
2694+ switch (find_obstructing_terrain_feature(monster_index, &feature_index, &relevant_polygon_index))
2695+ {
2696+ case _entering_platform_polygon:
2697+ switch (monster_can_enter_platform(feature_index, relevant_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
2698+ {
2699+ case _platform_will_never_be_accessable:
2700+ monster_needs_path(monster_index, true);
2701+ break;
2702+
2703+ case _platform_will_be_accessable:
2704+ /* we avoid vidding the door by only trying to open it every door_retry_mask+1 ticks */
2705+ if (!(dynamic_world->tick_count&definition->door_retry_mask)) try_and_change_platform_state(feature_index, true);
2706+ SET_MONSTER_IDLE_STATUS(monster, true);
2707+ legal_move= false;
2708+ break;
2709+
2710+ /* _platform_is_accessable */
2711+ }
2712+ break;
2713+
2714+ case _leaving_platform_polygon:
2715+ switch (monster_can_leave_platform(feature_index, relevant_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
2716+ {
2717+ case _exit_will_never_be_accessable:
2718+ monster_needs_path(monster_index, true);
2719+ break;
2720+
2721+ case _exit_will_be_accessable:
2722+ SET_MONSTER_IDLE_STATUS(monster, true);
2723+ legal_move= false;
2724+ break;
2725+
2726+ /* _exit_is_accessable, ignored */
2727+ }
2728+ break;
2729+
2730+ case _flying_or_floating_transition:
2731+ /* there is a wall in our way which we have to rise (or fall) along, so donユt
2732+ go anywhere unless weユre over it (or under it) */
2733+ if (ABS(object->location.z-monster->desired_height)>MINIMUM_FLOATING_HEIGHT) legal_move= false;
2734+ break;
2735+
2736+ case _standing_on_sniper_ledge:
2737+ /* weユve been told to freeze on a sniper ledge (no saving throw) */
2738+ legal_move= false;
2739+ break;
2740+ }
2741+
2742+ if (legal_move)
2743+ {
2744+ if ((monster->path_segment_length-= distance)<=0)
2745+ {
2746+ advance_monster_path(monster_index);
2747+ }
2748+ else
2749+ {
2750+ short old_polygon_index= object->polygon;
2751+
2752+ /* update the monsterユs object to reflect his new position */
2753+ if (translate_map_object(monster->object_index, &new_location, NONE)) monster_moved(monster_index, old_polygon_index);
2754+ }
2755+
2756+ legal_move= true;
2757+ }
2758+ }
2759+ else
2760+ {
2761+ struct object_data *obstacle_object= get_object_data(obstacle_index);
2762+
2763+ if (GET_OBJECT_OWNER(obstacle_object)==_object_is_monster)
2764+ {
2765+ struct monster_data *obstacle_monster= get_monster_data(obstacle_object->permutation);
2766+
2767+ /* we collided with another monster: see if we want to attack him; if not, see if we
2768+ can attack his current target (if he is locked or losing_lock); if not, drop lock
2769+ and ask for a new path. */
2770+
2771+ if (!TYPE_IS_ENEMY(definition, obstacle_monster->type) && !(MONSTER_HAS_VALID_TARGET(monster)&&monster->target_index==obstacle_object->permutation) &&
2772+ !MONSTER_IS_BERSERK(monster))
2773+ {
2774+ if (!MONSTER_IS_PLAYER(obstacle_monster))
2775+ {
2776+ if (monster->mode!=_monster_locked)
2777+ {
2778+ if (!MONSTER_HAS_VALID_TARGET(obstacle_monster) || !switch_target_check(monster_index, obstacle_monster->target_index, 0))
2779+ {
2780+ if (monster->mode==_monster_unlocked && !(global_random()&OBSTRUCTION_DEACTIVATION_MASK) &&
2781+ (monster->goal_polygon_index==NONE || monster->goal_polygon_index==object->polygon))
2782+ {
2783+ deactivate_monster(monster_index);
2784+ }
2785+ else
2786+ {
2787+ monster_needs_path(monster_index, false);
2788+ if (monster->mode!=_monster_locked)
2789+ {
2790+ /* if weユre not locked, we might want to think about deactivating here, but
2791+ for now we just build a new random path by forcing our state to _unlocked. */
2792+ set_monster_mode(monster_index, _monster_unlocked, NONE);
2793+ // dprintf("monster #%d going unlocked by obstruction;g;", monster_index);
2794+ }
2795+ }
2796+ }
2797+ }
2798+ else
2799+ {
2800+ attempt_evasive_manouvers(monster_index);
2801+ }
2802+ }
2803+
2804+ SET_MONSTER_IDLE_STATUS(monster, true);
2805+ }
2806+ else
2807+ {
2808+ struct monster_definition *obstacle_definition= get_monster_definition(obstacle_monster->type);
2809+ world_distance key_height= obstacle_object->location.z+(obstacle_definition->height>>1);
2810+
2811+ change_monster_target(monster_index, obstacle_object->permutation);
2812+
2813+ /* if weユre a kamakazi and weユre within range, pop */
2814+ if ((definition->flags&_monster_is_kamakazi) &&
2815+ object->location.z<key_height)
2816+ {
2817+ bool in_range = object->location.z+definition->height>key_height;
2818+
2819+ /* if we're short and can't float, take out their knees! */
2820+ if (!in_range && film_profile.allow_short_kamikaze && !(definition->flags&_monster_floats))
2821+ in_range = object->location.z>=obstacle_object->location.z;
2822+
2823+ if (in_range)
2824+ {
2825+ set_monster_action(monster_index, _monster_is_dying_hard);
2826+ monster_died(monster_index);
2827+ }
2828+ }
2829+
2830+ /* if we float and this is our target, go up */
2831+ if (definition->flags&_monster_floats)
2832+ {
2833+ monster->desired_height= obstacle_object->location.z;
2834+ }
2835+ }
2836+ }
2837+ else
2838+ {
2839+ attempt_evasive_manouvers(monster_index); // to avoid the scenery
2840+ }
2841+ }
2842+
2843+ return legal_move;
2844+}
2845+
2846+static bool attempt_evasive_manouvers(
2847+ short monster_index)
2848+{
2849+ struct monster_data *monster= get_monster_data(monster_index);
2850+ struct object_data *object= get_object_data(monster->object_index);
2851+ world_point2d destination= *((world_point2d*)&object->location);
2852+ angle new_facing= NORMALIZE_ANGLE(object->facing + ((global_random()&1) ? QUARTER_CIRCLE : -QUARTER_CIRCLE));
2853+ world_distance original_floor_height= get_polygon_data(object->polygon)->floor_height;
2854+ short polygon_index= object->polygon;
2855+ bool successful= true;
2856+
2857+ translate_point2d(&destination, EVASIVE_MANOUVER_DISTANCE, new_facing);
2858+ do
2859+ {
2860+ short line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)&object->location, &destination);
2861+
2862+ if (line_index==NONE)
2863+ {
2864+ polygon_index= NONE;
2865+ }
2866+ else
2867+ {
2868+ /* if we ran off the map, we failed */
2869+ if (LINE_IS_SOLID(get_line_data(line_index)) || (polygon_index= find_adjacent_polygon(polygon_index, line_index))==NONE)
2870+ {
2871+ polygon_index= NONE;
2872+ successful= false;
2873+ }
2874+ else
2875+ {
2876+ struct polygon_data *polygon= get_polygon_data(polygon_index);
2877+ if (polygon->floor_height!=original_floor_height || polygon->type==_polygon_is_monster_impassable)
2878+ {
2879+ polygon_index= NONE;
2880+ successful= false;
2881+ }
2882+ }
2883+ }
2884+ }
2885+ while (polygon_index!=NONE);
2886+
2887+ if (successful)
2888+ {
2889+ object->facing= new_facing;
2890+ if (monster->path!=NONE) delete_path(monster->path), monster->path= NONE;
2891+ monster->path_segment_length= EVASIVE_MANOUVER_DISTANCE;
2892+ }
2893+
2894+ return successful;
2895+}
2896+
2897+void advance_monster_path(
2898+ short monster_index)
2899+{
2900+ struct monster_data *monster= get_monster_data(monster_index);
2901+ struct object_data *object= get_object_data(monster->object_index);
2902+ world_point2d path_goal;
2903+ bool done= true;
2904+
2905+ if (monster->path==NONE)
2906+ {
2907+ /* only locked monsters in their targetユs polygon can advance without paths */
2908+ if (monster->mode!=_monster_locked || object->polygon!=get_object_data(get_monster_data(monster->target_index)->object_index)->polygon)
2909+ {
2910+ monster_needs_path(monster_index, true);
2911+ return;
2912+ }
2913+ }
2914+ else
2915+ {
2916+ done= move_along_path(monster->path, &path_goal);
2917+ if (done)
2918+ monster->path= NONE;
2919+ }
2920+
2921+ /* if weユre locked without a path, head right for the bastard (heユs in our polygon) */
2922+ if ((done||monster->path==NONE) && monster->mode==_monster_locked)
2923+ {
2924+ struct monster_data *target= get_monster_data(monster->target_index);
2925+ struct object_data *target_object= get_object_data(target->object_index);
2926+
2927+ if (object->polygon==target_object->polygon)
2928+ {
2929+ path_goal= *(world_point2d *)&get_object_data(get_monster_data(monster->target_index)->object_index)->location;
2930+ done= false;
2931+ }
2932+ }
2933+
2934+ if (done)
2935+ {
2936+ /* ask for a new path (never happens to locked monsters) */
2937+ monster_needs_path(monster_index, false);
2938+ if (monster->mode==_monster_unlocked)
2939+ {
2940+ monster->goal_polygon_index= NONE;
2941+ if (MONSTER_TELEPORTS_OUT_WHEN_DEACTIVATED(monster)) deactivate_monster(monster_index);
2942+ }
2943+ }
2944+ else
2945+ {
2946+ /* point ourselves at this new point in the path */
2947+ object->facing= arctangent(path_goal.x-object->location.x, path_goal.y-object->location.y);
2948+ monster->path_segment_length= distance2d(&path_goal, (world_point2d *)&object->location);
2949+ }
2950+}
2951+
2952+static bool try_monster_attack(
2953+ short monster_index)
2954+{
2955+ struct monster_data *monster= get_monster_data(monster_index);
2956+ struct object_data *object= get_object_data(monster->object_index);
2957+ struct monster_definition *definition= get_monster_definition(monster->type);
2958+ short repetitions= NONE;
2959+ short new_action= NONE, obstruction_index= NONE;
2960+ angle theta = 0;
2961+
2962+ if (MONSTER_HAS_VALID_TARGET(monster))
2963+ {
2964+ struct object_data *target_object= get_object_data(get_monster_data(monster->target_index)->object_index);
2965+ world_point3d origin= object->location, destination= target_object->location;
2966+ world_distance range= distance2d((world_point2d *)&origin, (world_point2d *)&destination);
2967+ short polygon_index;
2968+ world_point3d _vector;
2969+
2970+ theta= arctangent(destination.x-origin.x, destination.y-origin.y);
2971+ angle delta_theta= NORMALIZE_ANGLE(theta-object->facing);
2972+
2973+ if (!(definition->flags&_monster_cant_fire_backwards) || (delta_theta<QUARTER_CIRCLE+QUARTER_CIRCLE/2 || delta_theta>FULL_CIRCLE-QUARTER_CIRCLE-QUARTER_CIRCLE/2))
2974+ {
2975+ switch (monster->action)
2976+ {
2977+ case _monster_is_attacking_close:
2978+ case _monster_is_attacking_far:
2979+ new_action= monster->action;
2980+ break;
2981+
2982+ default:
2983+ if (definition->ranged_attack.type!=NONE && range<definition->ranged_attack.range) new_action= _monster_is_attacking_far;
2984+ if (definition->melee_attack.type!=NONE && range<definition->melee_attack.range)
2985+ {
2986+ new_action= _monster_is_attacking_close;
2987+
2988+ if (definition->flags&_monster_chooses_weapons_randomly)
2989+ {
2990+ bool switch_to_ranged = true;
2991+ if (film_profile.validate_random_ranged_attack)
2992+ {
2993+ if (definition->ranged_attack.type == NONE)
2994+ {
2995+ logWarning("Monster chooses weapons randomly, but has no ranged attack");
2996+ definition->flags &= ~_monster_chooses_weapons_randomly;
2997+ switch_to_ranged = false;
2998+ }
2999+ else
3000+ switch_to_ranged = (range<definition->ranged_attack.range);
3001+ }
3002+ if (switch_to_ranged && global_random()&1)
3003+ new_action= _monster_is_attacking_far;
3004+ }
3005+ }
3006+ break;
3007+ }
3008+
3009+ /* if we have a melee attack and we're at short range, use it */
3010+ if (new_action==_monster_is_attacking_close)
3011+ {
3012+ /* make sure this is a valid projectile, that we donユt hit any walls and that whatever
3013+ we did hit is _hostile. */
3014+ polygon_index= position_monster_projectile(monster_index, monster->target_index, &definition->melee_attack, &origin, &destination, &_vector, theta);
3015+ if (preflight_projectile(&origin, polygon_index, &destination, definition->melee_attack.error,
3016+ definition->melee_attack.type, monster_index, monster->type, &obstruction_index))
3017+ {
3018+ if ((obstruction_index!=NONE && get_monster_attitude(monster_index, obstruction_index)==_hostile) ||
3019+ !line_is_obstructed(object->polygon, (world_point2d *) &object->location, target_object->polygon, (world_point2d *) &target_object->location))
3020+ {
3021+ repetitions= definition->melee_attack.repetitions;
3022+ }
3023+ }
3024+ }
3025+ else
3026+ {
3027+ /* make sure we have a ranged attack and our target is within range */
3028+ if (new_action==_monster_is_attacking_far)
3029+ {
3030+ /* make sure this is a valid projectile, that we donユt hit any walls and that whatever
3031+ we did hit is _hostile. */
3032+ polygon_index= position_monster_projectile(monster_index, monster->target_index, &definition->ranged_attack, &origin, &destination, &_vector, theta);
3033+ if (preflight_projectile(&origin, polygon_index, &destination, definition->ranged_attack.error,
3034+ definition->ranged_attack.type, monster_index, monster->type, &obstruction_index))
3035+ {
3036+ if ((obstruction_index!=NONE && get_monster_attitude(monster_index, obstruction_index)==_hostile) ||
3037+ (obstruction_index==NONE && !line_is_obstructed(object->polygon, (world_point2d *) &object->location, target_object->polygon, (world_point2d *) &target_object->location)))
3038+ {
3039+ repetitions= definition->ranged_attack.repetitions;
3040+ }
3041+ }
3042+ }
3043+ }
3044+ }
3045+ }
3046+
3047+ if (repetitions!=NONE)
3048+ {
3049+ /* we can attack; set monster facing, start the attack action and reset ticks_since_attack */
3050+ object->facing= theta;
3051+ if (monster->action!=new_action) /* if weユre already attacking, this is a chained attack */
3052+ {
3053+ switch (dynamic_world->game_information.difficulty_level)
3054+ {
3055+ case _wuss_level: case _easy_level: repetitions>>= 1;
3056+ case _normal_level: repetitions= (repetitions<=1) ? repetitions : repetitions-1; break;
3057+ }
3058+
3059+ set_monster_action(monster_index, new_action);
3060+ monster->attack_repetitions= repetitions;
3061+ }
3062+
3063+ /* on the highest level, hitting a monster in the middle of an attack doesnユt really
3064+ stop him from continuing to attack because ticks_since_attack is never reset */
3065+ switch (dynamic_world->game_information.difficulty_level)
3066+ {
3067+ case _total_carnage_level:
3068+ break;
3069+
3070+ default:
3071+ monster->ticks_since_attack= 0;
3072+ break;
3073+ }
3074+ }
3075+ else
3076+ {
3077+ /* we canユt attack (for whatever reason), halve ticks_since_attack so we try again soon */
3078+ monster->ticks_since_attack= 0;
3079+
3080+ if (obstruction_index!=NONE && get_monster_attitude(monster_index, obstruction_index)==_friendly &&
3081+ MONSTER_IS_PLAYER(get_monster_data(obstruction_index)))
3082+ {
3083+ play_object_sound(monster->object_index, definition->clear_sound);
3084+ }
3085+ }
3086+
3087+ return new_action==NONE ? false : true;
3088+}
3089+
3090+static void execute_monster_attack(
3091+ short monster_index)
3092+{
3093+ struct monster_data *monster= get_monster_data(monster_index);
3094+
3095+ /* we used to assert that the attacking monster was locked, but monsters can be deactivated
3096+ or lose lock during an attack (!) so we just abort if we no longer have a valid target */
3097+ if (MONSTER_HAS_VALID_TARGET(monster))
3098+ {
3099+ struct monster_definition *definition= get_monster_definition(monster->type);
3100+ struct object_data *object= get_object_data(monster->object_index);
3101+ struct attack_definition *attack= (monster->action==_monster_is_attacking_close) ? &definition->melee_attack : &definition->ranged_attack;
3102+ short projectile_polygon_index;
3103+ world_point3d origin= object->location;
3104+ world_point3d _vector;
3105+
3106+ projectile_polygon_index= position_monster_projectile(monster_index, monster->target_index, attack, &origin, (world_point3d *) NULL, &_vector, object->facing);
3107+ if (projectile_polygon_index != NONE)
3108+ new_projectile(&origin, projectile_polygon_index, &_vector, attack->error, attack->type, monster_index, monster->type, monster->target_index, FIXED_ONE);
3109+ if (definition->flags&_monster_fires_symmetrically)
3110+ {
3111+ attack->dy= -attack->dy;
3112+ projectile_polygon_index= position_monster_projectile(monster_index, monster->target_index, attack, &origin, (world_point3d *) NULL, &_vector, object->facing);
3113+ if (projectile_polygon_index != NONE)
3114+ new_projectile(&origin, projectile_polygon_index, &_vector, attack->error, attack->type, monster_index, monster->type, monster->target_index, FIXED_ONE);
3115+ attack->dy= -attack->dy;
3116+ }
3117+ }
3118+}
3119+
3120+int32 monster_pathfinding_cost_function(
3121+ short source_polygon_index,
3122+ short line_index,
3123+ short destination_polygon_index,
3124+ void *vdata)
3125+{
3126+ struct monster_pathfinding_data *data=(struct monster_pathfinding_data *)vdata;
3127+ struct monster_definition *definition= data->definition;
3128+ struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
3129+ struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
3130+ struct line_data *line= get_line_data(line_index);
3131+ bool respect_polygon_heights= true;
3132+ struct object_data *object;
3133+ short object_index;
3134+ int32 cost;
3135+
3136+ /* base cost is the area of the polygon weユre leaving */
3137+ cost= source_polygon->area;
3138+
3139+ /* no solid lines (baby) */
3140+ if (LINE_IS_SOLID(line) && !LINE_IS_VARIABLE_ELEVATION(line)) cost= -1;
3141+
3142+ /* count up the monsters in destination_polygon and add a constant cost, MONSTER_PATHFINDING_OBSTRUCTION_PENALTY,
3143+ for each of them to discourage overcrowding */
3144+ for (object_index= destination_polygon->first_object; object_index!=NONE; object_index= object->next_object)
3145+ {
3146+ object= get_object_data(object_index);
3147+ if (GET_OBJECT_OWNER(object)==_object_is_monster) cost+= MONSTER_PATHFINDING_OBSTRUCTION_COST;
3148+ }
3149+
3150+ /* if weユre trying to move into a polygon with an area smaller than MINIMUM_MONSTER_PATHFINDING_POLYGON_AREA, disallow the move */
3151+ if (source_polygon->area<MINIMUM_MONSTER_PATHFINDING_POLYGON_AREA) cost= -1;
3152+
3153+ // do platform stuff
3154+ if (cost>0)
3155+ {
3156+ if (destination_polygon->type==_polygon_is_platform)
3157+ {
3158+ switch (monster_can_enter_platform(destination_polygon->permutation, source_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
3159+ {
3160+ case _platform_will_never_be_accessable: cost= -1; break;
3161+ default: cost+= MONSTER_PATHFINDING_PLATFORM_COST; respect_polygon_heights= false; break;
3162+ }
3163+ }
3164+ if (source_polygon->type==_polygon_is_platform)
3165+ {
3166+ switch (monster_can_leave_platform(source_polygon->permutation, destination_polygon_index, definition->height, definition->minimum_ledge_delta, definition->maximum_ledge_delta))
3167+ {
3168+ case _exit_will_never_be_accessable: cost= -1; break;
3169+ default: respect_polygon_heights= false; break;
3170+ }
3171+ }
3172+ }
3173+
3174+ /* if the ledge between polygons is too high, the fall is too far, or there just
3175+ isnユt enough vertical space, disallow the move (and ignore this if weユre dealing with
3176+ platforms or doors) */
3177+ if (respect_polygon_heights)
3178+ {
3179+ world_distance delta_height= destination_polygon->floor_height-source_polygon->floor_height;
3180+
3181+ if (delta_height<definition->minimum_ledge_delta||delta_height>definition->maximum_ledge_delta) cost= -1;
3182+ if (line->lowest_adjacent_ceiling-line->highest_adjacent_floor<definition->height) cost= -1;
3183+
3184+ if (cost>0) cost+= delta_height*delta_height; /* prefer not to change heights */
3185+ }
3186+
3187+ /* if this line not wide enough, disallow the move */
3188+ if (line->length<2*definition->radius) cost= -1;
3189+
3190+ if (cost>0)
3191+ {
3192+ /* if weユre trying to move into an impassable polygon, disallow the move */
3193+ switch (destination_polygon->type)
3194+ {
3195+ case _polygon_is_zone_border:
3196+ if (!data->cross_zone_boundaries) cost= -1;
3197+ break;
3198+
3199+ case _polygon_is_monster_impassable:
3200+ case _polygon_is_teleporter:
3201+ cost= -1;
3202+ break;
3203+ }
3204+ }
3205+
3206+ if (cost>0)
3207+ {
3208+ /* if weユre trying to move into media, pay the penalty */
3209+ if (destination_polygon->media_index!=NONE)
3210+ {
3211+ struct media_data *media= get_media_data(destination_polygon->media_index);
3212+
3213+ // LP change: idiot-proofed this
3214+ if (media)
3215+ {
3216+ if (media->height>destination_polygon->floor_height)
3217+ {
3218+ cost+= 2*destination_polygon->area;
3219+ }
3220+ }
3221+ }
3222+ }
3223+
3224+ return cost;
3225+}
3226+
3227+/* returns the type and index of any interesting terrain feature (platform or door) in front
3228+ of the given monster in his current direction; this lets us open doors and wait for
3229+ platforms. relevant_polygon_index is the polygon_index we have to pass to platform_is_accessable */
3230+static short find_obstructing_terrain_feature(
3231+ short monster_index,
3232+ short *feature_index,
3233+ short *relevant_polygon_index)
3234+{
3235+ struct monster_data *monster= get_monster_data(monster_index);
3236+ struct monster_definition *definition= get_monster_definition(monster->type);
3237+ struct object_data *object= get_object_data(monster->object_index);
3238+ short polygon_index, feature_type;
3239+ world_point2d p1;
3240+
3241+ ray_to_line_segment((world_point2d *)&object->location, &p1, object->facing, MONSTER_PLATFORM_BUFFER_DISTANCE+definition->radius);
3242+
3243+ feature_type= NONE;
3244+ *relevant_polygon_index= polygon_index= object->polygon;
3245+ do
3246+ {
3247+ struct polygon_data *polygon= get_polygon_data(polygon_index);
3248+ short line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)&object->location, &p1);
3249+
3250+ switch (polygon->type)
3251+ {
3252+ case _polygon_is_platform:
3253+ if (object->polygon==polygon_index)
3254+ {
3255+ /* weユre standing on the platform: find out where weユre headed (if weユre
3256+ going nowhere then pretend like everything is o.k.) */
3257+
3258+ polygon_index= line_index==NONE ? NONE : find_adjacent_polygon(polygon_index, line_index);
3259+ if (polygon_index!=NONE)
3260+ {
3261+ *relevant_polygon_index= polygon_index;
3262+ *feature_index= polygon->permutation;
3263+ feature_type= _leaving_platform_polygon;
3264+ assert(*feature_index!=NONE);
3265+ }
3266+ }
3267+ else
3268+ {
3269+ feature_type= _entering_platform_polygon;
3270+ *feature_index= polygon->permutation;
3271+ assert(*feature_index!=NONE);
3272+ }
3273+ break;
3274+
3275+ default:
3276+ if (((definition->flags&_monster_floats) && polygon->floor_height>monster->desired_height) ||
3277+ object->location.z+definition->height>polygon->ceiling_height)
3278+ {
3279+ monster->desired_height= polygon->floor_height;
3280+ feature_type= _flying_or_floating_transition;
3281+ *feature_index= 0;
3282+ }
3283+ if (definition->flags&_monster_flys)
3284+ {
3285+ if ((polygon->floor_height>monster->desired_height) ||
3286+ (polygon->ceiling_height<monster->desired_height+definition->height))
3287+ {
3288+ monster->desired_height= (polygon->floor_height>monster->desired_height) ?
3289+ polygon->floor_height : (polygon->ceiling_height - definition->height);
3290+ feature_type= _flying_or_floating_transition;
3291+ *feature_index= 0;
3292+ }
3293+
3294+ if (object->location.z<polygon->floor_height || object->location.z+definition->height>polygon->ceiling_height)
3295+ {
3296+ feature_type= _flying_or_floating_transition;
3297+ *feature_index= 0;
3298+ }
3299+ }
3300+ if (definition->flags&_monster_uses_sniper_ledges)
3301+ {
3302+ if ((polygon->floor_height+MINIMUM_SNIPER_ELEVATION<monster->desired_height) &&
3303+ monster->mode==_monster_locked)
3304+ {
3305+ feature_type= _standing_on_sniper_ledge;
3306+ }
3307+ }
3308+ if (!(definition->flags&(_monster_floats|_monster_flys)) && polygon->media_index!=NONE)
3309+ {
3310+ struct media_data *media= get_media_data(polygon->media_index);
3311+ // Monster will normally wade to half-height
3312+ world_distance height= definition->height>>1;
3313+ // LP change: idiot-proofing
3314+ if (media)
3315+ {
3316+ // In dangerous media, wade only to zero height (that is, don't wade at all)
3317+ if (IsMediaDangerous(media->type)) height= 0;
3318+
3319+ switch (media->type)
3320+ {
3321+ case _media_water: if (definition->flags&_monster_is_not_afraid_of_water) media= (struct media_data *) NULL; break;
3322+ case _media_jjaro: // LP addition: monsters treat Jjaro goo like sewage
3323+ case _media_sewage: if (definition->flags&_monster_is_not_afraid_of_sewage) media= (struct media_data *) NULL; break;
3324+ case _media_lava: /* height= 0; */ if (definition->flags&_monster_is_not_afraid_of_lava) media= (struct media_data *) NULL; break;
3325+ case _media_goo: /* height= 0; */ if (definition->flags&_monster_is_not_afraid_of_goo) media= (struct media_data *) NULL; break;
3326+ }
3327+ }
3328+ if (media && media->height-polygon->floor_height>height)
3329+ {
3330+ if (get_polygon_data(object->polygon)->floor_height>polygon->floor_height)
3331+ {
3332+ feature_type= _standing_on_sniper_ledge;
3333+ if (monster->mode!=_monster_locked) monster_needs_path(monster_index, false);
3334+ }
3335+ }
3336+ }
3337+ polygon_index= line_index==NONE ? NONE : find_adjacent_polygon(polygon_index, line_index);
3338+ break;
3339+ }
3340+
3341+ if (line_index!=NONE && polygon_index==NONE)
3342+ {
3343+ if (monster->path_segment_length<MONSTER_PLATFORM_BUFFER_DISTANCE)
3344+ {
3345+ monster->path_segment_length= 0;
3346+ }
3347+ else
3348+ {
3349+ /* weユre headed for a wall solid; freeze and get a new path, pronto */
3350+ feature_type= _standing_on_sniper_ledge;
3351+ monster_needs_path(monster_index, true);
3352+ }
3353+ }
3354+ }
3355+ while (polygon_index!=NONE&&(feature_type==NONE||feature_type==_flying_or_floating_transition));
3356+
3357+ return feature_type;
3358+}
3359+
3360+/* returns new polygon index; if destination is NULL then we fire along the monsterユs facing
3361+ and elevation, if destination is not NULL then we set it correctly and save the elevation angle */
3362+static short position_monster_projectile(
3363+ short aggressor_index,
3364+ short target_index,
3365+ struct attack_definition *attack,
3366+ world_point3d *origin,
3367+ world_point3d *destination,
3368+ world_point3d *_vector,
3369+ angle theta)
3370+{
3371+ struct monster_data *aggressor= get_monster_data(aggressor_index);
3372+ struct monster_data *target= get_monster_data(target_index);
3373+ struct object_data *aggressor_object= get_object_data(aggressor->object_index);
3374+ struct object_data *target_object= get_object_data(target->object_index);
3375+ world_distance radius, height;
3376+
3377+// dprintf("positioning #%d to #%d", aggressor_index, target_index);
3378+
3379+ /* adjust origin */
3380+ *origin= aggressor_object->location;
3381+ origin->z+= attack->dz;
3382+ translate_point2d((world_point2d *)origin, attack->dy, NORMALIZE_ANGLE(theta+QUARTER_CIRCLE));
3383+ translate_point2d((world_point2d *)origin, attack->dx, theta);
3384+
3385+ if (destination)
3386+ {
3387+ world_distance distance;
3388+
3389+ /* adjust destination */
3390+ get_monster_dimensions(target_index, &radius, &height);
3391+ *destination= target_object->location;
3392+ destination->z+= (height>>1) + (height>>2); /* shoot 3/4ths up the target */
3393+
3394+ /* calculate outbound vector */
3395+ _vector->x= destination->x-origin->x;
3396+ _vector->y= destination->y-origin->y;
3397+ _vector->z= destination->z-origin->z;
3398+
3399+ distance= isqrt(_vector->x*_vector->x + _vector->y*_vector->y);
3400+ aggressor->elevation= distance ? (_vector->z*TRIG_MAGNITUDE)/distance : 0;
3401+ }
3402+ else
3403+ {
3404+ _vector->x= cosine_table[theta];
3405+ _vector->y= sine_table[theta];
3406+ _vector->z= aggressor->elevation;
3407+ }
3408+
3409+ /* return polygon_index of the new origin point */
3410+ return find_new_object_polygon((world_point2d *)&aggressor_object->location,
3411+ (world_point2d *)origin, aggressor_object->polygon);
3412+}
3413+
3414+short nearest_goal_polygon_index(
3415+ short polygon_index)
3416+{
3417+ polygon_index= flood_map(polygon_index, INT32_MAX, nearest_goal_cost_function, _breadth_first, (void *) NULL);
3418+ while (polygon_index!=NONE)
3419+ {
3420+ struct polygon_data *polygon= get_polygon_data(polygon_index);
3421+
3422+ if (polygon->type==_polygon_is_goal) break;
3423+
3424+ polygon_index= flood_map(NONE, INT32_MAX, nearest_goal_cost_function, _breadth_first, (void *) NULL);
3425+ }
3426+
3427+ return polygon_index;
3428+}
3429+
3430+static int32 nearest_goal_cost_function(
3431+ short source_polygon_index,
3432+ short line_index,
3433+ short destination_polygon_index,
3434+ void *unused)
3435+{
3436+ struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
3437+// struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
3438+// struct line_data *line= get_line_data(line_index);
3439+ int32 cost= 1;
3440+
3441+ (void) (unused);
3442+ (void) (source_polygon_index);
3443+ (void) (line_index);
3444+
3445+ if (destination_polygon->type==_polygon_is_zone_border) cost= -1;
3446+
3447+ return cost;
3448+}
3449+
3450+
3451+// LP: will set player view attributes when trying to shoot a guided projectile.
3452+void SetPlayerViewAttribs(int16 half_visual_arc, int16 half_vertical_visual_arc,
3453+ world_distance visual_range, world_distance dark_visual_range)
3454+{
3455+ // Added a modified version of AlexJS's changes: change only if necessary
3456+ // Restoring AlexJLS's changes
3457+ monster_definition& PlayerAsMonster = monster_definitions[_monster_marine];
3458+ if (half_visual_arc > 0 || PlayerAsMonster.half_visual_arc > 0)
3459+ PlayerAsMonster.half_visual_arc = half_visual_arc;
3460+ if (half_vertical_visual_arc > 0 || PlayerAsMonster.half_vertical_visual_arc > 0)
3461+ PlayerAsMonster.half_vertical_visual_arc = half_vertical_visual_arc;
3462+ if (visual_range > 0 || PlayerAsMonster.visual_range > 0)
3463+ PlayerAsMonster.visual_range = visual_range;
3464+ if (dark_visual_range > 0 || PlayerAsMonster.dark_visual_range > 0)
3465+ PlayerAsMonster.dark_visual_range = dark_visual_range;
3466+}
3467+
3468+
3469+uint8 *unpack_monster_data(uint8 *Stream, monster_data *Objects, size_t Count)
3470+{
3471+ uint8* S = Stream;
3472+ monster_data* ObjPtr = Objects;
3473+
3474+ for (size_t k = 0; k < Count; k++, ObjPtr++)
3475+ {
3476+ StreamToValue(S,ObjPtr->type);
3477+ StreamToValue(S,ObjPtr->vitality);
3478+ StreamToValue(S,ObjPtr->flags);
3479+
3480+ StreamToValue(S,ObjPtr->path);
3481+ StreamToValue(S,ObjPtr->path_segment_length);
3482+ StreamToValue(S,ObjPtr->desired_height);
3483+
3484+ StreamToValue(S,ObjPtr->mode);
3485+ StreamToValue(S,ObjPtr->action);
3486+ StreamToValue(S,ObjPtr->target_index);
3487+ StreamToValue(S,ObjPtr->external_velocity);
3488+ StreamToValue(S,ObjPtr->vertical_velocity);
3489+ StreamToValue(S,ObjPtr->ticks_since_attack);
3490+ StreamToValue(S,ObjPtr->attack_repetitions);
3491+ StreamToValue(S,ObjPtr->changes_until_lock_lost);
3492+
3493+ StreamToValue(S,ObjPtr->elevation);
3494+
3495+ StreamToValue(S,ObjPtr->object_index);
3496+
3497+ StreamToValue(S,ObjPtr->ticks_since_last_activation);
3498+
3499+ StreamToValue(S,ObjPtr->activation_bias);
3500+
3501+ StreamToValue(S,ObjPtr->goal_polygon_index);
3502+
3503+ StreamToValue(S,ObjPtr->sound_location.x);
3504+ StreamToValue(S,ObjPtr->sound_location.y);
3505+ StreamToValue(S,ObjPtr->sound_location.z);
3506+ StreamToValue(S,ObjPtr->sound_polygon_index);
3507+
3508+ StreamToValue(S,ObjPtr->random_desired_height);
3509+
3510+ S += 7*2;
3511+ }
3512+
3513+ assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_data));
3514+ return S;
3515+}
3516+
3517+uint8 *pack_monster_data(uint8 *Stream, monster_data *Objects, size_t Count)
3518+{
3519+ uint8* S = Stream;
3520+ monster_data* ObjPtr = Objects;
3521+
3522+ for (size_t k = 0; k < Count; k++, ObjPtr++)
3523+ {
3524+ ValueToStream(S,ObjPtr->type);
3525+ ValueToStream(S,ObjPtr->vitality);
3526+ ValueToStream(S,ObjPtr->flags);
3527+
3528+ ValueToStream(S,ObjPtr->path);
3529+ ValueToStream(S,ObjPtr->path_segment_length);
3530+ ValueToStream(S,ObjPtr->desired_height);
3531+
3532+ ValueToStream(S,ObjPtr->mode);
3533+ ValueToStream(S,ObjPtr->action);
3534+ ValueToStream(S,ObjPtr->target_index);
3535+ ValueToStream(S,ObjPtr->external_velocity);
3536+ ValueToStream(S,ObjPtr->vertical_velocity);
3537+ ValueToStream(S,ObjPtr->ticks_since_attack);
3538+ ValueToStream(S,ObjPtr->attack_repetitions);
3539+ ValueToStream(S,ObjPtr->changes_until_lock_lost);
3540+
3541+ ValueToStream(S,ObjPtr->elevation);
3542+
3543+ ValueToStream(S,ObjPtr->object_index);
3544+
3545+ ValueToStream(S,ObjPtr->ticks_since_last_activation);
3546+
3547+ ValueToStream(S,ObjPtr->activation_bias);
3548+
3549+ ValueToStream(S,ObjPtr->goal_polygon_index);
3550+
3551+ ValueToStream(S,ObjPtr->sound_location.x);
3552+ ValueToStream(S,ObjPtr->sound_location.y);
3553+ ValueToStream(S,ObjPtr->sound_location.z);
3554+ ValueToStream(S,ObjPtr->sound_polygon_index);
3555+
3556+ ValueToStream(S,ObjPtr->random_desired_height);
3557+
3558+ S += 7*2;
3559+ }
3560+
3561+ assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_data));
3562+ return S;
3563+}
3564+
3565+
3566+inline void StreamToAttackDef(uint8* &S, attack_definition& Object)
3567+{
3568+ StreamToValue(S,Object.type);
3569+ StreamToValue(S,Object.repetitions);
3570+ StreamToValue(S,Object.error);
3571+ StreamToValue(S,Object.range);
3572+ StreamToValue(S,Object.attack_shape);
3573+
3574+ StreamToValue(S,Object.dx);
3575+ StreamToValue(S,Object.dy);
3576+ StreamToValue(S,Object.dz);
3577+}
3578+
3579+inline void AttackDefToStream(uint8* &S, attack_definition& Object)
3580+{
3581+ ValueToStream(S,Object.type);
3582+ ValueToStream(S,Object.repetitions);
3583+ ValueToStream(S,Object.error);
3584+ ValueToStream(S,Object.range);
3585+ ValueToStream(S,Object.attack_shape);
3586+
3587+ ValueToStream(S,Object.dx);
3588+ ValueToStream(S,Object.dy);
3589+ ValueToStream(S,Object.dz);
3590+}
3591+
3592+
3593+uint8 *unpack_monster_definition(uint8 *Stream, size_t Count)
3594+{
3595+ return unpack_monster_definition(Stream,monster_definitions,Count);
3596+}
3597+
3598+uint8 *unpack_monster_definition(uint8 *Stream, monster_definition* Objects, size_t Count)
3599+{
3600+ uint8* S = Stream;
3601+ monster_definition* ObjPtr = Objects;
3602+
3603+ for (size_t k = 0; k < Count; k++, ObjPtr++)
3604+ {
3605+ StreamToValue(S,ObjPtr->collection);
3606+
3607+ StreamToValue(S,ObjPtr->vitality);
3608+ StreamToValue(S,ObjPtr->immunities);
3609+ StreamToValue(S,ObjPtr->weaknesses);
3610+ StreamToValue(S,ObjPtr->flags);
3611+
3612+ StreamToValue(S,ObjPtr->_class);
3613+ StreamToValue(S,ObjPtr->friends);
3614+ StreamToValue(S,ObjPtr->enemies);
3615+
3616+ StreamToValue(S,ObjPtr->sound_pitch);
3617+ StreamToValue(S,ObjPtr->activation_sound);
3618+ StreamToValue(S,ObjPtr->friendly_activation_sound);
3619+ StreamToValue(S,ObjPtr->clear_sound);
3620+ StreamToValue(S,ObjPtr->kill_sound);
3621+ StreamToValue(S,ObjPtr->apology_sound);
3622+ StreamToValue(S,ObjPtr->friendly_fire_sound);
3623+ StreamToValue(S,ObjPtr->flaming_sound);
3624+ StreamToValue(S,ObjPtr->random_sound);
3625+ StreamToValue(S,ObjPtr->random_sound_mask);
3626+
3627+ StreamToValue(S,ObjPtr->carrying_item_type);
3628+
3629+ StreamToValue(S,ObjPtr->radius);
3630+ StreamToValue(S,ObjPtr->height);
3631+ StreamToValue(S,ObjPtr->preferred_hover_height);
3632+ StreamToValue(S,ObjPtr->minimum_ledge_delta);
3633+ StreamToValue(S,ObjPtr->maximum_ledge_delta);
3634+ StreamToValue(S,ObjPtr->external_velocity_scale);
3635+ StreamToValue(S,ObjPtr->impact_effect);
3636+ StreamToValue(S,ObjPtr->melee_impact_effect);
3637+ StreamToValue(S,ObjPtr->contrail_effect);
3638+
3639+ StreamToValue(S,ObjPtr->half_visual_arc);
3640+ StreamToValue(S,ObjPtr->half_vertical_visual_arc);
3641+ StreamToValue(S,ObjPtr->visual_range);
3642+ StreamToValue(S,ObjPtr->dark_visual_range);
3643+ StreamToValue(S,ObjPtr->intelligence);
3644+ StreamToValue(S,ObjPtr->speed);
3645+ StreamToValue(S,ObjPtr->gravity);
3646+ StreamToValue(S,ObjPtr->terminal_velocity);
3647+ StreamToValue(S,ObjPtr->door_retry_mask);
3648+ StreamToValue(S,ObjPtr->shrapnel_radius);
3649+ S = unpack_damage_definition(S,&ObjPtr->shrapnel_damage,1);
3650+
3651+ StreamToValue(S,ObjPtr->hit_shapes);
3652+ StreamToValue(S,ObjPtr->hard_dying_shape);
3653+ StreamToValue(S,ObjPtr->soft_dying_shape);
3654+ StreamToValue(S,ObjPtr->hard_dead_shapes);
3655+ StreamToValue(S,ObjPtr->soft_dead_shapes);
3656+ StreamToValue(S,ObjPtr->stationary_shape);
3657+ StreamToValue(S,ObjPtr->moving_shape);
3658+ StreamToValue(S,ObjPtr->teleport_in_shape);
3659+ StreamToValue(S,ObjPtr->teleport_out_shape);
3660+
3661+ StreamToValue(S,ObjPtr->attack_frequency);
3662+ StreamToAttackDef(S,ObjPtr->melee_attack);
3663+ StreamToAttackDef(S,ObjPtr->ranged_attack);
3664+ }
3665+
3666+ assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_definition));
3667+ return S;
3668+}
3669+
3670+uint8* unpack_m1_monster_definition(uint8 *Stream, size_t Count)
3671+{
3672+ uint8* S = Stream;
3673+ monster_definition* ObjPtr = monster_definitions;
3674+
3675+ for (size_t k = 0; k < Count; k++, ObjPtr++)
3676+ {
3677+ StreamToValue(S, ObjPtr->collection);
3678+
3679+ StreamToValue(S, ObjPtr->vitality);
3680+ StreamToValue(S, ObjPtr->immunities);
3681+ StreamToValue(S, ObjPtr->weaknesses);
3682+ StreamToValue(S, ObjPtr->flags);
3683+
3684+ StreamToValue(S, ObjPtr->_class);
3685+ StreamToValue(S, ObjPtr->friends);
3686+ StreamToValue(S, ObjPtr->enemies);
3687+
3688+ ObjPtr->sound_pitch = FIXED_ONE;
3689+ StreamToValue(S, ObjPtr->activation_sound);
3690+ S += 2; // ignore conversation sound
3691+
3692+ // Marathon doesn't have these
3693+ ObjPtr->friendly_activation_sound = NONE;
3694+ ObjPtr->clear_sound = NONE;
3695+ ObjPtr->kill_sound = NONE;
3696+ ObjPtr->apology_sound = NONE;
3697+ ObjPtr->friendly_fire_sound = NONE;
3698+
3699+ StreamToValue(S, ObjPtr->flaming_sound);
3700+ StreamToValue(S, ObjPtr->random_sound);
3701+ StreamToValue(S, ObjPtr->random_sound_mask);
3702+
3703+ StreamToValue(S, ObjPtr->carrying_item_type);
3704+
3705+ StreamToValue(S, ObjPtr->radius);
3706+ StreamToValue(S, ObjPtr->height);
3707+ StreamToValue(S, ObjPtr->preferred_hover_height);
3708+ StreamToValue(S, ObjPtr->minimum_ledge_delta);
3709+ StreamToValue(S, ObjPtr->maximum_ledge_delta);
3710+ StreamToValue(S, ObjPtr->external_velocity_scale);
3711+
3712+ StreamToValue(S, ObjPtr->impact_effect);
3713+ StreamToValue(S, ObjPtr->melee_impact_effect);
3714+ ObjPtr->contrail_effect = NONE;
3715+
3716+ StreamToValue(S,ObjPtr->half_visual_arc);
3717+ StreamToValue(S,ObjPtr->half_vertical_visual_arc);
3718+ StreamToValue(S,ObjPtr->visual_range);
3719+ StreamToValue(S,ObjPtr->dark_visual_range);
3720+ StreamToValue(S,ObjPtr->intelligence);
3721+ StreamToValue(S,ObjPtr->speed);
3722+ StreamToValue(S,ObjPtr->gravity);
3723+ StreamToValue(S,ObjPtr->terminal_velocity);
3724+ StreamToValue(S,ObjPtr->door_retry_mask);
3725+ StreamToValue(S,ObjPtr->shrapnel_radius);
3726+
3727+ S = unpack_damage_definition(S, &ObjPtr->shrapnel_damage, 1);
3728+
3729+ StreamToValue(S,ObjPtr->hit_shapes);
3730+ StreamToValue(S,ObjPtr->hard_dying_shape);
3731+ StreamToValue(S,ObjPtr->soft_dying_shape);
3732+ StreamToValue(S,ObjPtr->hard_dead_shapes);
3733+ StreamToValue(S,ObjPtr->soft_dead_shapes);
3734+ StreamToValue(S,ObjPtr->stationary_shape);
3735+ StreamToValue(S,ObjPtr->moving_shape);
3736+
3737+ ObjPtr->teleport_in_shape = ObjPtr->stationary_shape;
3738+ ObjPtr->teleport_out_shape = ObjPtr->teleport_out_shape;
3739+
3740+ StreamToValue(S, ObjPtr->attack_frequency);
3741+ StreamToAttackDef(S, ObjPtr->melee_attack);
3742+ StreamToAttackDef(S, ObjPtr->ranged_attack);
3743+ }
3744+
3745+ return S;
3746+}
3747+
3748+uint8 *pack_monster_definition(uint8 *Stream, size_t Count)
3749+{
3750+ return pack_monster_definition(Stream,monster_definitions,Count);
3751+}
3752+
3753+uint8 *pack_monster_definition(uint8 *Stream, monster_definition *Objects, size_t Count)
3754+{
3755+ uint8* S = Stream;
3756+ monster_definition* ObjPtr = Objects;
3757+
3758+ for (size_t k = 0; k < Count; k++, ObjPtr++)
3759+ {
3760+ ValueToStream(S,ObjPtr->collection);
3761+
3762+ ValueToStream(S,ObjPtr->vitality);
3763+ ValueToStream(S,ObjPtr->immunities);
3764+ ValueToStream(S,ObjPtr->weaknesses);
3765+ ValueToStream(S,ObjPtr->flags);
3766+
3767+ ValueToStream(S,ObjPtr->_class);
3768+ ValueToStream(S,ObjPtr->friends);
3769+ ValueToStream(S,ObjPtr->enemies);
3770+
3771+ ValueToStream(S,ObjPtr->sound_pitch);
3772+ ValueToStream(S,ObjPtr->activation_sound);
3773+ ValueToStream(S,ObjPtr->friendly_activation_sound);
3774+ ValueToStream(S,ObjPtr->clear_sound);
3775+ ValueToStream(S,ObjPtr->kill_sound);
3776+ ValueToStream(S,ObjPtr->apology_sound);
3777+ ValueToStream(S,ObjPtr->friendly_fire_sound);
3778+ ValueToStream(S,ObjPtr->flaming_sound);
3779+ ValueToStream(S,ObjPtr->random_sound);
3780+ ValueToStream(S,ObjPtr->random_sound_mask);
3781+
3782+ ValueToStream(S,ObjPtr->carrying_item_type);
3783+
3784+ ValueToStream(S,ObjPtr->radius);
3785+ ValueToStream(S,ObjPtr->height);
3786+ ValueToStream(S,ObjPtr->preferred_hover_height);
3787+ ValueToStream(S,ObjPtr->minimum_ledge_delta);
3788+ ValueToStream(S,ObjPtr->maximum_ledge_delta);
3789+ ValueToStream(S,ObjPtr->external_velocity_scale);
3790+ ValueToStream(S,ObjPtr->impact_effect);
3791+ ValueToStream(S,ObjPtr->melee_impact_effect);
3792+ ValueToStream(S,ObjPtr->contrail_effect);
3793+
3794+ ValueToStream(S,ObjPtr->half_visual_arc);
3795+ ValueToStream(S,ObjPtr->half_vertical_visual_arc);
3796+ ValueToStream(S,ObjPtr->visual_range);
3797+ ValueToStream(S,ObjPtr->dark_visual_range);
3798+ ValueToStream(S,ObjPtr->intelligence);
3799+ ValueToStream(S,ObjPtr->speed);
3800+ ValueToStream(S,ObjPtr->gravity);
3801+ ValueToStream(S,ObjPtr->terminal_velocity);
3802+ ValueToStream(S,ObjPtr->door_retry_mask);
3803+ ValueToStream(S,ObjPtr->shrapnel_radius);
3804+ S = pack_damage_definition(S,&ObjPtr->shrapnel_damage,1);
3805+
3806+ ValueToStream(S,ObjPtr->hit_shapes);
3807+ ValueToStream(S,ObjPtr->hard_dying_shape);
3808+ ValueToStream(S,ObjPtr->soft_dying_shape);
3809+ ValueToStream(S,ObjPtr->hard_dead_shapes);
3810+ ValueToStream(S,ObjPtr->soft_dead_shapes);
3811+ ValueToStream(S,ObjPtr->stationary_shape);
3812+ ValueToStream(S,ObjPtr->moving_shape);
3813+ ValueToStream(S,ObjPtr->teleport_in_shape);
3814+ ValueToStream(S,ObjPtr->teleport_out_shape);
3815+
3816+ ValueToStream(S,ObjPtr->attack_frequency);
3817+ AttackDefToStream(S,ObjPtr->melee_attack);
3818+ AttackDefToStream(S,ObjPtr->ranged_attack);
3819+ }
3820+
3821+ assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_monster_definition));
3822+ return S;
3823+}
3824+
3825+void init_monster_definitions()
3826+{
3827+ memcpy(monster_definitions, original_monster_definitions, sizeof(monster_definitions));
3828+}
3829+
3830+struct damage_kick_definition *original_damage_kick_definitions = NULL;
3831+class XML_DamageKickParser: public XML_ElementParser
3832+{
3833+ short Index;
3834+ damage_kick_definition Data;
3835+
3836+ // What is present?
3837+ bool IndexPresent;
3838+ enum {NumberOfValues = 3};
3839+ bool IsPresent[NumberOfValues];
3840+
3841+public:
3842+ bool Start();
3843+ bool HandleAttribute(const char *Tag, const char *Value);
3844+ bool AttributesDone();
3845+ bool ResetValues();
3846+
3847+ XML_DamageKickParser(): XML_ElementParser("kick") {}
3848+};
3849+
3850+bool XML_DamageKickParser::Start()
3851+{
3852+ // back up old values first
3853+ if (!original_damage_kick_definitions) {
3854+ original_damage_kick_definitions = (struct damage_kick_definition *) malloc(sizeof(struct damage_kick_definition) * NUMBER_OF_DAMAGE_TYPES);
3855+ assert(original_damage_kick_definitions);
3856+ for (unsigned i = 0; i < NUMBER_OF_DAMAGE_TYPES; i++)
3857+ original_damage_kick_definitions[i] = damage_kick_definitions[i];
3858+ }
3859+
3860+ IndexPresent = false;
3861+ for (int k=0; k<NumberOfValues; k++)
3862+ IsPresent[k] = false;
3863+
3864+ return true;
3865+}
3866+
3867+bool XML_DamageKickParser::HandleAttribute(const char *Tag, const char *Value)
3868+{
3869+ if (StringsEqual(Tag,"index"))
3870+ {
3871+ if (ReadBoundedInt16Value(Value,Index,0,NUMBER_OF_DAMAGE_TYPES-1))
3872+ {
3873+ IndexPresent = true;
3874+ return true;
3875+ }
3876+ else return false;
3877+ }
3878+ else if (StringsEqual(Tag,"base"))
3879+ {
3880+ if (ReadInt16Value(Value,Data.base_value))
3881+ {
3882+ IsPresent[0] = true;
3883+ return true;
3884+ }
3885+ else return false;
3886+ }
3887+ else if (StringsEqual(Tag,"mult"))
3888+ {
3889+ if (ReadFloatValue(Value,Data.delta_vitality_multiplier))
3890+ {
3891+ IsPresent[1] = true;
3892+ return true;
3893+ }
3894+ else return false;
3895+ }
3896+ else if (StringsEqual(Tag,"vertical"))
3897+ {
3898+ if (ReadBooleanValue(Value,Data.is_also_vertical))
3899+ {
3900+ IsPresent[2] = true;
3901+ return true;
3902+ }
3903+ else return false;
3904+ }
3905+ UnrecognizedTag();
3906+ return false;
3907+}
3908+
3909+bool XML_DamageKickParser::AttributesDone()
3910+{
3911+ // Verify...
3912+ if (!IndexPresent)
3913+ {
3914+ AttribsMissing();
3915+ return false;
3916+ }
3917+ damage_kick_definition& OrigData = damage_kick_definitions[Index];
3918+
3919+ if (IsPresent[0]) OrigData.base_value = Data.base_value;
3920+ if (IsPresent[1]) OrigData.delta_vitality_multiplier = Data.delta_vitality_multiplier;
3921+ if (IsPresent[2]) OrigData.is_also_vertical = Data.is_also_vertical;
3922+
3923+ return true;
3924+}
3925+
3926+bool XML_DamageKickParser::ResetValues()
3927+{
3928+ if (original_damage_kick_definitions) {
3929+ for (unsigned i = 0; i < NUMBER_OF_DAMAGE_TYPES; i++)
3930+ damage_kick_definitions[i] = original_damage_kick_definitions[i];
3931+ free(original_damage_kick_definitions);
3932+ original_damage_kick_definitions = NULL;
3933+ }
3934+ return true;
3935+}
3936+
3937+static XML_DamageKickParser DamageKickParser;
3938+
3939+
3940+static XML_ElementParser DamageKicksParser("damage_kicks");
3941+
3942+
3943+// XML-parser support
3944+XML_ElementParser *DamageKicks_GetParser()
3945+{
3946+ DamageKicksParser.AddChild(&DamageKickParser);
3947+
3948+ return &DamageKicksParser;
3949+}
--- marathon/trunk/Source_Files/GameWorld/weapons.cpp (revision 531)
+++ marathon/trunk/Source_Files/GameWorld/weapons.cpp (revision 532)
@@ -1,4608 +1,4612 @@
1-/*
2- weapons.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, May 13, 1995 4:41:04 PM- rdm created.
22- Recreating to fix all the annoying problems.
23-
24-Feb. 4, 2000 (Loren Petrich):
25- Changed halt() to assert(false) for better debugging
26-
27-Feb 19, 2000 (Loren Petrich):
28- Suppressed debug checking of which weapons and triggers
29- in get_trigger_definition()
30-
31-March 2, 2000 (Loren Petrich):
32- Suppressed player_weapon_has_ammo() assert in should_switch_to_weapon();
33- causes problems in "Missed Island"'s first level.
34-
35-March 3, 2000 (Loren Petrich):
36- Suppressed complicated assert in fire_weapon();
37- causes problems in the map "Dirt Devil", which turns the flamethrower into a jetpack.
38-
39-Apr 27, 2000 (Loren Petrich):
40- Added Josh Elsasser's "don't switch weapons" patch
41-
42-May 23, 2000 (Loren Petrich):
43- Correct behavior of weapon luminosity; it now adds to miner's light
44- rather than substituting for it.
45-
46-May 26, 2000 (Loren Petrich):
47- Added XML configuration of shell casings and weapon order.
48- Attempted to add more graceful behavior for some weapons being NONE.
49- In Muerte Machine, the weapons are disabled if the fists have weapon type NONE;
50- implemented the use of this as a flag.
51-
52- Added "CannotWieldWeapons()" test; it returns true if the fists have a weapon type of NONE
53-
54-Jun 14, 2000 (Loren Petrich):
55- Suppressed assertion about multiple triggers that follows the Dirt-Devil one
56-
57-Jun 15, 2000 (Loren Petrich):
58- Added support for Chris Pruett's Pfhortran
59-
60-Jul 1, 2000 (Loren Petrich):
61- Made some accessors inline
62-
63-Jul 1, 2000 (Loren Petrich):
64- Added Benad's changes
65-
66-Aug 31, 2000 (Loren Petrich):
67- Added stuff for unpacking and packing
68-
69-Sep 3, 2000 (Loren Petrich):
70- Suppressed "assert(weapon_type!=NUMBER_OF_WEAPONS);" in a search-for-which-weapon loop;
71- some physics models get to this without causing other trouble
72-
73-Oct 19, 2000 (Loren Petrich):
74- Weapon-sprite absence in get_weapon_display_information() handled by bugging out instead of
75- a failed assertion; having a SMG will not crash if one's using a M2 shapes file.
76- Also, added a bug-out in case of no view being found.
77-
78-Dec 24, 2000 (Loren Petrich):
79- Added support for idle-weapon animations
80-
81-Dec 31, 2000 (Loren Petrich):
82- Turned a remaining out-of-range assert into a no-render
83-
84-Jan 6, 2001 (Loren Petrich):
85- Added modification of guided-projectile patch from AlexJLS@aol.com;
86- one can now shoot guided missiles.
87-
88-Feb 1, 2001 (Loren Petrich):
89- Added fix for firing-animation wraparound; prevent_wrap is true for those animations also.
90-
91-Apr 10, 2003 (Woody Zenfell):
92- Fixed bug where dropping the skull made two of them (had this really been in there for almost 3 years??)
93-*/
94-
95-#include "cseries.h"
96-#include "map.h"
97-#include "projectiles.h"
98-#include "player.h"
99-#include "weapons.h"
100-#include "SoundManager.h"
101-#include "interface.h"
102-#include "items.h"
103-#include "monsters.h"
104-#include "game_window.h"
105-
106-#include "Packing.h"
107-#include "shell.h"
108-
109-#include <string.h>
110-#include <stdlib.h>
111-#include <limits.h>
112-
113-#include "weapon_definitions.h"
114-
115-#ifdef env68k
116- #pragma segment weapons
117-#endif
118-
119-// To Do:
120-// lowering second weapon on ammo empty flubs.
121-
122-/* ------------- enums */
123-enum /* weapon states */
124-{
125- _weapon_idle, /* if weapon_delay is non-zero, the weapon cannot be fired again yet */
126- _weapon_raising, /* weapon is rising to idle position */
127- _weapon_lowering, /* weapon is lowering off the screen */
128- _weapon_charging, /* Weapon is charging to fire.. */
129- _weapon_charged, /* Ready to fire.. */
130- _weapon_firing, /* in firing animation */
131- _weapon_recovering, /* Weapon is recovering from firing. */
132- _weapon_awaiting_reload, /* About to start reload sequence */
133- _weapon_waiting_to_load, /* waiting to actually put bullets in */
134- _weapon_finishing_reload, /* finishing the reload */
135-
136- _weapon_lowering_for_twofisted_reload, /* lowering so the other weapon can reload */
137- _weapon_awaiting_twofisted_reload, /* waiting for other to lower.. */
138- _weapon_waiting_for_twofist_to_reload, /* we are offscreen, waiting for the other to finish its load */
139- _weapon_sliding_over_to_second_position, /* pistol is going across when the weapon is present */
140- _weapon_sliding_over_from_second_position, /* Pistol returning to center of screen.. */
141- _weapon_waiting_for_other_idle_to_reload, /* Pistol awaiting friend's idle.. */
142- NUMBER_OF_WEAPON_STATES
143-};
144-
145-enum {
146- _trigger_down= 0x0001,
147- _primary_weapon_is_up= 0x0002,
148- _secondary_weapon_is_up= 0x0004,
149- _wants_twofist= 0x0008,
150- _flip_state= 0x0010
151-};
152-
153-enum {
154- _weapon_type= 0,
155- _shell_casing_type,
156- NUMBER_OF_DATA_TYPES
157-};
158-
159-enum { /* For the flags */ /* [11.unused 1.horizontal 1.vertical 3.unused] */
160- _flip_shape_horizontal= 0x08,
161- _flip_shape_vertical= 0x10
162-};
163-
164-#define PRIMARY_WEAPON_IS_VALID(wd) ((wd)->flags & _primary_weapon_is_up)
165-#define SECONDARY_WEAPON_IS_VALID(wd) ((wd)->flags & _secondary_weapon_is_up)
166-#define SET_PRIMARY_WEAPON_IS_VALID(wd, v) ((void)((v) ? ((wd)->flags |= _primary_weapon_is_up) : ((wd)->flags &= ~_primary_weapon_is_up)))
167-#define SET_SECONDARY_WEAPON_IS_VALID(wd, v) ((void)((v) ? ((wd)->flags |= _secondary_weapon_is_up) : ((wd)->flags &= ~_secondary_weapon_is_up)))
168-
169-#define SET_WEAPON_WANTS_TWOFIST(wd, v) ((void)((v) ? ((wd)->flags |= _wants_twofist) : ((wd)->flags &= ~_wants_twofist)))
170-#define WEAPON_WANTS_TWOFIST(wd) ((wd)->flags & _wants_twofist)
171-
172-#define TRIGGER_IS_DOWN(wd) ((wd)->flags & _trigger_down)
173-#define SET_TRIGGER_DOWN(wd, v) ((void)((v) ? ((wd)->flags |= _trigger_down) : ((wd)->flags &= ~_trigger_down)))
174-
175-#define GET_WEAPON_VARIANCE_SIGN(wd) (((wd)->flags & _flip_state) ? (1) : (-1))
176-#define FLIP_WEAPON_VARIANCE_SIGN(wd) (((wd)->flags & _flip_state) ? ((wd)->flags &= ~_flip_state) : ((wd)->flags |= _flip_state))
177-
178-#define PISTOL_SEPARATION_WIDTH (FIXED_ONE/4)
179-#define AUTOMATIC_STILL_FIRING_DURATION (4)
180-#define FIRING_BEFORE_SHELL_CASING_SOUND_IS_PLAYED (TICKS_PER_SECOND/2)
181-#define COST_PER_CHARGED_WEAPON_SHOT 4
182-#define ANGULAR_VARIANCE (32)
183-
184-enum // shell casing flags
185-{
186- _shell_casing_is_reversed= 0x0001
187-};
188-#define SHELL_CASING_IS_REVERSED(s) ((s)->flags&_shell_casing_is_reversed)
189-
190-/* ----------- structures */
191-/* ...were all moved to weapons.h */
192-
193-/* ------------- globals */
194-/* The array of player weapon states */
195-static struct player_weapon_data *player_weapons_array;
196-
197-/* ------------- macros */
198-#define get_maximum_number_of_players() (MAXIMUM_NUMBER_OF_PLAYERS)
199-#define BUILD_WEAPON_IDENTIFIER(weapon, trigger) (weapon<<1+trigger)
200-#define GET_WEAPON_FROM_IDENTIFIER(identifier) (identifier>>1)
201-#define GET_TRIGGER_FROM_IDENTIFIER(identifier) (identifier&1)
202-
203-/*static*/ player_weapon_data *get_player_weapon_data(
204- const short player_index);
205-
206-static weapon_definition *get_weapon_definition(
207- const short weapon_type);
208-
209-static shell_casing_definition *get_shell_casing_definition(
210- const short type);
211-
212-/* -------------- accessors */
213-
214-player_weapon_data *get_player_weapon_data(
215- const short player_index)
216-{
217- player_weapon_data *data = GetMemberWithBounds(player_weapons_array,player_index,get_maximum_number_of_players());
218- assert(data);
219-
220- return data;
221-}
222-
223-weapon_definition *get_weapon_definition(
224- const short weapon_type)
225-{
226- weapon_definition *definition = GetMemberWithBounds(weapon_definitions,weapon_type,NUMBER_OF_WEAPONS);
227- assert(definition);
228-
229- return definition;
230-}
231-
232-shell_casing_definition *get_shell_casing_definition(
233- const short type)
234-{
235- shell_casing_definition *definition = GetMemberWithBounds(shell_casing_definitions,type,NUMBER_OF_SHELL_CASING_TYPES);
236- assert(definition);
237-
238- return definition;
239-}
240-
241-/* ------------- local prototypes */
242-static void reset_trigger_data(short player_index, short weapon_type, short which_trigger);
243-static bool weapon_works_in_current_environment(short weapon_index);
244-/*static*/ void select_next_best_weapon(short player_index);
245-static struct trigger_data *get_player_trigger_data(short player_index,
246- short which_trigger);
247-struct trigger_data *get_trigger_data(short player_index, shor