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