1 /*
2 MAP.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 Sunday, August 15, 1993 12:13:52 PM
22 
23 Tuesday, December 7, 1993 9:35:13 AM
24 	fixed bug in map_index_to_map_point (removed bitwise exclusive-ors).
25 Sunday, January 2, 1994 10:53:34 PM
26 	transmogrify_object_shape plays sounds now.
27 Wednesday, March 9, 1994 4:34:40 PM
28 	support for lightsourcing/mapping floor ceiling with polygons.
29 Monday, June 27, 1994 6:52:10 PM
30 	ajr--push_out_line now takes the length of the line instead of calculating it.
31 Friday, December 9, 1994 1:31:09 PM  (Jason)
32 	translate_map_object moves objects leaving the map into the center of their polygon.
33 Friday, June 9, 1995 2:25:33 PM  (Jason)
34 	sounds on the other side of a media boundary are obstructed
35 Monday, September 18, 1995 4:38:30 PM  (Jason)
36 	the old sound_index is now the landscape_index for a given level
37 
38 Jan 30, 2000 (Loren Petrich):
39 	Added some typecasts
40 
41 Feb 4, 2000 (Loren Petrich):
42 	Renamed the "pathways/marathon" environment
43 	Changed halt() to assert(false) for better debugging
44 
45 Feb 13, 2000 (Loren Petrich):
46 	Added some idiot-proofing to the tick count in animate_object().
47 
48 Feb 15, 2000 (Loren Petrich):
49 	Suppressed some assertions designed to check for map consistency;
50 	this is to get around some Pfhorte bugs.
51 
52 Feb 17, 2000 (Loren Petrich):
53 	Fixed stuff near arctangent() to be long-distance-friendly
54 
55 Feb 20, 2000 (Loren Petrich): Suppressed height-consistency check in
56 	change_polygon_height().
57 
58 Apr 28, 2000 (Loren Petrich): In animate_object(), switched the two tests
59 	on the current frame so that the Pfhor can teleport out in
60 	M2's "Charon Doesn't Make Change".
61 
62 Jul 6, 2000 (Loren Petrich): Readjusted the frame checking yet again, so that both keyframe = 0
63 	and keyframe = [number of frames] would be detected.
64 
65 Jul 7, 2000 (Loren Petrich): Did yet another frame-checking readjustment, in order to suppress
66 	the reactivated Hunter soft-death bug.
67 
68 Aug 20, 2000 (Loren Petrich): eliminated a "pause()" statement -- some debugging statement?
69 
70 Oct 13, 2000 (Loren Petrich):
71 	Converted the intersected-objects list into a Standard Template Library vector
72 
73 Oct 19, 2000 (Loren Petrich):
74 	Changed get_object_shape_and_transfer_mode() so that it makes data->collection_code equal to NONE
75 	if it does not find a valid sequence or view.
76 
77 Nov 19, 2000 (Loren Petrich):
78 	Added XML support for texture-loading control. This contains a switch to indicate whether to load
79 	the landscape textures, and also stuff for loading the various texture environments.
80 	Each one of these has slots for several collection ID's to load; one can use a converted M1 map
81 	directly with this approach.
82 
83 Feb 8, 2001 (Loren Petrich):
84 	Had not too long ago changed many of the arrays into dynamically-allocated ones, thus ending the
85 	limits on the numbers of points, lines, polygons, etc.
86 	Fixed a *serious* bug in the calculation of the "dynamic world" quantities in recalculate_map_counts() --
87 	there are some count-down loops, but they ought to count down to the last used entity, not the last unused one.
88 
89 Feb 3, 2003 (Loren Petrich):
90 	In attach_parasitic_object(), will transmit the sizing of the host object to the parasite.
91 
92  June 14, 2003 (Woody Zenfell):
93 	New functions for manipulating polygons' object lists (in support of prediction).
94  */
95 
96 /*
97 find_line_crossed leaving polygon could be sped up considerable by reversing the search direction in some circumstances
98 
99 //find_line_crossed_leaving_polygon() does weird things when walking along a gridline
100 //keep_line_segment_out_of_walls() can slide the player slowly along a wall
101 */
102 
103 #include "cseries.h"
104 #include "map.h"
105 #include "FilmProfile.h"
106 #include "interface.h"
107 #include "monsters.h"
108 #include "preferences.h"
109 #include "projectiles.h"
110 #include "effects.h"
111 #include "player.h"
112 #include "platforms.h"
113 #include "lightsource.h"
114 #include "lua_script.h"
115 #include "media.h"
116 #include "scenery.h"
117 #include "SoundManager.h"
118 #include "Console.h"
119 #include "InfoTree.h"
120 
121 #include <string.h>
122 #include <stdlib.h>
123 #include <limits.h>
124 
125 #include <list>
126 
127 /* ---------- structures */
128 
129 /*
130 struct environment_definition
131 {
132 	short *shape_collections; *//* NONE terminated *//*
133 };
134 */
135 
136 /* ---------- constants */
137 
138 #define DEFAULT_MAP_MEMORY_SIZE (128*KILO)
139 
140 /* ---------- globals */
141 
142 // LP: modified texture-environment management so as to be easier to handle with XML
143 
144 const int NUMBER_OF_ENVIRONMENTS = 5;
145 const int NUMBER_OF_ENV_COLLECTIONS = 7;
146 
147 static short Environments[NUMBER_OF_ENVIRONMENTS][NUMBER_OF_ENV_COLLECTIONS] =
148 {
149 	{_collection_walls1, _collection_scenery1, NONE, NONE, NONE, NONE, NONE},	// Lh'owon Water
150 	{_collection_walls2, _collection_scenery2, NONE, NONE, NONE, NONE, NONE},	// Lh'owon Lava
151 	{_collection_walls3, _collection_scenery3, NONE, NONE, NONE, NONE, NONE},	// Lh'owon Sewage
152 	{_collection_walls4, _collection_scenery4, NONE, NONE, NONE, NONE, NONE},	// Jjaro (originally to be Pathways or Marathon)
153 	{_collection_walls5, _collection_scenery5, NONE, NONE, NONE, NONE, NONE}	// Pfhor
154 };
155 
156 /*
157 static short e1[]= {_collection_walls1, _collection_scenery1, NONE}; // lh�owon
158 static short e2[]= {_collection_walls2, _collection_scenery2, NONE}; // lh�owon
159 static short e3[]= {_collection_walls3, _collection_scenery3, NONE}; // lh�owon
160 static short e4[]= {_collection_walls4, _collection_scenery4, NONE}; // pathways (or marathon) (LP: jjaro)
161 static short e5[]= {_collection_walls5, _collection_scenery5, NONE}; // pfhor ship
162 
163 #define NUMBER_OF_ENVIRONMENTS (sizeof(environment_definitions)/sizeof(struct environment_definition))
164 static struct environment_definition environment_definitions[]=
165 {
166 	{e1},
167 	{e2},
168 	{e3},
169 	{e4},
170 	{e5}
171 };
172 */
173 
174 /* ---------- map globals */
175 
176 // Turned some of these lists into variable arrays;
177 // took over their maximum numbers as how many of them
178 
179 struct static_data *static_world = NULL;
180 struct dynamic_data *dynamic_world = NULL;
181 
182 // These are allocated here because the numbers of these objects vary as a game progresses.
183 vector<effect_data> EffectList(MAXIMUM_EFFECTS_PER_MAP);
184 vector<object_data> ObjectList(MAXIMUM_OBJECTS_PER_MAP);
185 vector<monster_data> MonsterList(MAXIMUM_MONSTERS_PER_MAP);
186 vector<projectile_data> ProjectileList(MAXIMUM_PROJECTILES_PER_MAP);
187 // struct object_data *objects = NULL;
188 // struct monster_data *monsters = NULL;
189 // struct projectile_data *projectiles = NULL;
190 
191 vector<endpoint_data> EndpointList;
192 vector<line_data> LineList;
193 vector<side_data> SideList;
194 vector<polygon_data> PolygonList;
195 vector<platform_data> PlatformList;
196 // struct polygon_data *map_polygons = NULL;
197 // struct side_data *map_sides = NULL;
198 // struct line_data *map_lines = NULL;
199 // struct endpoint_data *map_endpoints = NULL;
200 // struct platform_data *platforms = NULL;
201 
202 vector<ambient_sound_image_data> AmbientSoundImageList;
203 vector<random_sound_image_data> RandomSoundImageList;
204 // struct ambient_sound_image_data *ambient_sound_images = NULL;
205 // struct random_sound_image_data *random_sound_images = NULL;
206 
207 vector<int16> MapIndexList;
208 // short *map_indexes = NULL;
209 
210 vector<uint8> AutomapLineList;
211 vector<uint8> AutomapPolygonList;
212 // byte *automap_lines = NULL;
213 // byte *automap_polygons = NULL;
214 
215 vector<map_annotation> MapAnnotationList;
216 // struct map_annotation *map_annotations = NULL;
217 
218 vector<map_object> SavedObjectList;
219 // struct map_object *saved_objects = NULL;
220 struct item_placement_data *placement_information = NULL;
221 
222 bool game_is_networked = false;
223 
224 // This could be a handle
225 struct map_memory_data {
226 	byte *memory;
227 	int32 size;
228 	int32 index;
229 };
230 
231 // static struct map_memory_data map_structure_memory;
232 
233 // LP addition: growable list of intersected objects
234 static vector<short> IntersectedObjects;
235 
236 // Whether or not Marathon 2/oo landscapes had been loaded (switch off for Marathon 1 compatibility)
237 bool LandscapesLoaded = true;
238 
239 // The index number of the first texture loaded (should be the main wall texture);
240 // needed for infravision fog when landscapes are switched off
241 short LoadedWallTexture = NONE;
242 
243 /* ---------- private prototypes */
244 
245 static short _new_map_object(shape_descriptor shape, angle facing);
246 
247 // ZZZ: factored out some functionality for prediction, but ended up not using this stuff,
248 // so am not "publishing" it via map.h yet.
249 // SB: Blah.
250 void remove_object_from_polygon_object_list(short object_index, short polygon_index);
251 // The second infers the polygon_index from the object's "polygon" member field.
252 void add_object_to_polygon_object_list(short object_index, short polygon_index);
add_object_to_polygon_object_list(short object_index)253 inline void add_object_to_polygon_object_list(short object_index)
254 { add_object_to_polygon_object_list(object_index, get_object_data(object_index)->polygon); }
255 
256 short _find_line_crossed_leaving_polygon(short polygon_index, world_point2d *p0, world_point2d *p1, bool *last_line);
257 
258 /* ---------- code */
259 
260 // Accessors moved here to shrink the code
261 
get_object_data(const short object_index)262 object_data *get_object_data(
263 	const short object_index)
264 {
265 	struct object_data *object = GetMemberWithBounds(objects,object_index,MAXIMUM_OBJECTS_PER_MAP);
266 
267 	vassert(object, csprintf(temporary, "object index #%d is out of range", object_index));
268 	vassert(SLOT_IS_USED(object), csprintf(temporary, "object index #%d is unused", object_index));
269 
270 	return object;
271 }
272 
get_polygon_data(const short polygon_index)273 polygon_data *get_polygon_data(
274 	const short polygon_index)
275 {
276 	assert(map_polygons);
277 	struct polygon_data *polygon = GetMemberWithBounds(map_polygons,polygon_index,dynamic_world->polygon_count);
278 
279 	vassert(polygon, csprintf(temporary, "polygon index #%d is out of range", polygon_index));
280 
281 	return polygon;
282 }
283 
get_line_data(const short line_index)284 line_data *get_line_data(
285 	const short line_index)
286 {
287 	assert(map_lines);
288 	struct line_data *line = GetMemberWithBounds(map_lines,line_index,dynamic_world->line_count);
289 
290 	vassert(line, csprintf(temporary, "line index #%d is out of range", line_index));
291 
292 	return line;
293 }
294 
get_side_data(const short side_index)295 side_data *get_side_data(
296 	const short side_index)
297 {
298 	assert(map_sides);
299 	struct side_data *side = GetMemberWithBounds(map_sides,side_index,dynamic_world->side_count);
300 
301 	vassert(side, csprintf(temporary, "side index #%d is out of range", side_index));
302 
303 	return side;
304 }
305 
get_endpoint_data(const short endpoint_index)306 endpoint_data *get_endpoint_data(
307 	const short endpoint_index)
308 {
309 	assert(map_endpoints);
310 	struct endpoint_data *endpoint = GetMemberWithBounds(map_endpoints,endpoint_index,dynamic_world->endpoint_count);
311 
312 	vassert(endpoint, csprintf(temporary, "endpoint index #%d is out of range", endpoint_index));
313 
314 	return endpoint;
315 }
316 
get_map_indexes(const short index,const short count)317 short *get_map_indexes(
318 	const short index,
319 	const short count)
320 {
321 	assert(map_indexes);
322 	short *map_index = GetMemberWithBounds(map_indexes,static_cast<unsigned short>(index),static_cast<unsigned short>(dynamic_world->map_index_count)-count+1);
323 
324 	// vassert(map_index, csprintf(temporary, "map_indexes(#%d,#%d) are out of range", index, count));
325 
326 	return map_index;
327 }
328 
get_ambient_sound_image_data(const short ambient_sound_image_index)329 ambient_sound_image_data *get_ambient_sound_image_data(
330 	const short ambient_sound_image_index)
331 {
332 	return GetMemberWithBounds(ambient_sound_images,ambient_sound_image_index,dynamic_world->ambient_sound_image_count);
333 }
334 
get_random_sound_image_data(const short random_sound_image_index)335 random_sound_image_data *get_random_sound_image_data(
336 	const short random_sound_image_index)
337 {
338 	return GetMemberWithBounds(random_sound_images,random_sound_image_index,dynamic_world->random_sound_image_count);
339 }
340 
allocate_map_memory(void)341 void allocate_map_memory(
342 	void)
343 {
344 	assert(NUMBER_OF_COLLECTIONS<=MAXIMUM_COLLECTIONS);
345 
346 	static_world= new static_data;
347 	dynamic_world= new dynamic_data;
348 	assert(static_world&&dynamic_world);
349 	obj_clear(*static_world);
350 	obj_clear(*dynamic_world);
351 
352 	// monsters= new monster_data[MAXIMUM_MONSTERS_PER_MAP];
353 	// projectiles= new projectile_data[MAXIMUM_PROJECTILES_PER_MAP];
354 	// objects= new object_data[MAXIMUM_OBJECTS_PER_MAP];
355 	// effects= new effect_data[MAXIMUM_EFFECTS_PER_MAP];
356 	// lights= new light_data[MAXIMUM_LIGHTS_PER_MAP];
357 	// medias= new media_data[MAXIMUM_MEDIAS_PER_MAP];
358 	// assert(objects&&monsters&&effects&&projectiles&&lights&&medias);
359 
360 	// obj_clear(map_structure_memory);
361 	// reallocate_map_structure_memory(DEFAULT_MAP_MEMORY_SIZE);
362 
363 	// platforms= new platform_data[MAXIMUM_PLATFORMS_PER_MAP];
364 	// assert(platforms);
365 
366 	// ambient_sound_images= new ambient_sound_image_data[MAXIMUM_AMBIENT_SOUND_IMAGES_PER_MAP];
367 	// random_sound_images= new random_sound_image_data[MAXIMUM_RANDOM_SOUND_IMAGES_PER_MAP];
368 	// assert(ambient_sound_images && random_sound_images);
369 
370 	// map_annotations= new map_annotation[MAXIMUM_ANNOTATIONS_PER_MAP];
371 	// saved_objects= new map_object[MAXIMUM_SAVED_OBJECTS];
372 	// assert(map_annotations && saved_objects);
373 
374 	allocate_player_memory();
375 }
376 
initialize_map_for_new_game(void)377 void initialize_map_for_new_game(
378 	void)
379 {
380 	obj_clear(*dynamic_world);
381 
382 	initialize_players();
383 	initialize_monsters();
384 }
385 
initialize_map_for_new_level(void)386 void initialize_map_for_new_level(
387 	void)
388 {
389 	short total_civilians, total_causalties;
390 	uint32 tick_count;
391 	uint16 random_seed;
392 	short player_count;
393 	struct game_data game_information;
394 
395 	/* The player count, tick count, and random seed must persist.. */
396 	/* And the game information! (ajr) */
397 	player_count= dynamic_world->player_count;
398 	tick_count= dynamic_world->tick_count;
399 	random_seed= dynamic_world->random_seed;
400 	total_civilians= dynamic_world->total_civilian_count + dynamic_world->current_civilian_count;
401 	total_causalties= dynamic_world->total_civilian_causalties + dynamic_world->current_civilian_causalties;
402 	game_information= dynamic_world->game_information;
403 	obj_clear(*dynamic_world);
404 	dynamic_world->game_information= game_information;
405 	dynamic_world->player_count= player_count;
406 	dynamic_world->tick_count= tick_count;
407 	dynamic_world->random_seed= random_seed;
408 	dynamic_world->total_civilian_count= total_civilians;
409 	dynamic_world->total_civilian_causalties= total_causalties;
410 	dynamic_world->speaking_player_index= NONE;
411 	dynamic_world->garbage_object_count= 0;
412 
413 	obj_clear(*static_world);
414 	Console::instance()->clear_saves();
415 
416 	// Clear all these out -- supposed to be none of the contents of these when starting a level.
417 	objlist_clear(automap_lines, AutomapLineList.size());
418 	objlist_clear(automap_polygons, AutomapPolygonList.size());
419 	objlist_clear(effects, EffectList.size());
420 	objlist_clear(projectiles,  ProjectileList.size());
421 	objlist_clear(monsters,  MonsterList.size());
422 	objlist_clear(objects,  ObjectList.size());
423 
424 	/* Note that these pointers just point into a larger structure, so this is not a bad thing */
425 	// map_polygons= NULL;
426 	// map_sides= NULL;
427 	// map_lines= NULL;
428 	// map_endpoints= NULL;
429 	// automap_lines= NULL;
430 	// automap_polygons= NULL;
431 }
432 
433 static bool map_collections[NUMBER_OF_COLLECTIONS];
434 static bool media_effects[NUMBER_OF_EFFECT_TYPES];
435 
mark_map_collections(bool loading)436 void mark_map_collections(bool loading)
437 {
438 	if (loading)
439 	{
440 
441 		for (int collection = 0; collection < NUMBER_OF_COLLECTIONS; collection++)
442 		{
443 			map_collections[collection] = false;
444 		}
445 
446 		// walls/floors/ceilings
447 		for (int n = 0; n < dynamic_world->polygon_count; n++)
448 		{
449 			polygon_data *polygon = map_polygons + n;
450 			int coll;
451 			coll = GET_DESCRIPTOR_COLLECTION(polygon->floor_texture);
452 			if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
453 				map_collections[coll] = true;
454 
455 			coll = GET_DESCRIPTOR_COLLECTION(polygon->ceiling_texture);
456 			if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
457 				map_collections[coll] = true;
458 
459 			for (int i = 0; i < polygon->vertex_count; i++)
460 			{
461 				short side_index = polygon->side_indexes[i];
462 				if (side_index == NONE) continue;
463 				side_data *side = get_side_data(side_index);
464 				switch (side->type)
465 				{
466 				case _full_side:
467 					coll = GET_DESCRIPTOR_COLLECTION(side->primary_texture.texture);
468 					if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
469 						map_collections[coll] = true;
470 					break;
471 				case _split_side:
472 					coll = GET_DESCRIPTOR_COLLECTION(side->secondary_texture.texture);
473 					if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
474 						map_collections[coll] = true;
475 					// fall through to the high side case
476 				case _high_side:
477 					coll = GET_DESCRIPTOR_COLLECTION(side->primary_texture.texture);
478 					if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
479 						map_collections[coll] = true;
480 					break;
481 				case _low_side:
482 					coll = GET_DESCRIPTOR_COLLECTION(side->primary_texture.texture);
483 					if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
484 						map_collections[coll] = true;
485 				}
486 
487 				coll = GET_DESCRIPTOR_COLLECTION(side->transparent_texture.texture);
488 				if (coll >= 0 && coll < NUMBER_OF_COLLECTIONS)
489 					map_collections[coll] = true;
490 
491 			}
492 		}
493 
494 		// media textures and effects
495 		for (int media_effect = 0; media_effect < NUMBER_OF_EFFECT_TYPES; media_effect++)
496 		{
497 			media_effects[media_effect] = false;
498 		}
499 
500 		for (int media_index = 0; media_index < MAXIMUM_MEDIAS_PER_MAP; ++media_index)
501 		{
502 			if (get_media_data(media_index))
503 			{
504 				short collection;
505 				if (get_media_collection(media_index, collection))
506 				{
507 					map_collections[collection] = true;
508 				}
509 
510 				for (int detonation_type = 0; detonation_type < NUMBER_OF_MEDIA_DETONATION_TYPES; detonation_type++)
511 				{
512 					short detonation_effect;
513 					get_media_detonation_effect(media_index, detonation_type, &detonation_effect);
514 					if (detonation_effect >= 0 && detonation_effect < NUMBER_OF_EFFECT_TYPES)
515 						media_effects[detonation_effect] = true;
516 				}
517 			}
518 		}
519 
520 		// scenery
521 		for (int object_index = 0; object_index < dynamic_world->initial_objects_count; object_index++)
522 		{
523 			if (saved_objects[object_index].type == _saved_object)
524 			{
525 				short collection;
526 				if (get_scenery_collection(saved_objects[object_index].index, collection))
527 				{
528 					map_collections[collection] = true;
529 				}
530 
531 				if (get_damaged_scenery_collection(saved_objects[object_index].index, collection))
532 				{
533 					map_collections[collection] = true;
534 				}
535 			}
536 		}
537 
538 
539 		for (int collection = 0; collection < NUMBER_OF_COLLECTIONS; collection++)
540 		{
541 			if (map_collections[collection])
542 			{
543 				mark_collection_for_loading(collection);
544 			}
545 		}
546 
547 
548 		for (int media_effect = 0; media_effect < NUMBER_OF_EFFECT_TYPES; media_effect++)
549 		{
550 			if (media_effects[media_effect])
551 			{
552 				mark_effect_collections(media_effect, true);
553 			}
554 		}
555 
556 
557 	} else { // not loading
558 		for (int collection = 0; collection < NUMBER_OF_COLLECTIONS; collection++)
559 		{
560 			if (map_collections[collection])
561 			{
562 				mark_collection_for_unloading(collection);
563 			}
564 		}
565 
566 		for (int media_effect = 0; media_effect < NUMBER_OF_EFFECT_TYPES; media_effect++)
567 		{
568 			mark_effect_collections(media_effect, false);
569 		}
570 
571 	}
572 }
573 
collection_in_environment(short collection_code,short environment_code)574 bool collection_in_environment(
575 	short collection_code,
576 	short environment_code)
577 {
578 	short collection_index= GET_COLLECTION(collection_code);
579 	bool found= false;
580 	int i;
581 
582 	if (!(environment_code>=0 && environment_code<NUMBER_OF_ENVIRONMENTS)) return false;
583 	assert(collection_index>=0 && collection_index<NUMBER_OF_COLLECTIONS);
584 
585 	for (i= 0; i<NUMBER_OF_ENV_COLLECTIONS; ++i)
586 	{
587 		if (Environments[environment_code][i]==collection_index) {
588 			found= true;
589 			break;
590 		}
591 	}
592 
593 	return found;
594 }
595 
596 /* mark all of the shape collections belonging to a given environment code for loading or
597 	unloading */
mark_environment_collections(short environment_code,bool loading)598 void mark_environment_collections(
599 	short environment_code,
600 	bool loading)
601 {
602 	short i;
603 	short collection;
604 
605 	if (!(environment_code>=0&&environment_code<NUMBER_OF_ENVIRONMENTS)) return;
606 
607 	// LP change: modified to use new collection-environment management;
608 	// be sure to set "loaded wall texture" to the first one loaded
609 	LoadedWallTexture = NONE;
610 
611 	// for (i= 0; (collection= environment_definitions[environment_code].shape_collections[i])!=NONE; ++i)
612 	for (i= 0; i<NUMBER_OF_ENV_COLLECTIONS; ++i)
613 	{
614 		collection = Environments[environment_code][i];
615 		if (collection != NONE)
616 		{
617 			if (LoadedWallTexture == NONE) LoadedWallTexture = collection;
618 			loading ? mark_collection_for_loading(collection) : mark_collection_for_unloading(collection);
619 		}
620 	}
621 	if (LoadedWallTexture == NONE) LoadedWallTexture = 0;
622 
623 	// Don't load/unload if M1 compatible...
624 	if (LandscapesLoaded)
625 		loading ? mark_collection_for_loading(_collection_landscape1+static_world->song_index) :
626 			mark_collection_for_unloading(_collection_landscape1+static_world->song_index);
627 }
628 
629 /* make the object list and the map consistent */
reconnect_map_object_list(void)630 void reconnect_map_object_list(
631 	void)
632 {
633 	short i;
634 	struct object_data *object;
635 	struct polygon_data *polygon;
636 
637 	/* wipe first_object links from polygon structures */
638 	for (polygon=map_polygons,i=0;i<dynamic_world->polygon_count;--i,++polygon)
639 	{
640 		polygon->first_object= NONE;
641 	}
642 
643 	/* connect objects to their polygons */
644 	for (object=objects,i=0;i<MAXIMUM_OBJECTS_PER_MAP;++i,++object)
645 	{
646 		if (SLOT_IS_USED(object))
647 		{
648 			polygon= get_polygon_data(object->polygon);
649 
650 			object->next_object= polygon->first_object;
651 			polygon->first_object= i;
652 		}
653 	}
654 }
655 
valid_point2d(world_point2d * p)656 bool valid_point2d(
657 	world_point2d *p)
658 {
659 	return world_point_to_polygon_index(p)==NONE ? false : true;
660 }
661 
valid_point3d(world_point3d * p)662 bool valid_point3d(
663 	world_point3d *p)
664 {
665 	short polygon_index= world_point_to_polygon_index((world_point2d *)p);
666 	bool valid= false;
667 
668 	if (polygon_index!=NONE)
669 	{
670 		struct polygon_data *polygon= get_polygon_data(polygon_index);
671 
672 		if (p->z>polygon->floor_height&&p->z<polygon->ceiling_height)
673 		{
674 			valid= true;
675 		}
676 	}
677 
678 	return valid;
679 }
680 
new_map_object(struct object_location * location,shape_descriptor shape)681 short new_map_object(
682 	struct object_location *location,
683 	shape_descriptor shape)
684 {
685 	struct polygon_data *polygon= get_polygon_data(location->polygon_index);
686 	world_point3d p= location->p;
687 	short object_index;
688 
689 	p.z= ((location->flags&_map_object_hanging_from_ceiling) ? polygon->ceiling_height : polygon->floor_height) + p.z;
690 
691 	object_index= new_map_object3d(&p, location->polygon_index, shape, location->yaw);
692 	if (object_index!=NONE)
693 	{
694 		struct object_data *object= get_object_data(object_index);
695 
696 		if (location->flags&_map_object_is_invisible)
697 			SET_OBJECT_INVISIBILITY(object, true);
698 	}
699 
700 	return object_index;
701 }
702 
new_map_object2d(world_point2d * location,short polygon_index,shape_descriptor shape,angle facing)703 short new_map_object2d(
704 	world_point2d *location,
705 	short polygon_index,
706 	shape_descriptor shape,
707 	angle facing)
708 {
709 	world_point3d location3d;
710 	struct polygon_data *polygon;
711 
712 	polygon= get_polygon_data(polygon_index);
713 	location3d.x= location->x, location3d.y= location->y, location3d.z= polygon->floor_height;
714 
715 	return new_map_object3d(&location3d, polygon_index, shape, facing);
716 }
717 
new_map_object3d(world_point3d * location,short polygon_index,shape_descriptor shape,angle facing)718 short new_map_object3d(
719 	world_point3d *location,
720 	short polygon_index,
721 	shape_descriptor shape,
722 	angle facing)
723 {
724 	short object_index;
725 
726 	object_index= _new_map_object(shape, facing);
727 	if (object_index!=NONE)
728 	{
729 		struct polygon_data *polygon= get_polygon_data(polygon_index);
730 		struct object_data *object= get_object_data(object_index);
731 
732 		/* initialize object polygon and location */
733 		object->polygon= polygon_index;
734 		object->location= *location;
735 
736 		/* insert at head of linked list */
737 		object->next_object= polygon->first_object;
738 		polygon->first_object= object_index;
739 	}
740 
741 	return object_index;
742 }
743 
744 /* can be NONE if there is no direct route between the two points or the child point is not in any
745 	polygon */
find_new_object_polygon(world_point2d * parent_location,world_point2d * child_location,short parent_polygon_index)746 short find_new_object_polygon(
747 	world_point2d *parent_location,
748 	world_point2d *child_location,
749 	short parent_polygon_index)
750 {
751 	short child_polygon_index= parent_polygon_index;
752 
753 	if (child_polygon_index!=NONE)
754 	{
755 		short line_index;
756 
757 		do
758 		{
759 			line_index= find_line_crossed_leaving_polygon(child_polygon_index, parent_location, child_location);
760 			if (line_index!=NONE)
761 			{
762 				child_polygon_index= find_adjacent_polygon(child_polygon_index, line_index);
763 			}
764 		}
765 		while (line_index!=NONE&&child_polygon_index!=NONE);
766 	}
767 
768 	return child_polygon_index;
769 }
770 
attach_parasitic_object(short host_index,shape_descriptor shape,angle facing)771 short attach_parasitic_object(
772 	short host_index,
773 	shape_descriptor shape,
774 	angle facing)
775 {
776 	struct object_data *host_object, *parasite_object;
777 	short parasite_index;
778 
779 	/* walk this object�s parasite list until we find the last parasite and then attach there */
780 	for (host_object= get_object_data(host_index);
781 			host_object->parasitic_object!=NONE;
782 			host_index= host_object->parasitic_object, host_object= get_object_data(host_index))
783 		;
784 	parasite_index= _new_map_object(shape, facing);
785 	assert(parasite_index!=NONE);
786 
787 	parasite_object= get_object_data(parasite_index);
788 	parasite_object->location= host_object->location;
789 	host_object->parasitic_object= parasite_index;
790 
791 	// So that it will have the same size scaling as its host object
792 	SET_FLAG(parasite_object->flags, _object_is_enlarged, TEST_FLAG(host_object->flags, _object_is_enlarged));
793 	SET_FLAG(parasite_object->flags, _object_is_tiny, TEST_FLAG(host_object->flags, _object_is_tiny));
794 
795 	return parasite_index;
796 }
797 
remove_parasitic_object(short host_index)798 void remove_parasitic_object(
799 	short host_index)
800 {
801 	struct object_data *host= get_object_data(host_index);
802 	struct object_data *parasite= get_object_data(host->parasitic_object);
803 
804 	host->parasitic_object= NONE;
805 	MARK_SLOT_AS_FREE(parasite);
806 }
807 
808 /* look up the index yourself */
remove_map_object(short object_index)809 void remove_map_object(
810 	short object_index)
811 {
812 	short *next_object;
813 	struct object_data *object= get_object_data(object_index);
814 	struct polygon_data *polygon= get_polygon_data(object->polygon);
815 
816 	next_object= &polygon->first_object;
817 	while (*next_object!=object_index) next_object= &get_object_data(*next_object)->next_object;
818 
819 	if (object->parasitic_object!=NONE)
820 	{
821 		struct object_data *parasite= get_object_data(object->parasitic_object);
822 
823 		MARK_SLOT_AS_FREE(parasite);
824 	}
825 
826 	SoundManager::instance()->OrphanSound(object_index);
827 	L_Invalidate_Object(object_index);
828 	*next_object= object->next_object;
829 	MARK_SLOT_AS_FREE(object);
830 }
831 
832 
833 
834 /* remove the object from the old_polygon�s object list*/
835 void
remove_object_from_polygon_object_list(short object_index,short polygon_index)836 remove_object_from_polygon_object_list(short object_index, short polygon_index)
837 {
838 	struct object_data* object = get_object_data(object_index);
839 
840 	polygon_data* polygon= get_polygon_data(polygon_index);
841 	short* next_object= &polygon->first_object;
842 
843 	assert(*next_object != NONE);
844 
845 	while (*next_object!=object_index)
846 	{
847 		next_object= &get_object_data(*next_object)->next_object;
848 		assert(*next_object != NONE);
849 	}
850 
851 	*next_object= object->next_object;
852 
853 	object->polygon= NONE;
854 }
855 
856 void
remove_object_from_polygon_object_list(short object_index)857 remove_object_from_polygon_object_list(short object_index)
858 {
859 	remove_object_from_polygon_object_list(object_index, get_object_data(object_index)->polygon);
860 }
861 
862 
863 
864 /* add the object to the new_polygon�s object list */
865 void
add_object_to_polygon_object_list(short object_index,short polygon_index)866 add_object_to_polygon_object_list(short object_index, short polygon_index)
867 {
868 	struct object_data* object = get_object_data(object_index);
869 	struct polygon_data* polygon= get_polygon_data(polygon_index);
870 
871 	object->next_object= polygon->first_object;
872 	polygon->first_object= object_index;
873 
874 	object->polygon= polygon_index;
875 }
876 
877 typedef std::pair<short, short>	DeferredObjectListInsertion;
878 typedef std::list<DeferredObjectListInsertion> DeferredObjectListInsertionList;
879 static DeferredObjectListInsertionList sDeferredObjectListInsertions;
880 
881 void
deferred_add_object_to_polygon_object_list(short object_index,short index_to_precede)882 deferred_add_object_to_polygon_object_list(short object_index, short index_to_precede)
883 {
884 	sDeferredObjectListInsertions.push_back(DeferredObjectListInsertion(object_index, index_to_precede));
885 }
886 
887 
888 
889 void
perform_deferred_polygon_object_list_manipulations()890 perform_deferred_polygon_object_list_manipulations()
891 {
892 	// Loop while the list of insertions is non-empty (we may need to make multiple passes)
893 	while(!sDeferredObjectListInsertions.empty())
894 	{
895 		// Pass over the list of insertions, inserting whatever we can.
896 		bool something_changed = false;
897 
898 		for(DeferredObjectListInsertionList::iterator i = sDeferredObjectListInsertions.begin(); i != sDeferredObjectListInsertions.end(); )
899 		{
900 			short object_to_insert_index = (*i).first;
901 			short object_index_to_precede = (*i).second;
902 			object_data* object = get_object_data(object_to_insert_index);
903 			polygon_data* polygon = get_polygon_data(object->polygon);
904 
905 			// Find object index we're supposed to predece... and insert the object before it
906 			short* next_object_index_p = &(polygon->first_object);
907 			bool inserted = false;
908 
909 			while(!inserted)
910 			{
911 				if(*next_object_index_p == object_index_to_precede)
912 				{
913 					object->next_object = *next_object_index_p;
914 					*next_object_index_p = object_to_insert_index;
915 					inserted = true;
916 				}
917 
918 				if(*next_object_index_p == NONE)
919 					break;
920 
921 				next_object_index_p = &(get_object_data(*next_object_index_p)->next_object);
922 
923 			} // Insert object before object it's supposed to precede
924 
925 			// Each branch of this if() will increment i
926 			if(inserted)
927 			{
928 				// Most concise way to correctly remove-and-increment
929 				sDeferredObjectListInsertions.erase(i++);
930 				something_changed = true;
931 			}
932 			else
933 				// Perhaps we were trying to insert before another object that's scheduled for insertion.
934 				// In that case, maybe by the time we come around again, the other object will be inserted.
935 				// For now, we leave the insertion pending.
936 				++i;
937 
938 		} // Pass over the list of insertions, inserting whatever we can.
939 
940 		// We must make progress, otherwise my algorithm is flawed (we'd loop forever).  Progress here
941 		// is performing insertions into the polygon object lists and removing the corresponding deferred
942 		// insertions from the insertion list.
943 		assert(something_changed);
944 
945 	} // Loop while the list of insertions is non-empty
946 
947 } // perform_deferred_polygon_object_list_manipulations
948 
949 
950 
951 /* if a new polygon index is supplied, it will be used, otherwise we�ll try to find the new
952 	polygon index ourselves */
translate_map_object(short object_index,world_point3d * new_location,short new_polygon_index)953 bool translate_map_object(
954 	short object_index,
955 	world_point3d *new_location,
956 	short new_polygon_index)
957 {
958 	short line_index;
959 	struct object_data *object= get_object_data(object_index);
960 	short old_polygon_index= object->polygon;
961 	bool changed_polygons= false;
962 
963 	/* if new_polygon is NONE, find out what polygon the new_location is in */
964 	if (new_polygon_index==NONE)
965 	{
966 		new_polygon_index= old_polygon_index;
967 		do
968 		{
969 			line_index= find_line_crossed_leaving_polygon(new_polygon_index, (world_point2d *)&object->location, (world_point2d *)new_location);
970 			if (line_index!=NONE) new_polygon_index= find_adjacent_polygon(new_polygon_index, line_index);
971 			if (new_polygon_index==NONE)
972 			{
973 				*(world_point2d *)new_location= get_polygon_data(old_polygon_index)->center;
974 				new_polygon_index= old_polygon_index;
975 				changed_polygons= true; /* tell the caller we switched polygons, even though we didn�t */
976 				break;
977 			}
978 		}
979 		while (line_index!=NONE);
980 	}
981 
982 	/* if we changed polygons, update the old and new polygon�s linked lists of objects */
983 	if (old_polygon_index!=new_polygon_index)
984 	{
985 		remove_object_from_polygon_object_list(object_index, old_polygon_index);
986 		add_object_to_polygon_object_list(object_index, new_polygon_index);
987 		changed_polygons= true;
988 	}
989 	object->location= *new_location;
990 
991 	/* move (no saving throw) all parasitic objects along with their host */
992 	while (object->parasitic_object!=NONE)
993 	{
994 		object= get_object_data(object->parasitic_object);
995 		object->polygon= new_polygon_index;
996 		object->location= *new_location;
997 	}
998 
999 	return changed_polygons;
1000 }
1001 
1002 
1003 
get_object_shape_and_transfer_mode(world_point3d * camera_location,short object_index,struct shape_and_transfer_mode * data)1004 void get_object_shape_and_transfer_mode(
1005 	world_point3d *camera_location,
1006 	short object_index,
1007 	struct shape_and_transfer_mode *data)
1008 {
1009 	struct object_data *object= get_object_data(object_index);
1010 	struct shape_animation_data *animation;
1011 	angle theta;
1012 	short view;
1013 
1014 	animation= get_shape_animation_data(object->shape);
1015 	// Added bug-outs in case of incorrect data; turned asserts into these tests:
1016 	if (!animation)
1017 	{
1018 		data->collection_code = NONE; // Deliberate bad value
1019 		return;
1020 	}
1021 	else if (!(animation->frames_per_view>=1))
1022 	{
1023 		data->collection_code = NONE; // Deliberate bad value
1024 		return;
1025 	}
1026 	// assert(animation->frames_per_view>=1);
1027 
1028 	/* get correct base shape */
1029 	// LP change: made long-distance friendly
1030 	theta= arctangent(int32(object->location.x) - int32(camera_location->x), int32(object->location.y) - int32(camera_location->y)) - object->facing;
1031 	switch (animation->number_of_views)
1032 	{
1033 		case _unanimated:
1034 		case _animated1:
1035 			view= 0;
1036 			break;
1037 
1038 		case _animated3to4: /* front, quarter and side views only */
1039 		case _animated4:
1040 			switch (FACING4(theta))
1041 			{
1042 				case 0: view= 3; break; /* 90� (facing left) */
1043 				case 1: view= 0; break; /* 0� (facing forward) */
1044 				case 2: view= 1; break; /* -90� (facing right) */
1045 				case 3: view= 2; break; /* �180� (facing away) */
1046 				default:
1047 					data->collection_code = NONE; // Deliberate bad value
1048 					return;
1049 			}
1050 			break;
1051 
1052 		case _animated3to5:
1053 		case _animated5:
1054 			theta+= HALF_CIRCLE;
1055 			switch (FACING5(theta))
1056 			{
1057 				case 0: view= 4; break;
1058 				case 1: view= 3; break;
1059 				case 2: view= 2; break;
1060 				case 3: view= 1; break;
1061 				case 4: view= 0; break;
1062 				default:
1063 					data->collection_code = NONE; // Deliberate bad value
1064 					return;
1065 			}
1066 			break;
1067 
1068 		case _animated2to8:
1069 		case _animated5to8:
1070 		case _animated8:
1071 			switch (FACING8(theta))
1072 			{
1073 				case 0: view= 3; break; /* 135� (facing left) */
1074 				case 1: view= 2; break; /* 90� (facing left) */
1075 				case 2: view= 1; break; /* 45� (facing left) */
1076 				case 3: view= 0; break; /* 0� (facing forward) */
1077 				case 4: view= 7; break; /* -45� (facing right) */
1078 				case 5: view= 6; break; /* -90� (facing right) */
1079 				case 6: view= 5; break; /* -135� (facing right) */
1080 				case 7: view= 4; break; /* �180� (facing away) */
1081 				default:
1082 					data->collection_code = NONE; // Deliberate bad value
1083 					return;
1084 			}
1085 			break;
1086 
1087 		default:
1088 			data->collection_code = NONE; // Deliberate bad value
1089 			return;
1090 	}
1091 
1092 	/* fill in the structure (transfer modes in the animation override transfer modes in the object */
1093 	data->collection_code= GET_DESCRIPTOR_COLLECTION(object->shape);
1094 	short Frame = GET_SEQUENCE_FRAME(object->sequence);
1095 	data->Frame = Frame;
1096 
1097 	// Guess next frame
1098 	short NextFrame = Frame + 1;
1099 	if (NextFrame >= animation->frames_per_view)
1100 		NextFrame = animation->loop_frame;
1101 	data->NextFrame = NextFrame;
1102 
1103 	// Work out the phase in the cycle; be sure to start a cycle with 0 and not 1
1104 	short Phase = GET_SEQUENCE_PHASE(object->sequence);
1105 	short Ticks = animation->ticks_per_frame;
1106 	if (Phase >= Ticks) Phase -= Ticks;
1107 	data->Phase = Phase;
1108 	data->Ticks = Ticks;
1109 
1110 	// What bitmap, etc.
1111 	data->low_level_shape_index= animation->low_level_shape_indexes[view*animation->frames_per_view + Frame];
1112 	if (animation->transfer_mode==_xfer_normal && object->transfer_mode!=NONE)
1113 	{
1114 		data->transfer_mode= object->transfer_mode;
1115 		data->transfer_phase= object->transfer_period ? INTEGER_TO_FIXED(object->transfer_phase)/object->transfer_period : 0;
1116 
1117 //		if (object->transfer_mode==_xfer_fold_out) dprintf("#%d/#%d==%x", object->transfer_phase, object->transfer_period, data->transfer_phase);
1118 	}
1119 	else
1120 	{
1121 		data->transfer_mode= animation->transfer_mode;
1122 		data->transfer_phase= (animation->transfer_mode!=_xfer_normal && animation->transfer_mode_period) ? INTEGER_TO_FIXED(object->transfer_phase)/animation->transfer_mode_period : 0;
1123 	}
1124 }
1125 
1126 extern bool shapes_file_is_m1();
1127 
randomize_object_sequence(short object_index,shape_descriptor shape)1128 bool randomize_object_sequence(
1129 	short object_index,
1130 	shape_descriptor shape)
1131 {
1132 	struct object_data *object= get_object_data(object_index);
1133 	struct shape_animation_data *animation;
1134 	bool randomized= false;
1135 
1136 	animation= get_shape_animation_data(shape);
1137 	if (!animation) return false;
1138 
1139 	switch (shapes_file_is_m1() ? _unanimated : animation->number_of_views)
1140 	{
1141 		case _unanimated:
1142 			object->shape= shape;
1143 			object->sequence= BUILD_SEQUENCE(global_random()%animation->frames_per_view, 0);
1144 			randomized= true;
1145 			break;
1146 	}
1147 
1148 	return randomized;
1149 }
1150 
set_object_shape_and_transfer_mode(short object_index,shape_descriptor shape,short transfer_mode)1151 void set_object_shape_and_transfer_mode(
1152 	short object_index,
1153 	shape_descriptor shape,
1154 	short transfer_mode)
1155 {
1156 	struct object_data *object= get_object_data(object_index);
1157 
1158 	if (object->shape!=shape)
1159 	{
1160 		struct shape_animation_data *animation= get_shape_animation_data(shape);
1161 		// Quit if a nonexistent animation
1162 		// assert(animation);
1163 		if (!animation) return;
1164 
1165 		object->shape= shape;
1166 		if (animation->transfer_mode!=_xfer_normal || object->transfer_mode==NONE) object->transfer_phase= 0;
1167 		object->sequence= BUILD_SEQUENCE(0, 1);
1168 		SET_OBJECT_ANIMATION_FLAGS(object, _obj_not_animated);
1169 
1170 		play_object_sound(object_index, animation->first_frame_sound);
1171 	}
1172 
1173 	if (transfer_mode!=NONE)
1174 	{
1175 		if (object->transfer_mode!=transfer_mode)
1176 		{
1177 			object->transfer_mode= transfer_mode;
1178 			object->transfer_phase= 0;
1179 		}
1180 	}
1181 }
1182 
1183 /* no longer called by RENDER.C; must be called by monster, projectile or effect controller;
1184 	now assumes �t==1 tick */
animate_object(short object_index)1185 void animate_object(
1186 	short object_index)
1187 {
1188 	struct object_data *object= get_object_data(object_index);
1189 	struct shape_animation_data *animation;
1190 	short animation_type= _obj_not_animated;
1191 
1192 	if (!OBJECT_IS_INVISIBLE(object)) /* invisible objects don�t have valid .shape fields */
1193 	{
1194 		animation= get_shape_animation_data(object->shape);
1195 		if (!animation) return;
1196 
1197 		/* if this animation has frames, animate it */
1198 		if (animation->frames_per_view>=1 && animation->number_of_views!=_unanimated)
1199 		{
1200 			short frame, phase;
1201 
1202 			// LP change: added some idiot-proofing to the ticks-per-frame value
1203 			if (animation->ticks_per_frame <= 0)
1204 				animation->ticks_per_frame = 1;
1205 
1206 			frame= GET_SEQUENCE_FRAME(object->sequence);
1207 			phase= GET_SEQUENCE_PHASE(object->sequence);
1208 
1209 			if (!frame && (!phase || phase>=animation->ticks_per_frame)) play_object_sound(object_index, animation->first_frame_sound);
1210 
1211 			/* phase is left unadjusted if it goes over ticks_per_frame until the next call */
1212 			if (phase>=animation->ticks_per_frame) phase-= animation->ticks_per_frame;
1213 			if ((phase+= 1)>=animation->ticks_per_frame)
1214 			{
1215 				frame+= 1;
1216 				if (!film_profile.keyframe_fix)
1217 				{
1218 					animation_type|= _obj_animated;
1219 					if (frame==animation->key_frame)
1220 					{
1221 						animation_type|= _obj_keyframe_started;
1222 						if (animation->key_frame_sound!=NONE) play_object_sound(object_index, animation->key_frame_sound);
1223 					}
1224 					if (frame>=animation->frames_per_view)
1225 					{
1226 						frame= animation->loop_frame;
1227 						animation_type|= _obj_last_frame_animated;
1228 						if (animation->last_frame_sound!=NONE) play_object_sound(object_index, animation->last_frame_sound);
1229 					}
1230 				}
1231 				else
1232 				{
1233 					// LP change: interchanged these two so that
1234 					// 1: keyframe 0 would get recognized
1235 					// 2: to keep the timing correct in the nonzero case
1236 					// LP change: inverted the order yet again to get more like Moo,
1237 					// but this time, added detection of cases
1238 					// keyframe = 0 and keyframe = [frames per view]
1239 					// Inverted the order yet again (!) to supporess Hunter death bug
1240 					animation_type|= _obj_animated;
1241 					if (frame>=animation->frames_per_view)
1242 					{
1243 						frame= animation->loop_frame;
1244 						animation_type|= _obj_last_frame_animated;
1245 						if (animation->last_frame_sound!=NONE) play_object_sound(object_index, animation->last_frame_sound);
1246 					}
1247 					short offset_frame = frame + animation->frames_per_view; // LP addition
1248 					if (frame==animation->key_frame || offset_frame==animation->key_frame)
1249 					{
1250 						animation_type|= _obj_keyframe_started;
1251 						if (animation->key_frame_sound!=NONE) play_object_sound(object_index, animation->key_frame_sound);
1252 					}
1253 				}
1254 			}
1255 
1256 			object->sequence= BUILD_SEQUENCE(frame, phase);
1257 		}
1258 
1259 		/* if this object has a transfer animation, update the transfer animation counter */
1260 		{
1261 			short period= (animation->transfer_mode==_xfer_normal && object->transfer_mode!=NONE) ? object->transfer_period : animation->transfer_mode_period;
1262 
1263 			if (period)
1264 			{
1265 				if ((object->transfer_phase+= 1)>=period)
1266 				{
1267 					animation_type|= _obj_transfer_mode_finished;
1268 					object->transfer_phase= 0;
1269 				}
1270 			}
1271 		}
1272 
1273 		SET_OBJECT_ANIMATION_FLAGS(object, animation_type);
1274 	}
1275 
1276 	/* This allows you to animate parasites of objects that are invisible (though */
1277 	/*  it is questionable if you would ever want to do that) */
1278 	/* if this object has any parasites, animate those too */
1279 	if (object->parasitic_object!=NONE) animate_object(object->parasitic_object);
1280 }
1281 
calculate_line_midpoint(short line_index,world_point3d * midpoint)1282 void calculate_line_midpoint(
1283 	short line_index,
1284 	world_point3d *midpoint)
1285 {
1286 	struct line_data *line= get_line_data(line_index);
1287 	struct world_point2d *e0= &get_endpoint_data(line->endpoint_indexes[0])->vertex;
1288 	struct world_point2d *e1= &get_endpoint_data(line->endpoint_indexes[1])->vertex;
1289 
1290 	midpoint->x= (e0->x+e1->x)>>1;
1291 	midpoint->y= (e0->y+e1->y)>>1;
1292 	midpoint->z= (line->lowest_adjacent_ceiling+line->highest_adjacent_floor)>>1;
1293 }
1294 
point_in_polygon(short polygon_index,world_point2d * p)1295 bool point_in_polygon(
1296 	short polygon_index,
1297 	world_point2d *p)
1298 {
1299 	struct polygon_data *polygon= get_polygon_data(polygon_index);
1300 	bool point_inside= true;
1301 	short i;
1302 
1303 	for (i=0;i<polygon->vertex_count;++i)
1304 	{
1305 		struct line_data *line= get_line_data(polygon->line_indexes[i]);
1306 		bool clockwise= line->endpoint_indexes[0]==polygon->endpoint_indexes[i];
1307 		world_point2d *e0= &get_endpoint_data(line->endpoint_indexes[0])->vertex;
1308 		world_point2d *e1= &get_endpoint_data(line->endpoint_indexes[1])->vertex;
1309 		int32 cross_product= (p->x-e0->x)*(e1->y-e0->y) - (p->y-e0->y)*(e1->x-e0->x);
1310 
1311 		if ((clockwise && cross_product>0) || (!clockwise && cross_product<0))
1312 		{
1313 			point_inside= false;
1314 			break;
1315 		}
1316 	}
1317 
1318 	return point_inside;
1319 }
1320 
clockwise_endpoint_in_line(short polygon_index,short line_index,short index)1321 short clockwise_endpoint_in_line(
1322 	short polygon_index,
1323 	short line_index,
1324 	short index)
1325 {
1326 	struct line_data *line= get_line_data(line_index);
1327 	bool line_is_clockwise= true;
1328 
1329 	if (line->clockwise_polygon_owner!=polygon_index)
1330 	{
1331 		// LP change: get around some Pfhorte bugs
1332 		line_is_clockwise= false;
1333 	}
1334 
1335 	switch (index)
1336 	{
1337 		case 0:
1338 			index= line_is_clockwise ? 0 : 1;
1339 			break;
1340 		case 1:
1341 			index= line_is_clockwise ? 1 : 0;
1342 			break;
1343 		default:
1344 			assert(false);
1345 			break;
1346 	}
1347 
1348 	return line->endpoint_indexes[index];
1349 }
1350 
world_point_to_polygon_index(world_point2d * location)1351 short world_point_to_polygon_index(
1352 	world_point2d *location)
1353 {
1354 	short polygon_index;
1355 	struct polygon_data *polygon;
1356 
1357 	for (polygon_index=0,polygon=map_polygons;polygon_index<dynamic_world->polygon_count;++polygon_index,++polygon)
1358 	{
1359 		if (!POLYGON_IS_DETACHED(polygon))
1360 		{
1361 			if (point_in_polygon(polygon_index, location)) break;
1362 		}
1363 	}
1364 	if (polygon_index==dynamic_world->polygon_count) polygon_index= NONE;
1365 
1366 	return polygon_index;
1367 }
1368 
1369 /* return the polygon on the other side of the given line from the given polygon (i.e., return
1370 	the polygon adjacent to line_index which isn�t polygon_index).  can return NONE. */
find_adjacent_polygon(short polygon_index,short line_index)1371 short find_adjacent_polygon(
1372 	short polygon_index,
1373 	short line_index)
1374 {
1375 	struct line_data *line= get_line_data(line_index);
1376 	short new_polygon_index;
1377 
1378 	if (polygon_index==line->clockwise_polygon_owner)
1379 	{
1380 		new_polygon_index= line->counterclockwise_polygon_owner;
1381 	}
1382 	else
1383 	{
1384 		// LP change: get around some Pfhorte bugs
1385 		new_polygon_index= line->clockwise_polygon_owner;
1386 	}
1387 
1388 	assert(new_polygon_index!=polygon_index);
1389 
1390 	return new_polygon_index;
1391 }
1392 
1393 /* Find the polygon whose attributes we'll mimic on a flooded platform */
find_flooding_polygon(short polygon_index)1394 short find_flooding_polygon(
1395 	short polygon_index)
1396 {
1397 	int i;
1398 	struct polygon_data *polygon = get_polygon_data(polygon_index);
1399 
1400 	for (i= 0; i<polygon->vertex_count; ++i)
1401 	{
1402 		if (polygon->adjacent_polygon_indexes[i]!=NONE)
1403 		{
1404 			struct polygon_data *adjacent_polygon= get_polygon_data(polygon->adjacent_polygon_indexes[i]);
1405 
1406 			if (adjacent_polygon->type == _polygon_is_major_ouch ||
1407 				adjacent_polygon->type == _polygon_is_minor_ouch)
1408 			{
1409 				return polygon->adjacent_polygon_indexes[i];
1410 			}
1411 		}
1412 	}
1413 	return NONE;
1414 }
1415 
find_adjacent_side(short polygon_index,short line_index)1416 short find_adjacent_side(
1417 	short polygon_index,
1418 	short line_index)
1419 {
1420 	struct line_data *line= get_line_data(line_index);
1421 	short side_index;
1422 
1423 	if (line->clockwise_polygon_owner==polygon_index)
1424 	{
1425 		side_index= line->clockwise_polygon_side_index;
1426 	}
1427 	else
1428 	{
1429 		assert(line->counterclockwise_polygon_owner==polygon_index);
1430 		side_index= line->counterclockwise_polygon_side_index;
1431 	}
1432 
1433 	return side_index;
1434 }
1435 
line_is_landscaped(short polygon_index,short line_index,world_distance z)1436 bool line_is_landscaped(
1437 	short polygon_index,
1438 	short line_index,
1439 	world_distance z)
1440 {
1441 	bool landscaped= false;
1442 	short side_index= find_adjacent_side(polygon_index, line_index);
1443 
1444 	if (side_index!=NONE)
1445 	{
1446 		struct line_data *line= get_line_data(line_index);
1447 		struct side_data *side= get_side_data(side_index);
1448 
1449 		switch (side->type)
1450 		{
1451 			case _full_side:
1452 				landscaped= side->primary_transfer_mode==_xfer_landscape;
1453 				break;
1454 			case _split_side: /* render _low_side first */
1455 				if (z<line->highest_adjacent_floor)
1456 				{
1457 					landscaped= side->secondary_transfer_mode==_xfer_landscape;
1458 					break;
1459 				}
1460 			case _high_side:
1461 				landscaped= z>line->lowest_adjacent_ceiling ?
1462 					side->primary_transfer_mode==_xfer_landscape :
1463 					side->transparent_transfer_mode==_xfer_landscape;
1464 				break;
1465 			case _low_side:
1466 				landscaped= z<line->highest_adjacent_floor ?
1467 					side->primary_transfer_mode==_xfer_landscape :
1468 					side->transparent_transfer_mode==_xfer_landscape;
1469 				break;
1470 
1471 			default:
1472 				assert(false);
1473 				break;
1474 		}
1475 	}
1476 
1477 	return landscaped;
1478 }
1479 
1480 /* return the line_index where the two polygons meet (or NONE if they don�t meet) */
find_shared_line(short polygon_index1,short polygon_index2)1481 short find_shared_line(
1482 	short polygon_index1,
1483 	short polygon_index2)
1484 {
1485 	struct polygon_data *polygon= get_polygon_data(polygon_index1);
1486 	short shared_line_index= NONE;
1487 	short i;
1488 
1489 	for (i=0;i<polygon->vertex_count;++i)
1490 	{
1491 		struct line_data *line= get_line_data(polygon->line_indexes[i]);
1492 		if (line->clockwise_polygon_owner==polygon_index2||line->counterclockwise_polygon_owner==polygon_index2)
1493 		{
1494 			shared_line_index= polygon->line_indexes[i];
1495 			break;
1496 		}
1497 	}
1498 
1499 	return shared_line_index;
1500 }
1501 
get_object_light_intensity(short object_index)1502 _fixed get_object_light_intensity(
1503 	short object_index)
1504 {
1505 	struct object_data *object= get_object_data(object_index);
1506 	struct polygon_data *polygon= get_polygon_data(object->polygon);
1507 
1508 	return get_light_intensity(polygon->floor_lightsource_index);
1509 }
1510 
1511 /* returns the line_index of the line we intersected to leave this polygon, or NONE if destination
1512 	is in the given polygon */
find_line_crossed_leaving_polygon(short polygon_index,world_point2d * p0,world_point2d * p1)1513 short find_line_crossed_leaving_polygon(
1514 	short polygon_index,
1515 	world_point2d *p0, /* origin (not necessairly in polygon_index) */
1516 	world_point2d *p1) /* destination (not necessairly in polygon_index) */
1517 {
1518 	struct polygon_data *polygon= get_polygon_data(polygon_index);
1519 	short intersected_line_index= NONE;
1520 	short i;
1521 
1522 	for (i= 0; i<polygon->vertex_count; ++i)
1523 	{
1524 		/* e1 is clockwise from e0 */
1525 		world_point2d *e0= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex;
1526 		world_point2d *e1= &get_endpoint_data(polygon->endpoint_indexes[i==polygon->vertex_count-1?0:i+1])->vertex;
1527 
1528 		/* if e0p1 cross e0e1 is negative, p1 is on the outside of edge e0e1 (a result of zero
1529 			means p1 is on the line e0e1) */
1530 		if ((p1->x-e0->x)*(e1->y-e0->y) - (p1->y-e0->y)*(e1->x-e0->x) > 0)
1531 		{
1532 			/* if p0e1 cross p0p1 is positive, p0p1 crosses e0e1 to the left of e1 */
1533 			if ((e1->x-p0->x)*(p1->y-p0->y) - (e1->y-p0->y)*(p1->x-p0->x) <= 0)
1534 			{
1535 				/* if p0e0 cross p0p1 is negative or zero, p0p1 crosses e0e1 on or to the right of e0 */
1536 				if ((e0->x-p0->x)*(p1->y-p0->y) - (e0->y-p0->y)*(p1->x-p0->x) >= 0)
1537 				{
1538 					intersected_line_index= polygon->line_indexes[i];
1539 					break;
1540 				}
1541 			}
1542 		}
1543 	}
1544 
1545 	return intersected_line_index;
1546 }
1547 
1548 /* calculate the 3d intersection of the line segment p0p1 with the line e0e1 */
find_line_intersection(world_point2d * e0,world_point2d * e1,world_point3d * p0,world_point3d * p1,world_point3d * intersection)1549 _fixed find_line_intersection(
1550 	world_point2d *e0,
1551 	world_point2d *e1,
1552 	world_point3d *p0,
1553 	world_point3d *p1,
1554 	world_point3d *intersection)
1555 {
1556 	world_distance dx, dy, dz, line_dx, line_dy;
1557 	int32 numerator, denominator;
1558 	_fixed t;
1559 
1560 	/* calculate line deltas */
1561 	dx= p1->x-p0->x, dy= p1->y-p0->y, dz= p1->z-p0->z;
1562 	line_dx= e1->x-e0->x, line_dy= e1->y-e0->y;
1563 
1564 	/* calculate the numerator and denominator to compute t; our basic strategy here is to
1565 		shift the numerator up by eight bits and the denominator down by eight bits, yeilding
1566 		a fixed number in [0,FIXED_ONE] for t.  this won�t work if the numerator is greater
1567 		than or equal to 2^24, or the numerator is less than 2^8.  the first case can�t be
1568 		fixed in any decent way and shouldn�t happen if we have small deltas.  the second case
1569 		is approximated with a denominator of 1 or -1 (depending on the sign of the old
1570 		denominator, although notice here that numbers in [1,2^8) will get downshifted to
1571 		zero and then set to one, while numbers in (-2^8,-1] will get downshifted to -1 and
1572 		left there) */
1573 	numerator= line_dx*(e0->y-p0->y) + line_dy*(p0->x-e0->x);
1574 	denominator= line_dx*dy - line_dy*dx;
1575 	while (numerator>=(1<<24)||numerator<=((-1)<<24)) numerator>>= 1, denominator>>= 1;
1576 	assert(numerator<(1<<24));
1577 	numerator<<= 8;
1578 	if (!(denominator>>= 8)) denominator= 1;
1579 	t= numerator/denominator;
1580 
1581 	intersection->x = p0->x + FIXED_INTEGERAL_PART(int32(1LL*t*dx));
1582 	intersection->y = p0->y + FIXED_INTEGERAL_PART(int32(1LL*t*dy));
1583 	intersection->z = p0->z + FIXED_INTEGERAL_PART(int32(1LL*t*dz));
1584 
1585 	return t;
1586 }
1587 
1588 /* closest_point may be the same as p; if we�re within 1 of our source point in either
1589 	direction assume that we are actually at the source point */
closest_point_on_line(world_point2d * e0,world_point2d * e1,world_point2d * p,world_point2d * closest_point)1590 _fixed closest_point_on_line(
1591 	world_point2d *e0,
1592 	world_point2d *e1,
1593 	world_point2d *p,
1594 	world_point2d *closest_point)
1595 {
1596 	world_distance line_dx, line_dy, dx, dy;
1597 	world_point2d calculated_closest_point;
1598 	int32 numerator, denominator;
1599 	_fixed t;
1600 
1601 	/* calculate dx,dy and line_dx,line_dy */
1602 	dx= p->x-e0->x, dy= p->y-e0->y;
1603 	line_dx= e1->x-e0->x, line_dy= e1->y-e0->y;
1604 
1605 	/* same comment as above for calculating t; this is not wholly accurate */
1606 	numerator= line_dx*dx + line_dy*dy;
1607 	denominator= line_dx*line_dx + line_dy*line_dy;
1608 	while (numerator>=(1<<23)||numerator<=(-1<<23)) numerator>>= 1, denominator>>= 1;
1609 	numerator<<= 8;
1610 	if (!(denominator>>= 8)) denominator= 1;
1611 	t= numerator/denominator;
1612 
1613 	/* if we�ve only changed by �1 in x and y, return the original p to avoid sliding down
1614 		the edge on successive calls */
1615 	calculated_closest_point.x= e0->x + FIXED_INTEGERAL_PART(t*line_dx);
1616 	calculated_closest_point.y= e0->y + FIXED_INTEGERAL_PART(t*line_dy);
1617 	switch (calculated_closest_point.x-p->x)
1618 	{
1619 		case -1:
1620 		case 0:
1621 		case 1:
1622 			switch (calculated_closest_point.y-p->y)
1623 			{
1624 				case -1:
1625 				case 0:
1626 				case 1:
1627 					calculated_closest_point= *p;
1628 					break;
1629 			}
1630 	}
1631 	*closest_point= calculated_closest_point;
1632 
1633 	return t;
1634 }
1635 
closest_point_on_circle(world_point2d * c,world_distance radius,world_point2d * p,world_point2d * closest_point)1636 void closest_point_on_circle(
1637 	world_point2d *c,
1638 	world_distance radius,
1639 	world_point2d *p,
1640 	world_point2d *closest_point)
1641 {
1642 	world_distance dx= p->x - c->x;
1643 	world_distance dy= p->y - c->y;
1644 	world_distance magnitude= isqrt(dx*dx + dy*dy);
1645 
1646 	if (magnitude)
1647 	{
1648 		closest_point->x= c->x + (dx*radius)/magnitude;
1649 		closest_point->y= c->y + (dy*radius)/magnitude;
1650 	}
1651 	else
1652 	{
1653 		*closest_point= *p;
1654 	}
1655 }
1656 
find_center_of_polygon(short polygon_index,world_point2d * center)1657 void find_center_of_polygon(
1658 	short polygon_index,
1659 	world_point2d *center)
1660 {
1661 	struct polygon_data *polygon= get_polygon_data(polygon_index);
1662 	int32 x= 0, y= 0;
1663 	short i;
1664 
1665 	for (i=0;i<polygon->vertex_count;++i)
1666 	{
1667 		world_point2d *p= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex;
1668 
1669 		x+= p->x, y+= p->y;
1670 	}
1671 
1672     // polygon->vertex_count could possibly be zero, unsure of what to do here
1673     // making a note.
1674 	center->x= x/polygon->vertex_count;
1675 	center->y= y/polygon->vertex_count;
1676 }
1677 
1678 /* calculate 3d intersection of the line p0p1 with the plane z=h */
find_floor_or_ceiling_intersection(world_distance h,world_point3d * p0,world_point3d * p1,world_point3d * intersection)1679 _fixed find_floor_or_ceiling_intersection(
1680 	world_distance h,
1681 	world_point3d *p0,
1682 	world_point3d *p1,
1683 	world_point3d *intersection)
1684 {
1685 	_fixed t;
1686 	world_distance dx, dy, dz;
1687 
1688 	dx= p1->x-p0->x, dy= p1->y-p0->y, dz= p1->z-p0->z;
1689 	t= dz ? INTEGER_TO_FIXED(h-p0->z)/dz : 0; /* if dz==0, return (p0.x,p0.y,h) */
1690 
1691 	intersection->x= p0->x + FIXED_INTEGERAL_PART(int32(1LL*t*dx));
1692 	intersection->y= p0->y + FIXED_INTEGERAL_PART(int32(1LL*t*dy));
1693 	intersection->z= h;
1694 
1695 	return t;
1696 }
1697 
1698 enum /* keep out states */
1699 {
1700 	_first_line_pass,
1701 	_second_line_pass, /* if _first_line_pass yeilded more than one collision we have to go back
1702 		and make sure we don�t hit anything (or only hit one thing which we hit the first time) */
1703 	_second_line_pass_made_contact, /* we�ve already hit one thing we hit last time, if we hit
1704 		anything else then we abort */
1705 	_aborted, /* if we hit two lines on the second pass, we give up */
1706 	_point_pass /* checking against all points (and hit as many as we can) */
1707 };
1708 
1709 /* returns height at clipped p1 */
keep_line_segment_out_of_walls(short polygon_index,world_point3d * p0,world_point3d * p1,world_distance maximum_delta_height,world_distance height,world_distance * adjusted_floor_height,world_distance * adjusted_ceiling_height,short * supporting_polygon_index)1710 bool keep_line_segment_out_of_walls(
1711 	short polygon_index, /* where we started */
1712 	world_point3d *p0,
1713 	world_point3d *p1,
1714 	world_distance maximum_delta_height, /* the maximum positive change in height we can tolerate */
1715 	world_distance height, /* the height of the object being moved */
1716 	world_distance *adjusted_floor_height,
1717 	world_distance *adjusted_ceiling_height,
1718 	short *supporting_polygon_index)
1719 {
1720 	struct polygon_data *polygon= get_polygon_data(polygon_index);
1721 	short *indexes= get_map_indexes(polygon->first_exclusion_zone_index, polygon->line_exclusion_zone_count+polygon->point_exclusion_zone_count);
1722 	int32 line_collision_bitmap;
1723 	bool clipped= false;
1724 	short state;
1725 	short i;
1726 
1727 	// Skip the whole thing if exclusion-zone indexes were not found
1728 	if (!indexes)
1729 	{
1730 		polygon->line_exclusion_zone_count = 0;
1731 		polygon->point_exclusion_zone_count = 0;
1732 		return clipped;
1733 	}
1734 
1735 //	if (polygon_index==23) dprintf("#%d lines, #%d endpoints at %p", polygon->line_exclusion_zone_count, polygon->point_exclusion_zone_count, indexes);
1736 
1737 	state= _first_line_pass;
1738 	line_collision_bitmap= 0;
1739 	*supporting_polygon_index= polygon_index;
1740 	*adjusted_floor_height= polygon->floor_height;
1741 	*adjusted_ceiling_height= polygon->ceiling_height;
1742 	do
1743 	{
1744 		for (i=0;i<polygon->line_exclusion_zone_count&&state!=_aborted;++i)
1745 		{
1746 			short signed_line_index= indexes[i];
1747 			short unsigned_line_index= signed_line_index<0 ? -signed_line_index-1 : signed_line_index;
1748 
1749 			// If there is some map-index screwup...
1750 			if (unsigned_line_index >= dynamic_world->line_count)
1751 				continue;
1752 
1753 			struct line_data *line= get_line_data(unsigned_line_index);
1754 			short side_index= signed_line_index<0 ? line->counterclockwise_polygon_side_index : line->clockwise_polygon_side_index;
1755 
1756 //			if (unsigned_line_index==104) dprintf("checking against #%d", unsigned_line_index);
1757 
1758 			if (side_index!=NONE)
1759 			{
1760 				struct side_exclusion_zone *zone= &get_side_data(side_index)->exclusion_zone;
1761 
1762 				if ((p1->x-zone->e0.x)*(zone->e1.y-zone->e0.y) - (p1->y-zone->e0.y)*(zone->e1.x-zone->e0.x) > 0 &&
1763 					(p1->x-zone->e2.x)*(zone->e0.y-zone->e2.y) - (p1->y-zone->e2.y)*(zone->e0.x-zone->e2.x) > 0 &&
1764 					(p1->x-zone->e1.x)*(zone->e3.y-zone->e1.y) - (p1->y-zone->e1.y)*(zone->e3.x-zone->e1.x) > 0)
1765 				{
1766 					short adjacent_polygon_index= signed_line_index<0 ? line->clockwise_polygon_owner : line->counterclockwise_polygon_owner;
1767 					struct polygon_data *adjacent_polygon= adjacent_polygon_index==NONE ? NULL : get_polygon_data(adjacent_polygon_index);
1768 					world_distance lowest_ceiling;
1769 					world_distance highest_floor;
1770 
1771 					if (adjacent_polygon) {
1772 						lowest_ceiling= adjacent_polygon->ceiling_height<polygon->ceiling_height ? adjacent_polygon->ceiling_height : polygon->ceiling_height;
1773 						highest_floor= adjacent_polygon->floor_height>polygon->floor_height ? adjacent_polygon->floor_height : polygon->floor_height;
1774 					} else {
1775 						lowest_ceiling = highest_floor = 0;
1776 					}
1777 
1778 					/* if a) this line is solid, b) the new polygon is farther than maximum_delta height
1779 						above our feet, or c) the new polygon is lower than the top of our head then
1780 						we can�t move into the new polygon */
1781 					if (LINE_IS_SOLID(line) || adjacent_polygon == NULL ||
1782 						adjacent_polygon->floor_height-p1->z>maximum_delta_height ||
1783 						adjacent_polygon->ceiling_height-p1->z<height ||
1784 						lowest_ceiling-highest_floor<height)
1785 					{
1786 					//	if (unsigned_line_index==104) dprintf("inside solid line #%d (%p) in polygon #%d", unsigned_line_index, line, polygon_index);
1787 
1788 						switch (state)
1789 						{
1790 							case _first_line_pass:
1791 								/* first pass: set the flag and do the clip */
1792 								line_collision_bitmap|= 1<<i;
1793 								closest_point_on_line(&zone->e0, &zone->e1, (world_point2d*)p1, (world_point2d*)p1);
1794 								clipped= true;
1795 								break;
1796 
1797 							case _second_line_pass:
1798 								if (line_collision_bitmap&(1<<i))
1799 								{
1800 									/* we hit this line before, change states (we can only hit one thing we hit before) */
1801 									closest_point_on_line(&zone->e0, &zone->e1, (world_point2d*)p1, (world_point2d*)p1);
1802 									state= _second_line_pass_made_contact;
1803 								}
1804 								else
1805 								{
1806 									/* forget it; we hit something we didn�t hit the first time */
1807 									state= _aborted;
1808 								}
1809 								break;
1810 
1811 							case _second_line_pass_made_contact:
1812 								/* we have no tolerance for hitting two things during the second pass */
1813 								state= _aborted;
1814 								break;
1815 
1816 							default:
1817 								assert(false);
1818 						}
1819 					}
1820 					else
1821 					{
1822 						if (adjacent_polygon->floor_height>*adjusted_floor_height) {
1823 							*adjusted_floor_height= adjacent_polygon->floor_height;
1824 							*supporting_polygon_index= adjacent_polygon_index;
1825 						}
1826 						if (adjacent_polygon->ceiling_height>*adjusted_ceiling_height) {
1827 							*adjusted_ceiling_height= adjacent_polygon->ceiling_height;
1828 						}
1829 					}
1830 				}
1831 			}
1832 		}
1833 
1834 		switch (state)
1835 		{
1836 			case _first_line_pass:
1837 				state= _second_line_pass; break;
1838 			case _second_line_pass:
1839 			case _second_line_pass_made_contact:
1840 				state= _point_pass; break;
1841 		}
1842 	}
1843 	while (state==_second_line_pass);
1844 
1845 	/* if we didn�t abort while clipping lines, try clipping against points... */
1846 	if (state!=_aborted)
1847 	{
1848 		for (i=0;i<polygon->point_exclusion_zone_count;++i)
1849 		{
1850 			short endpoint_index = indexes[polygon->line_exclusion_zone_count+i];
1851 
1852 			// If there is some map-index screwup...
1853 			if (endpoint_index < 0 || endpoint_index >= dynamic_world->endpoint_count)
1854 				continue;
1855 
1856 			struct endpoint_data *endpoint= get_endpoint_data(endpoint_index);
1857 			world_distance dx= endpoint->vertex.x-p1->x;
1858 			world_distance dy= endpoint->vertex.y-p1->y;
1859 			int32 distance_squared= dx*dx+dy*dy;
1860 
1861 //			switch (indexes[polygon->line_exclusion_zone_count+i])
1862 //			{
1863 //				case 34:
1864 //				case 35:
1865 //					dprintf("endpoint#%d is %d away", indexes[polygon->line_exclusion_zone_count+i], distance_squared);
1866 //			}
1867 
1868 			if (distance_squared<MINIMUM_SEPARATION_FROM_WALL*MINIMUM_SEPARATION_FROM_WALL)
1869 			{
1870 				if (endpoint->highest_adjacent_floor_height-p1->z>maximum_delta_height ||
1871 					endpoint->lowest_adjacent_ceiling_height-p1->z<height ||
1872 					ENDPOINT_IS_SOLID(endpoint))
1873 				{
1874 					closest_point_on_circle(&endpoint->vertex, MINIMUM_SEPARATION_FROM_WALL, (world_point2d*)p1, (world_point2d*)p1);
1875 					clipped= true;
1876 				}
1877 				else
1878 				{
1879 					if (endpoint->highest_adjacent_floor_height>*adjusted_floor_height) *adjusted_floor_height= endpoint->highest_adjacent_floor_height, *supporting_polygon_index= endpoint->supporting_polygon_index;
1880 					if (endpoint->lowest_adjacent_ceiling_height>*adjusted_ceiling_height) *adjusted_ceiling_height= endpoint->lowest_adjacent_ceiling_height;
1881 				}
1882 			}
1883 		}
1884 	}
1885 
1886 	if (state==_aborted) p1->x= p0->x, p1->y= p0->y;
1887 	return clipped;
1888 }
1889 
1890 /* take the line e0e1 and destructively move it perpendicular to itself ("to the left" when looking
1891 	along e0e1) by the given distance d */
push_out_line(world_point2d * e0,world_point2d * e1,world_distance d,world_distance line_length)1892 void push_out_line(
1893 	world_point2d *e0,
1894 	world_point2d *e1,
1895 	world_distance d,
1896 	world_distance line_length)
1897 {
1898 	world_distance line_dx, line_dy;
1899 	world_distance dx, dy;
1900 
1901 	/* if line_length is zero, calculate it */
1902 	if (!line_length) {
1903 		line_length= distance2d(e0, e1);
1904 		if (!line_length)
1905 			return;
1906 	}
1907 
1908 	/* calculate dx, dy (a vector of length d perpendicular (outwards) to the line e0e1 */
1909 	line_dx= e1->x-e0->x, line_dy= e1->y-e0->y;
1910 	dx= - (d*line_dy)/line_length, dy= (d*line_dx)/line_length;
1911 
1912 	/* adjust the line */
1913 	e0->x+= dx, e0->y+= dy;
1914 	e1->x+= dx, e1->y+= dy;
1915 }
1916 
1917 /* given the ray p0,theta,d, calculate a point p1 such that p1 is on the ray but still inside
1918 	the [-32k,32k] bounds of our map. p0 can be the same as p1 */
ray_to_line_segment(world_point2d * p0,world_point2d * p1,angle theta,world_distance d)1919 void ray_to_line_segment(
1920 	world_point2d *p0,
1921 	world_point2d *p1,
1922 	angle theta,
1923 	world_distance d)
1924 {
1925 	short dx= cosine_table[theta], dy= sine_table[theta];
1926 	int32 x= (int32)p0->x + (int32)((d*dx)>>TRIG_SHIFT);
1927 	int32 y= (int32)p0->y + (int32)((d*dy)>>TRIG_SHIFT);
1928 
1929 	if (x<INT16_MIN) x= INT16_MIN, y= (int32)p0->y + (dy*(INT16_MIN-p0->x))/dx;
1930 	if (x>INT16_MAX) x= INT16_MAX, y= (int32)p0->y + (dy*(INT16_MAX-p0->x))/dx;
1931 	if (y<INT16_MIN) y= INT16_MIN, x= (int32)p0->x + (dx*(INT16_MIN-p0->y))/dy;
1932 	if (y>INT16_MAX) y= INT16_MAX, x= (int32)p0->x + (dx*(INT16_MAX-p0->y))/dy;
1933 
1934 	p1->x= x;
1935 	p1->y= y;
1936 }
1937 
1938 /* computes the squared distance from p to the line segment e0e1 */
point_to_line_segment_distance_squared(world_point2d * p,world_point2d * a,world_point2d * b)1939 int32 point_to_line_segment_distance_squared(
1940 	world_point2d *p,
1941 	world_point2d *a,
1942 	world_point2d *b)
1943 {
1944 	world_distance abx= b->x-a->x, aby= b->y-a->y;
1945 	world_distance apx= p->x-a->x, apy= p->y-a->y;
1946 	world_distance bpx= p->x-b->x, bpy= p->y-b->y;
1947 	int32 distance;
1948 
1949 	/* if AB dot BP is greather than or equal to zero, d is the distance between B and P */
1950 	if (abx*bpx+aby*bpy>=0)
1951 	{
1952 		distance= bpx*bpx + bpy*bpy;
1953 	}
1954 	else
1955 	{
1956 		/* if BA dot AP is greather than or equal to zero, d is the distance between A and P
1957 			(we don�t calculate BA and use -AB instead */
1958 		if (abx*apx+aby*apy<=0)
1959 		{
1960 			distance= apx*apx + apy*apy;
1961 		}
1962 		else
1963 		{
1964 			distance= point_to_line_distance_squared(p, a, b);
1965 		}
1966 	}
1967 
1968 	return distance;
1969 }
1970 
point_to_line_distance_squared(world_point2d * p,world_point2d * a,world_point2d * b)1971 int32 point_to_line_distance_squared(
1972 	world_point2d *p,
1973 	world_point2d *a,
1974 	world_point2d *b)
1975 {
1976 	world_distance abx= b->x-a->x, aby= b->y-a->y;
1977 	world_distance apx= p->x-a->x, apy= p->y-a->y;
1978 	int32 signed_numerator;
1979 	uint32 numerator, denominator;
1980 
1981 	/* numerator is absolute value of the cross product of AB and AP, denominator is the
1982 		magnitude of AB squared */
1983 	signed_numerator= apx*aby - apy*abx;
1984 	numerator= ABS(signed_numerator);
1985 	denominator= abx*abx + aby*aby;
1986 
1987 	/* before squaring numerator we make sure that it is smaller than fifteen bits (and we
1988 		adjust the denominator to compensate).  if denominator==0 then we make it ==1.  */
1989 	while (numerator>=(1<<16)) numerator>>= 1, denominator>>= 2;
1990 	if (!denominator) denominator= 1;
1991 
1992 	return (numerator*numerator)/denominator;
1993 }
1994 
get_next_map_annotation(short * count)1995 struct map_annotation *get_next_map_annotation(
1996 	short *count)
1997 {
1998 	struct map_annotation *annotation= (struct map_annotation *) NULL;
1999 
2000 	if (*count<dynamic_world->default_annotation_count) annotation= map_annotations + (*count)++;
2001 
2002 	return annotation;
2003 }
2004 
2005 /* for saving or whatever; finds the highest used index plus one for objects, monsters, projectiles
2006 	and effects */
recalculate_map_counts(void)2007 void recalculate_map_counts(
2008 	void)
2009 {
2010 	struct object_data *object;
2011 	struct monster_data *monster;
2012 	struct projectile_data *projectile;
2013 	struct effect_data *effect;
2014 	struct light_data *light;
2015 	size_t count;
2016 
2017 	// LP: fixed serious bug in the counting logic
2018 
2019 	for (count=MAXIMUM_OBJECTS_PER_MAP,object=objects+MAXIMUM_OBJECTS_PER_MAP-1;
2020 			count>0&&(!SLOT_IS_USED(object));
2021 			--count,--object)
2022 		;
2023 	dynamic_world->object_count= static_cast<int16>(count);
2024 
2025 	for (count=MAXIMUM_MONSTERS_PER_MAP,monster=monsters+MAXIMUM_MONSTERS_PER_MAP-1;
2026 			count>0&&(!SLOT_IS_USED(monster));
2027 			--count,--monster)
2028 		;
2029 	dynamic_world->monster_count= static_cast<int16>(count);
2030 
2031 	for (count=MAXIMUM_PROJECTILES_PER_MAP,projectile=projectiles+MAXIMUM_PROJECTILES_PER_MAP-1;
2032 			count>0&&(!SLOT_IS_USED(projectile));
2033 			--count,--projectile)
2034 		;
2035 	dynamic_world->projectile_count= static_cast<int16>(count);
2036 
2037 	for (count=MAXIMUM_EFFECTS_PER_MAP,effect=effects+MAXIMUM_EFFECTS_PER_MAP-1;
2038 			count>0&&(!SLOT_IS_USED(effect));
2039 			--count,--effect)
2040 		;
2041 	dynamic_world->effect_count= static_cast<int16>(count);
2042 
2043 	for (count=MAXIMUM_LIGHTS_PER_MAP,light=lights+MAXIMUM_LIGHTS_PER_MAP-1;
2044 			count>0&&(!SLOT_IS_USED(light));
2045 			--count,--light)
2046 		;
2047 	dynamic_world->light_count= static_cast<int16>(count);
2048 }
2049 
change_polygon_height(short polygon_index,world_distance new_floor_height,world_distance new_ceiling_height,struct damage_definition * damage)2050 bool change_polygon_height(
2051 	short polygon_index,
2052 	world_distance new_floor_height,
2053 	world_distance new_ceiling_height,
2054 	struct damage_definition *damage)
2055 {
2056 	bool legal_change;
2057 
2058 	/* returns false if a monster prevented the given change from ocurring (and probably did damage
2059 		to him or maybe even caused him to pop) */
2060 	legal_change= legal_polygon_height_change(polygon_index, new_floor_height, new_ceiling_height, damage);
2061 
2062 	/* if this was a legal change, adjust all objects (handle monsters separately) */
2063 	if (legal_change)
2064 	{
2065 		struct polygon_data *polygon= get_polygon_data(polygon_index);
2066 		short object_index= polygon->first_object;
2067 
2068 		/* Change the objects heights... */
2069 		while (object_index!=NONE)
2070 		{
2071 			struct object_data *object= get_object_data(object_index);
2072 
2073 			if (OBJECT_IS_VISIBLE(object))
2074 			{
2075 				switch (GET_OBJECT_OWNER(object))
2076 				{
2077 					case _object_is_monster:
2078 						adjust_monster_for_polygon_height_change(object->permutation, polygon_index, new_floor_height, new_ceiling_height);
2079 						break;
2080 
2081 					default:
2082 						if (object->location.z==polygon->floor_height) object->location.z= new_floor_height;
2083 						break;
2084 				}
2085 			}
2086 
2087 			object_index= object->next_object;
2088 		}
2089 
2090 		/* slam the polygon heights, directly */
2091 		polygon->floor_height= new_floor_height;
2092 		polygon->ceiling_height= new_ceiling_height;
2093 
2094 		/* the highest_adjacent_floor, lowest_adjacent_ceiling and supporting_polygon_index fields
2095 			of all of this polygon�s endpoints and lines are potentially invalid now.  to assure
2096 			that they are correct, recalculate them using the appropriate redundant functions.
2097 			to do things quickly, slam them yourself.  only these three fields of are invalid,
2098 			nothing else is effected by the height change. */
2099 	}
2100 
2101 	return legal_change;
2102 }
2103 
2104 /* we used to check to see that the point in question was within the player�s view
2105 	cone, but that was queer because stuff would appear behind him all the time
2106 	(which was completely inconvient when this happened to monsters) */
2107 /*
2108 	Added max_players, because this could be called during initial player creation,
2109 	when dynamic_world->player_count was not valid.
2110 */
point_is_player_visible(short max_players,short polygon_index,world_point2d * p,int32 * distance)2111 bool point_is_player_visible(
2112 	short max_players,
2113 	short polygon_index,
2114 	world_point2d *p,
2115 	int32 *distance)
2116 {
2117 	short player_index;
2118 	bool visible= false;
2119 
2120 	*distance= INT32_MAX; /* infinite */
2121 	for (player_index=0;player_index<max_players;++player_index)
2122 	{
2123 		struct player_data *player= get_player_data(player_index);
2124 		struct monster_data *monster= get_monster_data(player->monster_index);
2125 		struct object_data *object= get_object_data(monster->object_index);
2126 
2127 		if (!line_is_obstructed(object->polygon, (world_point2d*)&object->location, polygon_index, p))
2128 		{
2129 			int32 this_distance= guess_distance2d((world_point2d*)&object->location, p);
2130 
2131 			if (*distance>this_distance) *distance= this_distance;
2132 			visible= true;
2133 		}
2134 	}
2135 
2136 	return visible;
2137 }
2138 
point_is_monster_visible(short polygon_index,world_point2d * p,int32 * distance)2139 bool point_is_monster_visible(
2140 	short polygon_index,
2141 	world_point2d *p,
2142 	int32 *distance)
2143 {
2144 	size_t  object_count;
2145 
2146 	*distance = INT32_MAX; /* infinite */
2147 
2148 	// LP change:
2149 	IntersectedObjects.clear();
2150 	possible_intersecting_monsters(&IntersectedObjects, LOCAL_INTERSECTING_MONSTER_BUFFER_SIZE, polygon_index, false);
2151 	object_count = IntersectedObjects.size();
2152 
2153 	for (size_t i=0;i<object_count;++i)
2154 	{
2155 		// LP change:
2156 		struct object_data *object= get_object_data(IntersectedObjects[i]);
2157 		int32 this_distance;
2158 
2159 		this_distance = guess_distance2d((world_point2d*)&object->location, p);
2160 		if (*distance>this_distance) *distance= this_distance;
2161 	}
2162 
2163 	return *distance!=INT32_MAX;
2164 }
2165 
line_is_obstructed(short polygon_index1,world_point2d * p1,short polygon_index2,world_point2d * p2)2166 bool line_is_obstructed(
2167 	short polygon_index1,
2168 	world_point2d *p1,
2169 	short polygon_index2,
2170 	world_point2d *p2)
2171 {
2172 	short polygon_index= polygon_index1;
2173 	bool obstructed= false;
2174 	short line_index;
2175 
2176 	do
2177 	{
2178 		bool last_line = false;
2179 		if (film_profile.line_is_obstructed_fix)
2180 		{
2181 			line_index = find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)p1, (world_point2d *)p2);
2182 		}
2183 		else
2184 		{
2185 			line_index= _find_line_crossed_leaving_polygon(polygon_index, (world_point2d *)p1, (world_point2d *)p2, &last_line);
2186 		}
2187 
2188 		if (line_index!=NONE)
2189 		{
2190 			if (last_line && polygon_index==polygon_index2) break;
2191 			if (!LINE_IS_SOLID(get_line_data(line_index)))
2192 			{
2193 				/* transparent line, find adjacent polygon */
2194 				polygon_index= find_adjacent_polygon(polygon_index, line_index);
2195 				assert(polygon_index!=NONE);
2196 			}
2197 			else
2198 			{
2199 				obstructed= true; /* non-transparent line */
2200 			}
2201 			if (last_line)
2202 			{
2203 				if (polygon_index==polygon_index2) break;
2204 				obstructed= true;
2205 				break;
2206 			}
2207 		}
2208 		else
2209 		{
2210 			/* the polygon we ended up in is different than the polygon the caller thinks the
2211 				destination point is in; this probably means that the source is on a different
2212 				level than the caller, but it could also easily mean that we�re dealing with
2213 				weird boundary conditions of find_line_crossed_leaving_polygon() */
2214 			if (polygon_index!=polygon_index2)
2215 			{
2216 				obstructed= true;
2217 				if (film_profile.line_is_obstructed_fix)
2218 				{
2219 					polygon_data* polygon = get_polygon_data(polygon_index);
2220 					polygon_data* polygon2 = get_polygon_data(polygon_index2);
2221 					for (int i = 0; i < polygon->vertex_count; ++i)
2222 					{
2223 						for (int j = 0; j < polygon2->vertex_count; ++j)
2224 						{
2225 							if (polygon->endpoint_indexes[i] == polygon2->endpoint_indexes[j])
2226 							{
2227 								// if our destination polygon shares any endpoints with our actual destination, we're ok
2228 								obstructed = false;
2229 								break;
2230 							}
2231 						}
2232 					}
2233 				}
2234 			}
2235 		}
2236 	}
2237 	while (!obstructed&&line_index!=NONE);
2238 
2239 	return obstructed;
2240 }
2241 
2242 #define MAXIMUM_GARBAGE_OBJECTS_PER_MAP 256
2243 #define MAXIMUM_GARBAGE_OBJECTS_PER_POLYGON 10
2244 
turn_object_to_shit(short garbage_object_index)2245 void turn_object_to_shit( /* garbage that is, garbage */
2246 	short garbage_object_index)
2247 {
2248 	struct object_data *garbage_object= get_object_data(garbage_object_index);
2249 	struct polygon_data *polygon= get_polygon_data(garbage_object->polygon);
2250 	short garbage_objects_in_polygon, random_garbage_object_index = 0, object_index;
2251 
2252 	struct object_data *object;
2253 
2254 	/* count the number of garbage objects in this polygon */
2255 	garbage_objects_in_polygon= 0;
2256 	for (object_index=polygon->first_object;object_index!=NONE;object_index=object->next_object)
2257 	{
2258 		object= get_object_data(object_index);
2259 		if (GET_OBJECT_OWNER(object)==_object_is_garbage)
2260 		{
2261 			random_garbage_object_index= object_index;
2262 			garbage_objects_in_polygon+= 1;
2263 		}
2264 	}
2265 
2266 	if (garbage_objects_in_polygon> (graphics_preferences->double_corpse_limit ? MAXIMUM_GARBAGE_OBJECTS_PER_POLYGON * 2 : MAXIMUM_GARBAGE_OBJECTS_PER_POLYGON))
2267 	{
2268 		/* there are too many garbage objects in this polygon, remove the last (oldest?) one in
2269 			the linked list */
2270 		remove_map_object(random_garbage_object_index);
2271 	}
2272 	else
2273 	{
2274 		/* see if we have overflowed the maximum allowable garbage objects per map; if we have then
2275 			remove an existing piece of shit to make room for the new one (this sort of removal
2276 			could be really obvious... but who pays attention to dead bodies anyway?) */
2277 	  if (dynamic_world->garbage_object_count>= (graphics_preferences->double_corpse_limit? MAXIMUM_GARBAGE_OBJECTS_PER_MAP * 2 : MAXIMUM_GARBAGE_OBJECTS_PER_MAP))
2278 	    {
2279 			/* find a garbage object to remove, and do so (we�re certain that many exist) */
2280 			for (object_index= garbage_object_index, object= garbage_object;
2281 					SLOT_IS_FREE(object) || GET_OBJECT_OWNER(object)!=_object_is_garbage;
2282 					object_index= (object_index==MAXIMUM_OBJECTS_PER_MAP-1) ? 0 : (object_index+1), object= objects+object_index)
2283 				;
2284 			remove_map_object(object_index);
2285 		}
2286 		else
2287 		{
2288 			dynamic_world->garbage_object_count+= 1;
2289 		}
2290 	}
2291 
2292 	SET_OBJECT_OWNER(garbage_object, _object_is_garbage);
2293 }
2294 
2295 /* find an (x,y) and polygon_index for a random point on the given circle, at the same height
2296 	as the center point */
random_point_on_circle(world_point3d * center,short center_polygon_index,world_distance radius,world_point3d * random_point,short * random_polygon_index)2297 void random_point_on_circle(
2298 	world_point3d *center,
2299 	short center_polygon_index,
2300 	world_distance radius,
2301 	world_point3d *random_point,
2302 	short *random_polygon_index)
2303 {
2304 	world_distance adjusted_floor_height, adjusted_ceiling_height, supporting_polygon_index; /* not used */
2305 
2306 	*random_point= *center;
2307 	translate_point2d((world_point2d *)random_point, radius, global_random()&(NUMBER_OF_ANGLES-1));
2308 	keep_line_segment_out_of_walls(center_polygon_index, center, random_point, 0, WORLD_ONE/12,
2309 		&adjusted_floor_height, &adjusted_ceiling_height, &supporting_polygon_index);
2310 	*random_polygon_index= find_new_object_polygon((world_point2d *)center,
2311 		(world_point2d *)random_point, center_polygon_index);
2312 	if (*random_polygon_index!=NONE)
2313 	{
2314 		struct polygon_data *center_polygon= get_polygon_data(center_polygon_index);
2315 		struct polygon_data *random_polygon= get_polygon_data(*random_polygon_index);
2316 
2317 		if (center_polygon->floor_height!=random_polygon->floor_height) *random_polygon_index= NONE;
2318 	}
2319 }
2320 
2321 /* ---------- private code */
2322 
2323 /* returns the line_index of the line we intersected to leave this polygon, or NONE if destination
2324 	is in the given polygon */
_find_line_crossed_leaving_polygon(short polygon_index,world_point2d * p0,world_point2d * p1,bool * last_line)2325 short _find_line_crossed_leaving_polygon(
2326 	short polygon_index,
2327 	world_point2d *p0, /* origin (not necessairly in polygon_index) */
2328 	world_point2d *p1, /* destination (not necessairly in polygon_index) */
2329 	bool *last_line) /* set if p1 is on the line leaving the last polygon */
2330 {
2331 	struct polygon_data *polygon= get_polygon_data(polygon_index);
2332 	short intersected_line_index= NONE;
2333 	short i;
2334 
2335 	for (i= 0; i<polygon->vertex_count; ++i)
2336 	{
2337 		/* e1 is clockwise from e0 */
2338 		world_point2d *e0= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex;
2339 		world_point2d *e1= &get_endpoint_data(polygon->endpoint_indexes[i==polygon->vertex_count-1?0:i+1])->vertex;
2340 		int32 not_on_line;
2341 
2342 		/* if e0p1 cross e0e1 is negative, p1 is on the outside of edge e0e1 (a result of zero
2343 			means p1 is on the line e0e1) */
2344 		if ((not_on_line= (p1->x-e0->x)*(e1->y-e0->y) - (p1->y-e0->y)*(e1->x-e0->x)) >= 0)
2345 		{
2346 			/* if p0e1 cross p0p1 is positive, p0p1 crosses e0e1 to the left of e1 */
2347 			if ((e1->x-p0->x)*(p1->y-p0->y) - (e1->y-p0->y)*(p1->x-p0->x) <= 0)
2348 			{
2349 				/* if p0e0 cross p0p1 is negative or zero, p0p1 crosses e0e1 on or to the right of e0 */
2350 				if ((e0->x-p0->x)*(p1->y-p0->y) - (e0->y-p0->y)*(p1->x-p0->x) >= 0)
2351 				{
2352 					intersected_line_index= polygon->line_indexes[i];
2353 					*last_line= !not_on_line;
2354 					break;
2355 				}
2356 			}
2357 		}
2358 	}
2359 
2360 	return intersected_line_index;
2361 }
2362 
_new_map_object(shape_descriptor shape,angle facing)2363 static short _new_map_object(
2364 	shape_descriptor shape,
2365 	angle facing)
2366 {
2367 	struct object_data *object;
2368 	short object_index;
2369 
2370 	for (object_index=0,object=objects;object_index<MAXIMUM_OBJECTS_PER_MAP;++object_index,++object)
2371 	{
2372 		if (SLOT_IS_FREE(object))
2373 		{
2374 			/* initialize the object_data structure.  the defaults result in a normal (i.e., scenery),
2375 				non-solid object.  the rendered, animated and status flags are initially clear. */
2376 			object->polygon= NONE;
2377 			object->shape= shape;
2378 			object->facing= facing;
2379 			object->transfer_mode= NONE;
2380 			object->transfer_phase= 0;
2381 			object->permutation= 0;
2382 			object->sequence= 0;
2383 			object->flags= 0;
2384 			object->next_object= NONE;
2385 			object->parasitic_object= NONE;
2386 			object->sound_pitch= FIXED_ONE;
2387 
2388 			MARK_SLOT_AS_USED(object);
2389 
2390 			/* Objects with a shape of UNONE are invisible. */
2391 			if(shape==UNONE)
2392 			{
2393 				SET_OBJECT_INVISIBILITY(object, true);
2394 			}
2395 
2396 			break;
2397 		}
2398 	}
2399 	if (object_index==MAXIMUM_OBJECTS_PER_MAP) object_index= NONE;
2400 
2401 	return object_index;
2402 }
2403 
line_has_variable_height(short line_index)2404 bool line_has_variable_height(
2405 	short line_index)
2406 {
2407 	struct line_data *line= get_line_data(line_index);
2408 	struct polygon_data *polygon;
2409 
2410 	if(line->clockwise_polygon_owner != NONE)
2411 	{
2412 		if(line->counterclockwise_polygon_owner != NONE)
2413 		{
2414 			polygon= get_polygon_data(line->counterclockwise_polygon_owner);
2415 			if (polygon->type==_polygon_is_platform)
2416 			{
2417 				return true;
2418 			}
2419 		}
2420 
2421 		polygon= get_polygon_data(line->clockwise_polygon_owner);
2422 		if (polygon->type==_polygon_is_platform)
2423 		{
2424 			return true;
2425 		}
2426 	}
2427 
2428 	return false;
2429 }
2430 
2431 /* ---------- sound code */
2432 
play_object_sound(short object_index,short sound_code)2433 void play_object_sound(
2434 	short object_index,
2435 	short sound_code)
2436 {
2437 	struct object_data *object= get_object_data(object_index);
2438 	world_location3d *location= GET_OBJECT_OWNER(object)==_object_is_monster ?
2439 		(world_location3d *) &get_monster_data(object->permutation)->sound_location :
2440 		(world_location3d *) &object->location;
2441 
2442 	SoundManager::instance()->PlaySound(sound_code, location, object_index, object->sound_pitch);
2443 }
2444 
play_polygon_sound(short polygon_index,short sound_code)2445 void play_polygon_sound(
2446 	short polygon_index,
2447 	short sound_code)
2448 {
2449 	struct polygon_data *polygon= get_polygon_data(polygon_index);
2450 	world_location3d source;
2451 
2452 	find_center_of_polygon(polygon_index, (world_point2d *)&source.point);
2453 	source.point.z= polygon->floor_height;
2454 	source.polygon_index= polygon_index;
2455 
2456 	SoundManager::instance()->PlaySound(sound_code, &source, NONE);
2457 }
2458 
_play_side_sound(short side_index,short sound_code,_fixed pitch)2459 void _play_side_sound(
2460 	short side_index,
2461 	short sound_code,
2462 	_fixed pitch)
2463 {
2464 	struct side_data *side= get_side_data(side_index);
2465 	world_location3d source;
2466 
2467 	calculate_line_midpoint(side->line_index, &source.point);
2468 	source.polygon_index= side->polygon_index;
2469 
2470 	SoundManager::instance()->PlaySound(sound_code, &source, NONE, pitch);
2471 }
2472 
play_world_sound(short polygon_index,world_point3d * origin,short sound_code)2473 void play_world_sound(
2474 	short polygon_index,
2475 	world_point3d *origin,
2476 	short sound_code)
2477 {
2478 	world_location3d source;
2479 
2480 	source.point= *origin;
2481 	source.polygon_index= polygon_index;
2482 	SoundManager::instance()->PlaySound(sound_code, &source, NONE);
2483 }
2484 
_sound_listener_proc(void)2485 world_location3d *_sound_listener_proc(
2486 	void)
2487 {
2488 	return (world_location3d *) ((get_game_state()==_game_in_progress) ?
2489 		&current_player->camera_location :
2490 //		&get_object_data(get_monster_data(current_player->monster_index)->object_index)->location :
2491 		NULL);
2492 }
2493 
2494 // stuff floating on top of media is above it
_sound_obstructed_proc(world_location3d * source)2495 uint16 _sound_obstructed_proc(
2496 	world_location3d *source)
2497 {
2498 	world_location3d *listener= _sound_listener_proc();
2499 	uint16 flags= 0;
2500 
2501 	if (listener)
2502 	{
2503 		if (line_is_obstructed(source->polygon_index, (world_point2d *)&source->point,
2504 			listener->polygon_index, (world_point2d *)&listener->point))
2505 		{
2506 			flags|= _sound_was_obstructed;
2507 		}
2508 		else
2509 		{
2510 			struct polygon_data *source_polygon= get_polygon_data(source->polygon_index);
2511 			struct polygon_data *listener_polygon= get_polygon_data(listener->polygon_index);
2512 			bool source_under_media= false, listener_under_media= false;
2513 
2514 			// LP change: idiot-proofed the media handling
2515 			if (source_polygon->media_index!=NONE)
2516 			{
2517 				media_data *media = get_media_data(source_polygon->media_index);
2518 				if (media)
2519 				{
2520 					if (source->point.z<media->height)
2521 					{
2522 						source_under_media= true;
2523 					}
2524 				}
2525 			}
2526 
2527 			if (listener_polygon->media_index!=NONE)
2528 			{
2529 				media_data *media = get_media_data(listener_polygon->media_index);
2530 				if (media)
2531 				{
2532 					if (listener->point.z<media->height)
2533 					{
2534 						listener_under_media= true;
2535 					}
2536 				}
2537 			}
2538 
2539 			if (source_under_media)
2540 			{
2541 				if (!listener_under_media || source_polygon->media_index!=listener_polygon->media_index)
2542 				{
2543 					flags|= _sound_was_media_obstructed;
2544 				}
2545 				else
2546 				{
2547 					flags|= _sound_was_media_muffled;
2548 				}
2549 			}
2550 			else
2551 			{
2552 				if (listener_under_media)
2553 				{
2554 					flags|= _sound_was_media_obstructed;
2555 				}
2556 			}
2557 		}
2558 	}
2559 
2560 	return flags;
2561 }
2562 
2563 // for current player
_sound_add_ambient_sources_proc(void * data,add_ambient_sound_source_proc_ptr add_one_ambient_sound_source)2564 void _sound_add_ambient_sources_proc(
2565 	void *data,
2566 	add_ambient_sound_source_proc_ptr add_one_ambient_sound_source)
2567 {
2568 	struct world_location3d *listener= _sound_listener_proc();
2569 
2570 	if (listener)
2571 	{
2572 		struct polygon_data *listener_polygon= get_polygon_data(listener->polygon_index);
2573 		struct media_data *media= listener_polygon->media_index!=NONE ? get_media_data(listener_polygon->media_index) : (struct media_data *) NULL;
2574 		short *indexes= get_map_indexes(listener_polygon->sound_source_indexes, 0);
2575 		world_location3d source;
2576 		bool under_media= false;
2577 		short index;
2578 
2579 		// add ambient sound image
2580 		if (media && listener->point.z<media->height)
2581 		{
2582 			// if we�re under media don�t play the ambient sound image
2583 			add_one_ambient_sound_source((struct ambient_sound_data *)data, (world_location3d *) NULL, listener,
2584 				get_media_sound(listener_polygon->media_index, _media_snd_ambient_under), MAXIMUM_SOUND_VOLUME);
2585 			under_media= true;
2586 		}
2587 		else
2588 		{
2589 			// if we have an ambient sound image, play it
2590 			if (listener_polygon->ambient_sound_image_index!=NONE)
2591 			{
2592 				struct ambient_sound_image_data *image= get_ambient_sound_image_data(listener_polygon->ambient_sound_image_index);
2593 
2594 				// LP change: returning NULL means this is invalid; do some editing if necessary
2595 				if (image)
2596 					add_one_ambient_sound_source((struct ambient_sound_data *)data, (world_location3d *) NULL, listener, image->sound_index, image->volume);
2597 				else
2598 					listener_polygon->ambient_sound_image_index = NONE;
2599 			}
2600 
2601 			// if we�re over media, play that ambient sound image
2602 			if (media && (media->height>=listener_polygon->floor_height || !MEDIA_SOUND_OBSTRUCTED_BY_FLOOR(media)))
2603 			{
2604 				source= *listener, source.point.z= media->height;
2605 				add_one_ambient_sound_source((struct ambient_sound_data *)data, &source, listener,
2606 					get_media_sound(listener_polygon->media_index, _media_snd_ambient_over), MAXIMUM_SOUND_VOLUME);
2607 			}
2608 		}
2609 
2610 		// add ambient sound image from platform
2611 		if (listener_polygon->type==_polygon_is_platform)
2612 		{
2613 			struct platform_data *platform= get_platform_data(listener_polygon->permutation);
2614 
2615 			if (PLATFORM_IS_ACTIVE(platform) && PLATFORM_IS_MOVING(platform))
2616 			{
2617 				source= *listener, source.point.z= listener_polygon->floor_height;
2618 				add_one_ambient_sound_source((struct ambient_sound_data *)data, &source, listener,
2619 					get_platform_moving_sound(listener_polygon->permutation), MAXIMUM_SOUND_VOLUME);
2620 			}
2621 		}
2622 
2623 		// add ambient sound sources
2624 		// do only if indexes were found
2625 		if (indexes)
2626 		{
2627 		while ((index= *indexes++)!=NONE && index < MAXIMUM_SAVED_OBJECTS)
2628 		{
2629 			struct map_object *object= saved_objects + index; // gross, sorry
2630 			struct polygon_data *polygon= get_polygon_data(object->polygon_index);
2631 			struct media_data *media= polygon->media_index!=NONE ? get_media_data(polygon->media_index) : (struct media_data *) NULL;
2632 			short sound_type= object->index;
2633 			short sound_volume= object->facing;
2634 			bool active= true;
2635 
2636 			if (sound_volume<0)
2637 			{
2638 				sound_volume= get_light_intensity(-sound_volume)>>8;
2639 			}
2640 
2641 			// yaw, pitch are irrelevant
2642 			source.point= object->location;
2643 			source.polygon_index= object->polygon_index;
2644 			if (object->flags&_map_object_hanging_from_ceiling)
2645 			{
2646 				source.point.z+= polygon->ceiling_height;
2647 			}
2648 			else
2649 			{
2650 				if ((object->flags&_map_object_floats) && media)
2651 				{
2652 					source.point.z+= media->height;
2653 				}
2654 				else
2655 				{
2656 					source.point.z+= polygon->floor_height;
2657 				}
2658 			}
2659 
2660 			// adjust source if necessary (like, for a platform)
2661 			if (object->flags&_map_object_is_platform_sound)
2662 			{
2663 				if (polygon->type==_polygon_is_platform && PLATFORM_IS_MOVING(get_platform_data(polygon->permutation)))
2664 				{
2665 					sound_type= get_platform_moving_sound(polygon->permutation);
2666 					source.point.z= listener->point.z; // always on our level
2667 				}
2668 				else
2669 				{
2670 					active= false;
2671 				}
2672 			}
2673 
2674 			// .index is environmental sound type, .facing is volume
2675 			// CB: added check for media != NULL because it sometimes crashed here when being underwater
2676 			if (active && (!under_media || (media && source.point.z<media->height && polygon->media_index==listener_polygon->media_index)))
2677 			{
2678 				add_one_ambient_sound_source((struct ambient_sound_data *)data, &source, listener, sound_type, sound_volume);
2679 			}
2680 		}
2681 		}
2682 	}
2683 }
2684 
handle_random_sound_image(void)2685 void handle_random_sound_image(
2686 	void)
2687 {
2688 	struct polygon_data *polygon= get_polygon_data(current_player->camera_polygon_index);
2689 
2690 	if (polygon->random_sound_image_index!=NONE)
2691 	{
2692 		struct random_sound_image_data *image= get_random_sound_image_data(polygon->random_sound_image_index);
2693 
2694 		// LP change: returning NULL means this is invalid; do some editing if necessary
2695 		if (image)
2696 		{
2697 		// play a random sound
2698 		if (!image->phase)
2699 		{
2700 			short volume= image->volume;
2701 			angle direction= image->direction;
2702 			_fixed pitch= image->pitch;
2703 
2704 			if (image->delta_volume) volume+= local_random()%image->delta_volume;
2705 			if (image->delta_direction) direction= NORMALIZE_ANGLE(direction + local_random()%image->delta_direction);
2706 			if (image->delta_pitch) pitch+= local_random()%image->delta_pitch;
2707 
2708 			SoundManager::instance()->DirectPlaySound(SoundManager::instance()->RandomSoundIndexToSoundIndex(image->sound_index), (image->flags & _sound_image_is_non_directional) ? NONE : direction, volume, pitch);
2709 		}
2710 
2711 		// lower phase and reset if necessary
2712 		if ((image->phase-= 1)<0)
2713 		{
2714 			image->phase= image->period;
2715 			if (image->delta_period) image->phase+= local_random()%image->delta_period;
2716 		}
2717 		}
2718 		else
2719 			polygon->random_sound_image_index = NONE;
2720 	}
2721 }
2722 
2723 
2724 // XML elements for parsing the texture-loading specification
2725 // Uses an attribute for loading the landscapes
2726 // and a subelement for specifying which texture in an environment
2727 
2728 short **OriginalEnvironments = NULL;
2729 
reset_mml_texture_loading()2730 void reset_mml_texture_loading()
2731 {
2732 	LandscapesLoaded = true;
2733 	if (OriginalEnvironments) {
2734 		for (int i = 0; i < NUMBER_OF_ENVIRONMENTS; i++) {
2735 			for (int j = 0; j < NUMBER_OF_ENV_COLLECTIONS; j++)
2736 				Environments[i][j] = OriginalEnvironments[i][j];
2737 			free(OriginalEnvironments[i]);
2738 		}
2739 		free(OriginalEnvironments);
2740 		OriginalEnvironments = NULL;
2741 	}
2742 }
2743 
parse_mml_texture_loading(const InfoTree & root)2744 void parse_mml_texture_loading(const InfoTree& root)
2745 {
2746 	// back up old values first
2747 	if (!OriginalEnvironments) {
2748 		OriginalEnvironments = (short **) malloc(sizeof(short *) * NUMBER_OF_ENVIRONMENTS);
2749 		assert(OriginalEnvironments);
2750 		for (int i = 0; i < NUMBER_OF_ENVIRONMENTS; i++) {
2751 			OriginalEnvironments[i] = (short *) malloc(sizeof(short) * NUMBER_OF_ENV_COLLECTIONS);
2752 			assert(OriginalEnvironments[i]);
2753 			for (int j = 0; j < NUMBER_OF_ENV_COLLECTIONS; j++)
2754 				OriginalEnvironments[i][j] = Environments[i][j];
2755 		}
2756 	}
2757 
2758 	root.read_attr("landscapes", LandscapesLoaded);
2759 
2760 	BOOST_FOREACH(InfoTree env, root.children_named("texture_env"))
2761 	{
2762 		int16 index, which, coll;
2763 		if (env.read_indexed("index", index, NUMBER_OF_ENVIRONMENTS) &&
2764 			env.read_indexed("which", which, NUMBER_OF_ENV_COLLECTIONS) &&
2765 			env.read_indexed("coll", coll, MAXIMUM_COLLECTIONS, true))
2766 		{
2767 			Environments[index][which] = coll;
2768 		}
2769 	}
2770 }
2771