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 ¤t_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