1 /*
2 PLATFORMS.C
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 Saturday, April 30, 1994 1:18:29 AM
22 
23 Friday, September 16, 1994 7:50:32 PM   (alain)
24 	fixed update_polygon_endpoint_data_for_height_change() so that it actually
25 	calculates highest_adjacent_floor and lowest_adjacent_ceiling correctly.
26 Saturday, September 17, 1994 6:04:11 PM   (alain)
27 	added _one_stop_platform which moves one level, then won't move until you get off and back on.
28 Saturday, October 29, 1994 2:42:22 AM (Jason)
29 	razed.
30 Saturday, November 5, 1994 2:53:39 PM (Jason)
31 	added _platform_cannot_be_externally_deactivated.
32 Sunday, November 6, 1994 8:31:29 PM  (Jason)
33 	added _platform_uses_native_polygon_heights.
34 Tuesday, November 15, 1994 11:36:37 PM  (Jason)
35 	fixed recursive activates/deactivates; added flooding.
36 Wednesday, May 3, 1995 4:37:18 PM  (Jason)
37 	updates endpoint transparency correctly.
38 Friday, June 9, 1995 11:43:11 AM  (Jason')
39 	keys.
40 Tuesday, July 11, 1995 11:32:46 AM  (Jason)
41 	media sounds.
42 
43 Feb. 4, 2000 (Loren Petrich):
44 	Changed halt() to assert(false) for better debugging
45 
46 Feb 25, 2000 (Loren Petrich):
47 	Suppressed consistency check for platform extrema in calculate_platform_extrema()
48 	as possibly unnecessary
49 
50 May 17, 2000 (Loren Petrich):
51 	Added XML support, including a damage parser
52 
53 Dec 19, 2000 (Loren Petrich):
54 	Suppressed assertion that a platform polygon must have at least one moving surface;
55 	this is for compatibility with some Pfhorte maps like "Descent". Also, added softer
56 	failure mode for get_platform_definition().
57 	Also suppressed an assertion that platform[polygon[platform]] = platform;
58 	currently handling failure in that by skipping over the platform.
59 
60 Jun 30, 2002 (tiennou):
61 	Added support for Pfhortran Procedure: platform_activated
62 */
63 
64 #include <string.h>
65 #include "cseries.h"
66 
67 #include "world.h"
68 #include "map.h"
69 #include "platforms.h"
70 #include "lightsource.h"
71 #include "SoundManager.h"
72 #include "player.h"
73 #include "media.h"
74 #include "InfoTree.h"
75 
76 // LP addition: XML parser for damage
77 #include "items.h"
78 #include "Packing.h"
79 
80 //MH: Lua scripting
81 #include "lua_script.h"
82 
83 #include <string.h>
84 
85 #include "editor.h" // MARATHON_ONE_DATA_VERSION
86 
87 /*
88 //opening sounds made by closed platforms are sometimes obscured
89 */
90 
91 /* ---------- constants */
92 
93 /* ---------- structures */
94 
95 /* ---------- globals */
96 
97 #include "platform_definitions.h"
98 
99 /* ---------- private prototypes */
100 
101 static short polygon_index_to_platform_index(short polygon_index);
102 
103 bool set_platform_state(short platform_index, bool state, short parent_platform_index);
104 static void set_adjacent_platform_states(short platform_index, bool state);
105 
106 static void take_out_the_garbage(short platform_index);
107 static void adjust_platform_sides(short platform_index, world_distance old_ceiling_height, world_distance new_ceiling_height);
108 static void calculate_platform_extrema(short platform_index, world_distance lowest_level,
109 	world_distance highest_level);
110 
111 static void play_platform_sound(short platform_index, short sound_code);
112 
113 static platform_definition *get_platform_definition(const short type);
114 
115 /* ---------- code */
116 
get_platform_data(short platform_index)117 platform_data *get_platform_data(
118 	short platform_index)
119 {
120 	struct platform_data *platform = GetMemberWithBounds(platforms,platform_index,dynamic_world->platform_count);
121 
122 	vassert(platform, csprintf(temporary, "platform index #%d is out of range", platform_index));
123 
124 	return platform;
125 }
126 
get_platform_definition(const short type)127 platform_definition *get_platform_definition(const short type)
128 {
129 	return GetMemberWithBounds(platform_definitions,type,NUMBER_OF_PLATFORM_TYPES);
130 }
131 
132 
new_platform(struct static_platform_data * data,short polygon_index,short version)133 short new_platform(
134 	struct static_platform_data *data,
135 	short polygon_index,
136 	short version)
137 {
138 	short platform_index= NONE;
139 	struct platform_data *platform;
140 
141 	assert(NUMBER_OF_DYNAMIC_PLATFORM_FLAGS<=16);
142 	assert(NUMBER_OF_STATIC_PLATFORM_FLAGS<=32);
143 	// LP: OK for a platform to be a do-nothing platform
144 	// assert(data->static_flags&(FLAG(_platform_comes_from_floor)|FLAG(_platform_comes_from_ceiling)));
145 
146 	if (dynamic_world->platform_count<int(MAXIMUM_PLATFORMS_PER_MAP))
147 	{
148 		struct polygon_data *polygon= get_polygon_data(polygon_index);
149 		short i;
150 
151 		platform_index= dynamic_world->platform_count++;
152 		platform= platforms+platform_index;
153 
154 		/* remember the platform_index in the polygon�s .permutation field */
155 		polygon->permutation= platform_index;
156 		polygon->type= _polygon_is_platform;
157 
158 		/* initialize the platform */
159 		platform->type= data->type;
160 		platform->static_flags= data->static_flags;
161 		platform->tag= data->tag;
162 		platform->speed= data->speed;
163 		platform->delay= data->delay;
164 		platform->polygon_index= polygon_index;
165 		platform->parent_platform_index= NONE;
166 		calculate_platform_extrema(platform_index, data->minimum_height, data->maximum_height);
167 
168 		if (version == MARATHON_ONE_DATA_VERSION)
169 		{
170 			switch (platform->type)
171 			{
172 			case 0: // marathon door
173 			case 3: // pfhor door
174 				SET_PLATFORM_IS_DOOR(platform, true);
175 				break;
176 			}
177 
178 			if (PLATFORM_IS_LOCKED(platform))
179 			{
180 				SET_PLATFORM_IS_LOCKED(platform, false);
181 				SET_PLATFORM_FLOODS_M1(platform, true);
182 			}
183 		}
184 
185 #if 0
186 		switch (platform->type)
187 		{
188 			case _platform_is_spht_door:
189 			case _platform_is_spht_split_door:
190 			case _platform_is_locked_spht_door:
191 			case _platform_is_pfhor_door:
192 				SET_PLATFORM_IS_DOOR(platform, true);
193 				break;
194 		}
195 #endif
196 
197 		/* stuff in the correct defaults; if the platform is initially active it begins moving
198 			immediately */
199 		platform->dynamic_flags= 0;
200 		platform->floor_height= polygon->floor_height;
201 		platform->ceiling_height= polygon->ceiling_height;
202 		if (PLATFORM_IS_INITIALLY_ACTIVE(platform))
203 		{
204 			SET_PLATFORM_IS_ACTIVE(platform, true);
205 			SET_PLATFORM_HAS_BEEN_ACTIVATED(platform);
206 			SET_PLATFORM_IS_MOVING(platform, true);
207 		}
208 		if (PLATFORM_IS_INITIALLY_EXTENDED(platform))
209 		{
210 			if (PLATFORM_COMES_FROM_FLOOR(platform)) platform->floor_height= platform->maximum_floor_height;
211 			if (PLATFORM_COMES_FROM_CEILING(platform)) platform->ceiling_height= platform->minimum_ceiling_height;
212 			SET_PLATFORM_IS_CONTRACTING(platform);
213 			SET_PLATFORM_IS_FULLY_EXTENDED(platform);
214 		}
215 		else
216 		{
217 			if (PLATFORM_COMES_FROM_FLOOR(platform)) platform->floor_height= platform->minimum_floor_height;
218 			if (PLATFORM_COMES_FROM_CEILING(platform)) platform->ceiling_height= platform->maximum_ceiling_height;
219 			SET_PLATFORM_IS_EXTENDING(platform);
220 			SET_PLATFORM_IS_FULLY_CONTRACTED(platform);
221 		}
222 
223 		/* remember what polygons and lines are adjacent to the endpoints of the platform
224 			polygon so we can quickly recalculate heights later */
225 		for (i= 0; i<polygon->vertex_count; ++i)
226 		{
227 			calculate_endpoint_polygon_owners(polygon->endpoint_indexes[i], &platform->endpoint_owners[i].first_polygon_index,
228 				&platform->endpoint_owners[i].polygon_index_count);
229 			calculate_endpoint_line_owners(polygon->endpoint_indexes[i], &platform->endpoint_owners[i].first_line_index,
230 				&platform->endpoint_owners[i].line_index_count);
231 		}
232 
233 		polygon->floor_height= platform->floor_height;
234 		polygon->ceiling_height= platform->ceiling_height;
235 		adjust_platform_endpoint_and_line_heights(platform_index);
236 		adjust_platform_for_media(platform_index, true);
237 	}
238 
239 	return platform_index;
240 }
241 
get_defaults_for_platform_type(short type)242 struct static_platform_data *get_defaults_for_platform_type(
243 	short type)
244 {
245 	struct platform_definition *definition= get_platform_definition(type);
246 	// Fallback for out-of-range type
247 	if (!definition) definition = get_platform_definition(0);
248 
249 	return &definition->defaults;
250 }
251 
update_platforms(void)252 void update_platforms(
253 	void)
254 {
255 	short platform_index;
256 	struct platform_data *platform;
257 
258 	for (platform_index= 0, platform= platforms; platform_index<dynamic_world->platform_count; ++platform_index, ++platform)
259 	{
260 		CLEAR_PLATFORM_WAS_JUST_ACTIVATED_OR_DEACTIVATED(platform);
261 
262 		if (PLATFORM_IS_ACTIVE(platform))
263 		{
264 			struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
265 			short sound_code= NONE;
266 			bool was_flooded = PLATFORM_IS_FLOODED(platform);
267 
268 			// Should there be some warning message about platform-polygon inconsistences?
269 			// assert(polygon->permutation==platform_index);
270 			if (!(polygon->permutation==platform_index)) continue;
271 
272 			if (!PLATFORM_IS_MOVING(platform))
273 			{
274 				/* waiting to move */
275 				if ((platform->ticks_until_restart-= 1)<=0)
276 				{
277 					SET_PLATFORM_IS_MOVING(platform, true);
278 					sound_code= _starting_sound;
279 				}
280 			}
281 
282 			if (PLATFORM_IS_MOVING(platform))
283 			{
284 				struct platform_definition *definition= get_platform_definition(platform->type);
285 				if (!definition) continue;
286 				world_distance new_floor_height= platform->floor_height, new_ceiling_height= platform->ceiling_height;
287 				world_distance delta_height= PLATFORM_IS_EXTENDING(platform) ? platform->speed :
288 					(PLATFORM_CONTRACTS_SLOWER(platform) ? (-(platform->speed>>2)) : -platform->speed);
289 
290 				/* adjust and pin heights: if we think we�re fully contracted or expanded, make
291 					sure our heights reflect that (we don�t want a split platform to have blank
292 					space between it because it didn�t quite close all the way) */
293 				CLEAR_PLATFORM_POSITIONING_FLAGS(platform);
294 				if (PLATFORM_COMES_FROM_FLOOR(platform))
295 				{
296 					new_floor_height+= delta_height;
297 					if (new_floor_height>=platform->maximum_floor_height)
298 						SET_PLATFORM_IS_FULLY_EXTENDED(platform);
299 					if (new_floor_height<=platform->minimum_floor_height)
300 						SET_PLATFORM_IS_FULLY_CONTRACTED(platform);
301 				}
302 				if (PLATFORM_COMES_FROM_CEILING(platform))
303 				{
304 					new_ceiling_height-= delta_height;
305 					if (new_ceiling_height>=platform->maximum_ceiling_height)
306 						SET_PLATFORM_IS_FULLY_CONTRACTED(platform);
307 					if (new_ceiling_height<=platform->minimum_ceiling_height)
308 						SET_PLATFORM_IS_FULLY_EXTENDED(platform);
309 				}
310 				if (PLATFORM_IS_FULLY_EXTENDED(platform))
311 				{
312 					if (PLATFORM_COMES_FROM_FLOOR(platform)) new_floor_height= platform->maximum_floor_height;
313 					if (PLATFORM_COMES_FROM_CEILING(platform)) new_ceiling_height= platform->minimum_ceiling_height;
314 				}
315 				if (PLATFORM_IS_FULLY_CONTRACTED(platform))
316 				{
317 					if (PLATFORM_COMES_FROM_FLOOR(platform)) new_floor_height= platform->minimum_floor_height;
318 					if (PLATFORM_COMES_FROM_CEILING(platform)) new_ceiling_height= platform->maximum_ceiling_height;
319 				}
320 
321 				/* calculate new ceiling and floor heights for the platform polygon and see if
322 					the change is obstructed */
323 				if (change_polygon_height(platform->polygon_index, new_floor_height, new_ceiling_height,
324 					PLATFORM_CAUSES_DAMAGE(platform) ? &definition->damage : (struct damage_definition *) NULL))
325 				{
326 					/* if we weren�t blocked, remember that we moved last time, change our current
327 						level, adjust the textures if we�re coming down from the ceiling,
328 						and finally adjust the heights of all endpoints and lines which make
329 						up our polygon to reflect the height change */
330 					if (PLATFORM_COMES_FROM_CEILING(platform))
331 						adjust_platform_sides(platform_index, platform->ceiling_height, new_ceiling_height);
332 					platform->ceiling_height= new_ceiling_height, platform->floor_height= new_floor_height;
333 					SET_PLATFORM_WAS_MOVING(platform);
334 					adjust_platform_endpoint_and_line_heights(platform_index);
335 					adjust_platform_for_media(platform_index, false);
336 				}
337 				else
338 				{
339 					/* if we were blocked, play a sound if we weren�t blocked last time and reverse
340 						directions if we�re supposed to */
341 					if (PLATFORM_WAS_MOVING(platform)) sound_code= _obstructed_sound;
342 					if (PLATFORM_REVERSES_DIRECTION_WHEN_OBSTRUCTED(platform))
343 					{
344 						PLATFORM_IS_EXTENDING(platform) ?
345 							SET_PLATFORM_IS_CONTRACTING(platform) :
346 							SET_PLATFORM_IS_EXTENDING(platform);
347 					}
348 					else
349 					{
350 						SET_PLATFORM_WAS_BLOCKED(platform);
351 					}
352 				}
353 
354 				if (PLATFORM_IS_FULLY_EXTENDED(platform) || PLATFORM_IS_FULLY_CONTRACTED(platform))
355 				{
356 					bool deactivate= false;
357 
358 					SET_PLATFORM_IS_MOVING(platform, false);
359 					platform->ticks_until_restart= platform->delay;
360 					sound_code= _stopping_sound;
361 
362 					/* handle changing directions at extremes and deactivating if necessary */
363 					if (PLATFORM_IS_FULLY_CONTRACTED(platform))
364 					{
365 						if (PLATFORM_IS_INITIALLY_CONTRACTED(platform) && PLATFORM_DEACTIVATES_AT_INITIAL_LEVEL(platform))
366 							deactivate= true;
367 						SET_PLATFORM_IS_EXTENDING(platform);
368 					}
369 					else
370 					{
371 						if (PLATFORM_IS_FULLY_EXTENDED(platform))
372 						{
373 							if (platform->floor_height==platform->ceiling_height)
374 								take_out_the_garbage(platform_index);
375 							if (PLATFORM_IS_INITIALLY_EXTENDED(platform)
376 									&& PLATFORM_DEACTIVATES_AT_INITIAL_LEVEL(platform))
377 								deactivate= true;
378 							SET_PLATFORM_IS_CONTRACTING(platform);
379 						}
380 						else
381 						{
382 							assert(false);
383 						}
384 					}
385 					if (PLATFORM_DEACTIVATES_AT_EACH_LEVEL(platform)) deactivate= true;
386 
387 					if (PLATFORM_ACTIVATES_ADJACENT_PLATFORMS_AT_EACH_LEVEL(platform)) set_adjacent_platform_states(platform_index, true);
388 					if (deactivate) set_platform_state(platform_index, false, NONE);
389 				}
390 			}
391 
392 			if (sound_code!=NONE) play_platform_sound(platform_index, sound_code);
393 
394 			if (was_flooded != PLATFORM_IS_FLOODED(platform))
395 			{
396 				// flood status changed - update side lights
397 				// FIXME: this assumes Marathon 1 map lighting
398 				for (int i = 0; i < polygon->vertex_count; i++)
399 				{
400 					short side_index = polygon->side_indexes[i];
401 					if (side_index == NONE) continue;
402 					guess_side_lightsource_indexes(side_index);
403 				}
404 			}
405 		}
406 	}
407 }
408 
platform_is_on(short platform_index)409 bool platform_is_on(
410 	short platform_index)
411 {
412 	struct platform_data *platform;
413 
414 	platform= get_platform_data(platform_index);
415 
416 	return PLATFORM_IS_ACTIVE(platform) ? true : false;
417 }
418 
monster_can_enter_platform(short platform_index,short source_polygon_index,world_distance height,world_distance minimum_ledge_delta,world_distance maximum_ledge_delta)419 short monster_can_enter_platform(
420 	short platform_index,
421 	short source_polygon_index,
422 	world_distance height,
423 	world_distance minimum_ledge_delta,
424 	world_distance maximum_ledge_delta)
425 {
426 	struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
427 	struct platform_data *platform= get_platform_data(platform_index);
428 	struct polygon_data *destination_polygon= get_polygon_data(platform->polygon_index);
429 	world_distance destination_floor_height= destination_polygon->floor_height;
430 	world_distance destination_ceiling_height= destination_polygon->ceiling_height;
431 	world_distance delta_height;
432 	short result_code= _platform_is_accessable;
433 
434 	if (PLATFORM_IS_DOOR(platform))
435 	{
436 		if (PLATFORM_IS_MONSTER_CONTROLLABLE(platform) && platform->delay>=_short_delay_platform)
437 		{
438 			destination_floor_height= platform->minimum_floor_height;
439 			destination_ceiling_height= platform->maximum_ceiling_height;
440 
441 			result_code= PLATFORM_IS_FULLY_CONTRACTED(platform) ? _platform_is_accessable : _platform_will_be_accessable;
442 		}
443 	}
444 	else
445 	{
446 		if (PLATFORM_IS_ACTIVE(platform) && PLATFORM_COMES_FROM_FLOOR(platform) && !PLATFORM_COMES_FROM_CEILING(platform))
447 		{
448 			/* if this platform doesn�t go floor to ceiling and it stops at the source polygon, it might be ok */
449 			if (platform->maximum_floor_height!=platform->minimum_ceiling_height &&
450 				(platform->minimum_floor_height==source_polygon->floor_height ||
451 				platform->maximum_floor_height==source_polygon->floor_height))
452 			{
453 				if (platform->minimum_floor_height==source_polygon->floor_height)
454 				{
455 					destination_floor_height= platform->minimum_floor_height;
456 					destination_ceiling_height= platform->maximum_ceiling_height;
457 				}
458 				else
459 				{
460 					destination_floor_height= platform->maximum_floor_height;
461 					destination_ceiling_height= platform->minimum_ceiling_height;
462 				}
463 				result_code= (platform->floor_height==source_polygon->floor_height) ? _platform_is_accessable : _platform_will_be_accessable;
464 			}
465 		}
466 	}
467 
468 	delta_height= destination_floor_height-source_polygon->floor_height;
469 	if (delta_height<minimum_ledge_delta || delta_height>maximum_ledge_delta ||
470 		MIN(destination_ceiling_height, source_polygon->ceiling_height) - MAX(destination_floor_height, source_polygon->floor_height)<height)
471 	{
472 		result_code= _platform_will_never_be_accessable;
473 	}
474 
475 	return result_code;
476 }
477 
monster_can_leave_platform(short platform_index,short destination_polygon_index,world_distance height,world_distance minimum_ledge_delta,world_distance maximum_ledge_delta)478 short monster_can_leave_platform(
479 	short platform_index,
480 	short destination_polygon_index,
481 	world_distance height,
482 	world_distance minimum_ledge_delta,
483 	world_distance maximum_ledge_delta) /* negative */
484 {
485 	struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
486 	struct platform_data *platform= get_platform_data(platform_index);
487 	struct polygon_data *source_polygon= get_polygon_data(platform->polygon_index);
488 	world_distance source_floor_height= source_polygon->floor_height;
489 	world_distance source_ceiling_height= source_polygon->ceiling_height;
490 	world_distance delta_height;
491 	short result_code= _exit_is_accessable;
492 
493 	if (PLATFORM_IS_DOOR(platform))
494 	{
495 		source_floor_height= platform->minimum_floor_height;
496 		source_ceiling_height= platform->maximum_ceiling_height;
497 	}
498 	else
499 	{
500 		if (PLATFORM_IS_ACTIVE(platform) && PLATFORM_COMES_FROM_FLOOR(platform) && !PLATFORM_COMES_FROM_CEILING(platform))
501 		{
502 			if (platform->minimum_floor_height==destination_polygon->floor_height ||
503 				platform->maximum_floor_height==destination_polygon->floor_height)
504 			{
505 				source_floor_height= destination_polygon->floor_height;
506 				result_code= (platform->floor_height==destination_polygon->floor_height) ? _exit_is_accessable : _exit_will_be_accessable;
507 			}
508 		}
509 	}
510 
511 	delta_height= destination_polygon->floor_height-source_floor_height;
512 	if (delta_height<minimum_ledge_delta || delta_height>maximum_ledge_delta ||
513 		MIN(destination_polygon->ceiling_height, source_ceiling_height) - MAX(destination_polygon->floor_height, source_floor_height)<height)
514 	{
515 		result_code= _exit_will_never_be_accessable;
516 	}
517 
518 	return result_code;
519 }
520 
player_touch_platform_state(short player_index,short platform_index)521 void player_touch_platform_state(
522 	short player_index,
523 	short platform_index)
524 {
525 	struct platform_data *platform= get_platform_data(platform_index);
526 	struct platform_definition *definition= get_platform_definition(platform->type);
527 	if (!definition) return;
528 	short sound_code= NONE;
529 
530 	/* if we can�t control this platform, play the uncontrollable sound, if it�s inactive activate
531 		it and if it�s active and moving reverse it�s direction if that�s what it does when it�s
532 		obstructed, if it�s active but not moving then zero the delay */
533 	if (PLATFORM_IS_PLAYER_CONTROLLABLE(platform))
534 	{
535 		if (PLATFORM_IS_ACTIVE(platform))
536 		{
537 			if (PLATFORM_CANNOT_BE_EXTERNALLY_DEACTIVATED(platform))
538 			{
539 				sound_code= _uncontrollable_sound;
540 			}
541 			else
542 			{
543 				if (PLATFORM_IS_MOVING(platform))
544 				{
545 					if (PLATFORM_REVERSES_DIRECTION_WHEN_OBSTRUCTED(platform))
546 					{
547 						PLATFORM_IS_EXTENDING(platform) ?
548 							SET_PLATFORM_IS_CONTRACTING(platform) :
549 							SET_PLATFORM_IS_EXTENDING(platform);
550 						sound_code= _starting_sound;
551 					}
552 					else
553 					{
554 						sound_code= _uncontrollable_sound;
555 					}
556 				}
557 				else
558 				{
559 					platform->ticks_until_restart= 0;
560 				}
561 			}
562 		}
563 		else
564 		{
565 			if (definition->key_item_index==NONE || try_and_subtract_player_item(player_index, definition->key_item_index))
566 			{
567 				set_platform_state(platform_index, true, NONE);
568 			}
569 			else
570 			{
571 				// no key
572 				sound_code= _uncontrollable_sound;
573 			}
574 		}
575 	}
576 	else
577 	{
578 		sound_code= _uncontrollable_sound;
579 	}
580 
581 	if (sound_code!=NONE) play_platform_sound(platform_index, sound_code);
582 }
583 
584 
platform_was_entered(short platform_index,bool player)585 void platform_was_entered(
586 	short platform_index,
587 	bool player)
588 {
589 	struct platform_data *platform= get_platform_data(platform_index);
590 
591 	if (!PLATFORM_IS_DOOR(platform))
592 	{
593 		if ((player && PLATFORM_IS_PLAYER_CONTROLLABLE(platform)) ||
594 			(!player && PLATFORM_IS_MONSTER_CONTROLLABLE(platform)))
595 		{
596 			try_and_change_platform_state(platform_index, true);
597 		}
598 	}
599 }
600 
platform_is_legal_player_target(short platform_index)601 bool platform_is_legal_player_target(
602 	short platform_index)
603 {
604 	struct platform_data *platform= get_platform_data(platform_index);
605 	struct platform_definition *definition= get_platform_definition(platform->type);
606 	if (!definition) return false;
607 	bool legal_player_target= false;
608 
609 	if (PLATFORM_IS_DOOR(platform))
610 	{
611 		if ((PLATFORM_IS_PLAYER_CONTROLLABLE(platform) || definition->uncontrollable_sound!=NONE) &&
612 			(!PLATFORM_ACTIVATES_ONLY_ONCE(platform) || !PLATFORM_HAS_BEEN_ACTIVATED(platform)))
613 		{
614 			legal_player_target= true;
615 		}
616 	}
617 
618 	return legal_player_target;
619 }
620 
platform_is_at_initial_state(short platform_index)621 bool platform_is_at_initial_state(
622 	short platform_index)
623 {
624 	struct platform_data *platform= get_platform_data(platform_index);
625 
626 	return (PLATFORM_HAS_BEEN_ACTIVATED(platform) && (!PLATFORM_IS_ACTIVE(platform) || PLATFORM_CANNOT_BE_EXTERNALLY_DEACTIVATED(platform))) ? false : true;
627 }
628 
try_and_change_platform_state(short platform_index,bool state)629 bool try_and_change_platform_state(
630 	short platform_index,
631 	bool state)
632 {
633 	struct platform_data *platform= get_platform_data(platform_index);
634 	bool changed= false;
635 
636 	if (state || !PLATFORM_IS_ACTIVE(platform) || !PLATFORM_CANNOT_BE_EXTERNALLY_DEACTIVATED(platform))
637 	{
638 		bool new_state= set_platform_state(platform_index, state, NONE);
639 
640 		changed= (new_state && state) || (!new_state && !state);
641 	}
642 
643 	return changed;
644 }
645 
try_and_change_tagged_platform_states(short tag,bool state)646 bool try_and_change_tagged_platform_states(
647 	short tag,
648 	bool state)
649 {
650 	struct platform_data *platform;
651 	bool changed= false;
652 	short platform_index;
653 
654 	if (tag)
655 	{
656 		for (platform_index= 0, platform= platforms; platform_index<dynamic_world->platform_count; ++platform_index, ++platform)
657 		{
658 			if (platform->tag==tag)
659 			{
660 				if (try_and_change_platform_state(platform_index, state))
661 				{
662 					changed= true;
663 				}
664 			}
665 		}
666 	}
667 
668 	return changed;
669 }
670 
get_platform_moving_sound(short platform_index)671 short get_platform_moving_sound(
672 	short platform_index)
673 {
674 	struct platform_data *platform= get_platform_data(platform_index);
675 	struct platform_definition *definition= get_platform_definition(platform->type);
676 	if (!definition) return NONE;
677 
678 	return definition->moving_sound;
679 }
680 
681 /* ---------- private code */
682 
683 
polygon_index_to_platform_index(short polygon_index)684 static short polygon_index_to_platform_index(
685 	short polygon_index)
686 {
687 	short platform_index;
688 	struct platform_data *platform;
689 
690 	for (platform_index= 0, platform= platforms; platform_index<dynamic_world->platform_count; ++platform_index, ++platform)
691 	{
692 		if (platform->polygon_index==polygon_index) break;
693 	}
694 	if (platform_index==dynamic_world->platform_count) platform_index= NONE;
695 
696 	return platform_index;
697 }
698 
set_platform_state(short platform_index,bool state,short parent_platform_index)699 bool set_platform_state(
700 	short platform_index,
701 	bool state,
702 	short parent_platform_index)
703 {
704 	struct platform_data *platform= get_platform_data(platform_index);
705 	bool new_state= PLATFORM_IS_ACTIVE(platform) ? true : false;
706 	short sound_code= NONE;
707 
708 	if (!PLATFORM_WAS_JUST_ACTIVATED_OR_DEACTIVATED(platform))
709 	{
710 		if (!state || !PLATFORM_ACTIVATES_ONLY_ONCE(platform) || !PLATFORM_HAS_BEEN_ACTIVATED(platform))
711 		{
712 			if ((state && !PLATFORM_IS_ACTIVE(platform)) || (!state && PLATFORM_IS_ACTIVE(platform)))
713 			{
714 				struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
715 
716 				/* the state of this platform cannot be changed again this tick */
717 				SET_PLATFORM_WAS_JUST_ACTIVATED_OR_DEACTIVATED(platform);
718 
719 				if (state)
720 				{
721 					SET_PLATFORM_HAS_BEEN_ACTIVATED(platform);
722 					SET_PLATFORM_IS_MOVING(platform, false);
723 					platform->ticks_until_restart= PLATFORM_DELAYS_BEFORE_ACTIVATION(platform) ?
724 						platform->delay : 0;
725 
726 					if (PLATFORM_ACTIVATES_LIGHT(platform))
727 					{
728 						set_light_status(polygon->floor_lightsource_index, true);
729 						set_light_status(polygon->ceiling_lightsource_index, true);
730 					}
731 
732 					platform->parent_platform_index= parent_platform_index;
733 
734 					if (PLATFORM_ACTIVATES_ADJACENT_PLATFORMS_WHEN_ACTIVATING(platform)) set_adjacent_platform_states(platform_index, true);
735 					if (PLATFORM_DEACTIVATES_ADJACENT_PLATFORMS_WHEN_ACTIVATING(platform)) set_adjacent_platform_states(platform_index, false);
736 				}
737 				else
738 				{
739 					if (PLATFORM_DEACTIVATES_LIGHT(platform))
740 					{
741 						set_light_status(polygon->floor_lightsource_index, false);
742 						set_light_status(polygon->ceiling_lightsource_index, false);
743 					}
744 
745 					if (PLATFORM_ACTIVATES_ADJACENT_PLATFORMS_WHEN_DEACTIVATING(platform)) set_adjacent_platform_states(platform_index, true);
746 					if (PLATFORM_DEACTIVATES_ADJACENT_PLATFORMS_WHEN_DEACTIVATING(platform)) set_adjacent_platform_states(platform_index, false);
747 
748 					if (PLATFORM_IS_MOVING(platform)) sound_code= _obstructed_sound;
749 				}
750 
751 				/* assume the correct state, and correctly update all switches referencing this platform */
752 				SET_PLATFORM_IS_ACTIVE(platform, state);
753                                 //MH: Lua script hook
754                                 L_Call_Platform_Activated(platform->polygon_index);
755 				assume_correct_switch_position(_panel_is_platform_switch, platform->polygon_index, state);
756 
757 				new_state= state;
758 			}
759 		}
760 
761 		if (sound_code!=NONE) play_platform_sound(platform_index, sound_code);
762 	}
763 
764 	return new_state;
765 }
766 
set_adjacent_platform_states(short platform_index,bool state)767 static void set_adjacent_platform_states(
768 	short platform_index,
769 	bool state)
770 {
771 	struct platform_data *platform= get_platform_data(platform_index);
772 	struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
773 	short i;
774 
775 	for (i= 0; i<polygon->vertex_count; ++i)
776 	{
777 		short adjacent_polygon_index= polygon->adjacent_polygon_indexes[i];
778 		short adjacent_platform_index= polygon_index_to_platform_index(adjacent_polygon_index);
779 
780 		if (adjacent_platform_index!=NONE)
781 		{
782 			struct polygon_data *adjacent_polygon= get_polygon_data(adjacent_polygon_index);
783 
784 			if (!PLATFORM_DOES_NOT_ACTIVATE_PARENT(platform) || platform->parent_platform_index!=adjacent_platform_index)
785 			{
786 				set_platform_state(adjacent_polygon->permutation, state, platform_index);
787 			}
788 		}
789 	}
790 }
791 
792 /* remove all garbage objects in the platform */
take_out_the_garbage(short platform_index)793 static void take_out_the_garbage(
794 	short platform_index)
795 {
796 	struct platform_data *platform= get_platform_data(platform_index);
797 	struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
798 	short object_index= polygon->first_object;
799 
800 	while (object_index!=NONE)
801 	{
802 		struct object_data *object= get_object_data(object_index);
803 
804 		if (GET_OBJECT_OWNER(object)==_object_is_garbage) remove_map_object(object_index);
805 		object_index= object->next_object; /* relies on remove_map_object() not changing this */
806 	}
807 }
808 
adjust_platform_for_media(short platform_index,bool initialize)809 void adjust_platform_for_media(
810 	short platform_index,
811 	bool initialize)
812 {
813 	struct platform_data *platform= get_platform_data(platform_index);
814 	struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
815 
816 	if (polygon->media_index!=NONE)
817 	{
818 		// LP change: idiot-proofing
819 		struct media_data *media= get_media_data(polygon->media_index);
820 		if (media)
821 		{
822 		bool floor_below_media= platform->floor_height<media->height;
823 		bool ceiling_below_media= platform->ceiling_height<media->height;
824 
825 		if (!initialize)
826 		{
827 			short sound_code= NONE;
828 
829 			if ((PLATFORM_FLOOR_BELOW_MEDIA(platform) && !floor_below_media) ||
830 				(PLATFORM_CEILING_BELOW_MEDIA(platform) && !ceiling_below_media))
831 			{
832 				sound_code= _media_snd_platform_leaving;
833 			}
834 			if ((!PLATFORM_FLOOR_BELOW_MEDIA(platform) && floor_below_media) ||
835 				(!PLATFORM_CEILING_BELOW_MEDIA(platform) && ceiling_below_media))
836 			{
837 				sound_code= _media_snd_platform_entering;
838 			}
839 
840 			if (sound_code!=NONE)
841 			{
842 				play_polygon_sound(platform->polygon_index, get_media_sound(polygon->media_index, sound_code));
843 			}
844 		}
845 
846 		SET_PLATFORM_FLOOR_BELOW_MEDIA(platform, floor_below_media);
847 		SET_PLATFORM_CEILING_BELOW_MEDIA(platform, ceiling_below_media);
848 		}
849 	}
850 }
851 
adjust_platform_endpoint_and_line_heights(short platform_index)852 void adjust_platform_endpoint_and_line_heights(
853 	short platform_index)
854 {
855 	struct platform_data *platform= get_platform_data(platform_index);
856 	struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
857 	short i;
858 
859 	for (i= 0; i<polygon->vertex_count; ++i)
860 	{
861 		struct endpoint_data *endpoint= get_endpoint_data(polygon->endpoint_indexes[i]);
862 		struct line_data *line= get_line_data(polygon->line_indexes[i]);
863 		short polygon_count= platform->endpoint_owners[i].polygon_index_count;
864 		short *polygon_indexes= get_map_indexes(platform->endpoint_owners[i].first_polygon_index, polygon_count);
865 		short line_count= platform->endpoint_owners[i].line_index_count;
866 		short *line_indexes= get_map_indexes(platform->endpoint_owners[i].first_line_index, line_count);
867 		short lowest_adjacent_ceiling= 0, highest_adjacent_floor= 0, supporting_polygon_index = NONE;
868 		struct polygon_data *adjacent_polygon;
869 		short j;
870 
871 		/* adjust line heights and set proper line transparency and solidity */
872 		// Skip this step if line indexes were not found
873 		if (polygon->adjacent_polygon_indexes[i]!=NONE && line_indexes)
874 		{
875 			adjacent_polygon= get_polygon_data(polygon->adjacent_polygon_indexes[i]);
876 			line->highest_adjacent_floor= MAX(polygon->floor_height, adjacent_polygon->floor_height);
877 			line->lowest_adjacent_ceiling= MIN(polygon->ceiling_height, adjacent_polygon->ceiling_height);
878 
879 			/* only worry about transparency and solidity if there�s a polygon on the other side */
880 			if (LINE_IS_VARIABLE_ELEVATION(line))
881 			{
882 				SET_LINE_TRANSPARENCY(line, line->highest_adjacent_floor<line->lowest_adjacent_ceiling);
883 				SET_LINE_SOLIDITY(line, line->highest_adjacent_floor>=line->lowest_adjacent_ceiling);
884 			}
885 
886 			/* and only if there is another polygon does this endpoint have a chance of being transparent */
887 			for (j= 0; j<line_count; ++j) if (LINE_IS_SOLID(get_line_data(line_indexes[j]))) break;
888 			SET_ENDPOINT_SOLIDITY(endpoint, (j!=line_count));
889 
890 			/* and only if there is another polygon does this endpoint have a chance of being transparent */
891 			for (j= 0; j<line_count; ++j) if (!LINE_IS_TRANSPARENT(get_line_data(line_indexes[j]))) break;
892 			SET_ENDPOINT_TRANSPARENCY(endpoint, (j==line_count));
893 		}
894 		else
895 		{
896 			line->highest_adjacent_floor= polygon->floor_height;
897 			line->lowest_adjacent_ceiling= polygon->ceiling_height;
898 		}
899 
900 		/* adjust endpoint heights */
901 		// Skip this step if no polygon indexes were found
902 		if (polygon_indexes)
903 		{
904 			for (j= 0; j<polygon_count; ++j)
905 			{
906 				adjacent_polygon= get_polygon_data(polygon_indexes[j]);
907 				if (!j || highest_adjacent_floor<adjacent_polygon->floor_height) highest_adjacent_floor= adjacent_polygon->floor_height, supporting_polygon_index= polygon_indexes[j];
908 				if (!j || lowest_adjacent_ceiling>adjacent_polygon->ceiling_height) lowest_adjacent_ceiling= adjacent_polygon->ceiling_height;
909 			}
910 		}
911 		endpoint->highest_adjacent_floor_height= highest_adjacent_floor;
912 		endpoint->lowest_adjacent_ceiling_height= lowest_adjacent_ceiling;
913 		endpoint->supporting_polygon_index= supporting_polygon_index;
914 	}
915 }
916 
play_platform_sound(short platform_index,short type)917 static void play_platform_sound(
918 	short platform_index,
919 	short type)
920 {
921 	struct platform_data *platform= get_platform_data(platform_index);
922 	struct platform_definition *definition= get_platform_definition(platform->type);
923 	if (!definition) return;
924 	short sound_code;
925 
926 	switch (type)
927 	{
928 		case _obstructed_sound:
929 			sound_code= definition->obstructed_sound;
930 			break;
931 
932 		case _uncontrollable_sound:
933 			sound_code= definition->uncontrollable_sound;
934 			break;
935 
936 		case _starting_sound:
937 			sound_code= PLATFORM_IS_EXTENDING(platform) ? definition->starting_extension : definition->starting_contraction;
938 			break;
939 		case _stopping_sound:
940 			sound_code= PLATFORM_IS_FULLY_CONTRACTED(platform) ? definition->stopping_contraction : definition->stopping_extension;
941 			break;
942 
943 		default:
944 			assert(false);
945 			break;
946 	}
947 
948 	play_polygon_sound(platform->polygon_index, sound_code);
949 	SoundManager::instance()->CauseAmbientSoundSourceUpdate();
950 }
951 
952 /* rules for using native polygon heights: a) if this is a floor platform, then take the polygon�s
953 	native floor height to be the maximum height if it is greater than the minimum height, otherwise
954 	use it as the minimum height; b) if this is a ceiling platform, then take the polygon�s native
955 	ceiling height to be the minimum height if it is less than the maximum height, otherwise use it
956 	as the maximum height; c) native polygon height is not used for floor/ceiling platforms */
calculate_platform_extrema(short platform_index,world_distance lowest_level,world_distance highest_level)957 static void calculate_platform_extrema(
958 	short platform_index,
959 	world_distance lowest_level,
960 	world_distance highest_level)
961 {
962 	short i;
963 	struct platform_data *platform= get_platform_data(platform_index);
964 	struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
965 	world_distance lowest_adjacent_floor, highest_adjacent_ceiling;
966 	world_distance highest_adjacent_floor, lowest_adjacent_ceiling;
967 
968 	// LP change: no need for this test
969 	// assert(lowest_level==NONE||highest_level==NONE||lowest_level<highest_level);
970 
971 	/* calculate lowest and highest adjacent floors and ceilings */
972 	lowest_adjacent_floor= highest_adjacent_floor= polygon->floor_height;
973 	lowest_adjacent_ceiling= highest_adjacent_ceiling= polygon->ceiling_height;
974 	for (i= 0; i<polygon->vertex_count; ++i)
975 	{
976 		if (polygon->adjacent_polygon_indexes[i]!=NONE)
977 		{
978 			struct polygon_data *adjacent_polygon= get_polygon_data(polygon->adjacent_polygon_indexes[i]);
979 
980 			if (adjacent_polygon->floor_height<lowest_adjacent_floor) lowest_adjacent_floor= adjacent_polygon->floor_height;
981 			if (adjacent_polygon->floor_height>highest_adjacent_floor) highest_adjacent_floor= adjacent_polygon->floor_height;
982 			if (adjacent_polygon->ceiling_height<lowest_adjacent_ceiling) lowest_adjacent_ceiling= adjacent_polygon->ceiling_height;
983 			if (adjacent_polygon->ceiling_height>highest_adjacent_ceiling) highest_adjacent_ceiling= adjacent_polygon->ceiling_height;
984 		}
985 	}
986 
987 	/* take into account the EXTENDS_FLOOR_TO_CEILING flag */
988 	if (PLATFORM_EXTENDS_FLOOR_TO_CEILING(platform))
989 	{
990 		if (polygon->ceiling_height>highest_adjacent_floor) highest_adjacent_floor= polygon->ceiling_height;
991 		if (polygon->floor_height<lowest_adjacent_ceiling) lowest_adjacent_ceiling= polygon->floor_height;
992 	}
993 
994 	/* calculate floor and ceiling min, max values as appropriate for the platform direction */
995 	if (PLATFORM_GOES_BOTH_WAYS(platform))
996 	{
997 		/* split platforms always meet in the center */
998 		platform->minimum_floor_height= lowest_level==NONE ? lowest_adjacent_floor : lowest_level;
999 		platform->maximum_ceiling_height= highest_level==NONE ? highest_adjacent_ceiling : highest_level;
1000 		platform->maximum_floor_height= platform->minimum_ceiling_height=
1001 			(platform->minimum_floor_height+platform->maximum_ceiling_height)/2;
1002 	}
1003 	else
1004 	{
1005 		if (PLATFORM_COMES_FROM_FLOOR(platform))
1006 		{
1007 			if (PLATFORM_USES_NATIVE_POLYGON_HEIGHTS(platform))
1008 			{
1009 				if (polygon->floor_height<lowest_adjacent_floor || PLATFORM_EXTENDS_FLOOR_TO_CEILING(platform))
1010 				{
1011 					lowest_adjacent_floor= polygon->floor_height;
1012 				}
1013 				else
1014 				{
1015 					highest_adjacent_floor= polygon->floor_height;
1016 				}
1017 			}
1018 
1019 			platform->minimum_floor_height= lowest_level==NONE ? lowest_adjacent_floor : lowest_level;
1020 			platform->maximum_floor_height= highest_level==NONE ? highest_adjacent_floor : highest_level;
1021 			platform->minimum_ceiling_height= platform->maximum_ceiling_height= polygon->ceiling_height;
1022 		}
1023 		else if (PLATFORM_COMES_FROM_CEILING(platform))
1024 		{
1025 
1026 			if (PLATFORM_USES_NATIVE_POLYGON_HEIGHTS(platform))
1027 			{
1028 				if (polygon->ceiling_height>highest_adjacent_ceiling || PLATFORM_EXTENDS_FLOOR_TO_CEILING(platform))
1029 				{
1030 					highest_adjacent_ceiling= polygon->ceiling_height;
1031 				}
1032 				else
1033 				{
1034 					lowest_adjacent_ceiling= polygon->ceiling_height;
1035 				}
1036 			}
1037 
1038 			platform->minimum_ceiling_height= lowest_level==NONE ? lowest_adjacent_ceiling : lowest_level;
1039 			platform->maximum_ceiling_height= highest_level==NONE ? highest_adjacent_ceiling : highest_level;
1040 			platform->minimum_floor_height= platform->maximum_floor_height= polygon->floor_height;
1041 		}
1042 	}
1043 }
1044 
adjust_platform_sides(short platform_index,world_distance old_ceiling_height,world_distance new_ceiling_height)1045 static void adjust_platform_sides(
1046 	short platform_index,
1047 	world_distance old_ceiling_height,
1048 	world_distance new_ceiling_height)
1049 {
1050 	struct platform_data *platform= get_platform_data(platform_index);
1051 	struct polygon_data *polygon= get_polygon_data(platform->polygon_index);
1052 	world_distance delta_height= new_ceiling_height-old_ceiling_height;
1053 	short i;
1054 
1055 	for (i= 0; i<polygon->vertex_count; ++i)
1056 	{
1057 		short side_index;
1058 		struct side_data *side;
1059 		struct line_data *line= get_line_data(polygon->line_indexes[i]);
1060 		short adjacent_polygon_index= polygon->adjacent_polygon_indexes[i];
1061 
1062 		/* adjust the platform side (i.e., the texture on the side of the platform) */
1063 		if (adjacent_polygon_index!=NONE)
1064 		{
1065 			side_index= adjacent_polygon_index==line->clockwise_polygon_owner ? line->clockwise_polygon_side_index : line->counterclockwise_polygon_side_index;
1066 			if (side_index!=NONE)
1067 			{
1068 				side= get_side_data(side_index);
1069 				switch (side->type)
1070 				{
1071 					case _full_side:
1072 					case _high_side:
1073 					case _split_side:
1074 						side->primary_texture.y0+= delta_height;
1075 						break;
1076 				}
1077 			}
1078 		}
1079 
1080 		/* adjust the shaft side (i.e., the texture the platform slides against) */
1081 		side_index= polygon->side_indexes[i];
1082 		if (side_index!=NONE)
1083 		{
1084 			world_distance top_of_side_height;
1085 
1086 			side= get_side_data(side_index);
1087 			switch (side->type)
1088 			{
1089 				case _split_side: /* secondary */
1090 					top_of_side_height= MIN(line->highest_adjacent_floor,  polygon->ceiling_height);
1091 					side->primary_texture.y0-= (old_ceiling_height<top_of_side_height && new_ceiling_height<top_of_side_height) ?
1092 						delta_height : new_ceiling_height-top_of_side_height;
1093 					break;
1094 				case _high_side: /* primary */
1095 //					top_of_side_height= polygon->ceiling_height;
1096 					side->primary_texture.y0-= delta_height; //(old_ceiling_height<top_of_side_height && new_ceiling_height<top_of_side_height) ?
1097 //						delta_height : new_ceiling_height-top_of_side_height;
1098 					break;
1099 				case _full_side: /* primary */
1100 					side->primary_texture.y0-= delta_height;
1101 					break;
1102 				case _low_side: /* primary */
1103 					// ghs: the following doesn't appear to be necessary at all!
1104 /*
1105 					top_of_side_height= MIN(line->highest_adjacent_floor, polygon->ceiling_height);
1106 					side->primary_texture.y0-= (old_ceiling_height<top_of_side_height && new_ceiling_height<top_of_side_height) ?
1107 						delta_height : new_ceiling_height-top_of_side_height;
1108 */
1109 					break;
1110 
1111 				default:
1112 					vhalt(csprintf(temporary, "wasn't expecting side #%d to have type #%d", side_index, side->type));
1113 					break;
1114 			}
1115 		}
1116 	}
1117 }
1118 
unpack_static_platform_data(uint8 * Stream,static_platform_data * Objects,size_t Count)1119 uint8 *unpack_static_platform_data(uint8 *Stream, static_platform_data* Objects, size_t Count)
1120 {
1121 	uint8* S = Stream;
1122 	static_platform_data* ObjPtr = Objects;
1123 
1124 	for (size_t k = 0; k < Count; k++, ObjPtr++)
1125 	{
1126 		StreamToValue(S,ObjPtr->type);
1127 		StreamToValue(S,ObjPtr->speed);
1128 		StreamToValue(S,ObjPtr->delay);
1129 		StreamToValue(S,ObjPtr->maximum_height);
1130 		StreamToValue(S,ObjPtr->minimum_height);
1131 
1132 		StreamToValue(S,ObjPtr->static_flags);
1133 
1134 		StreamToValue(S,ObjPtr->polygon_index);
1135 
1136 		StreamToValue(S,ObjPtr->tag);
1137 
1138 		S += 7*2;
1139 	}
1140 
1141 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_static_platform_data));
1142 	return S;
1143 }
1144 
pack_static_platform_data(uint8 * Stream,static_platform_data * Objects,size_t Count)1145 uint8 * pack_static_platform_data(uint8 *Stream, static_platform_data* Objects, size_t Count)
1146 {
1147 	uint8* S = Stream;
1148 	static_platform_data* ObjPtr = Objects;
1149 
1150 	for (size_t k = 0; k < Count; k++, ObjPtr++)
1151 	{
1152 		ValueToStream(S,ObjPtr->type);
1153 		ValueToStream(S,ObjPtr->speed);
1154 		ValueToStream(S,ObjPtr->delay);
1155 		ValueToStream(S,ObjPtr->maximum_height);
1156 		ValueToStream(S,ObjPtr->minimum_height);
1157 
1158 		ValueToStream(S,ObjPtr->static_flags);
1159 
1160 		ValueToStream(S,ObjPtr->polygon_index);
1161 
1162 		ValueToStream(S,ObjPtr->tag);
1163 
1164 		S += 7*2;
1165 	}
1166 
1167 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_static_platform_data));
1168 	return S;
1169 }
1170 
1171 
StreamToEndpointOwner(uint8 * & S,endpoint_owner_data & Object)1172 inline void StreamToEndpointOwner(uint8* &S, endpoint_owner_data& Object)
1173 {
1174 	StreamToValue(S,Object.first_polygon_index);
1175 	StreamToValue(S,Object.polygon_index_count);
1176 	StreamToValue(S,Object.first_line_index);
1177 	StreamToValue(S,Object.line_index_count);
1178 }
1179 
EndpointOwnerToStream(uint8 * & S,endpoint_owner_data & Object)1180 inline void EndpointOwnerToStream(uint8* &S, endpoint_owner_data& Object)
1181 {
1182 	ValueToStream(S,Object.first_polygon_index);
1183 	ValueToStream(S,Object.polygon_index_count);
1184 	ValueToStream(S,Object.first_line_index);
1185 	ValueToStream(S,Object.line_index_count);
1186 }
1187 
1188 
unpack_platform_data(uint8 * Stream,platform_data * Objects,size_t Count)1189 uint8 *unpack_platform_data(uint8 *Stream, platform_data* Objects, size_t Count)
1190 {
1191 	uint8* S = Stream;
1192 	platform_data* ObjPtr = Objects;
1193 
1194 	for (size_t k = 0; k < Count; k++, ObjPtr++)
1195 	{
1196 		StreamToValue(S,ObjPtr->type);
1197 		StreamToValue(S,ObjPtr->static_flags);
1198 		StreamToValue(S,ObjPtr->speed);
1199 		StreamToValue(S,ObjPtr->delay);
1200 		StreamToValue(S,ObjPtr->minimum_floor_height);
1201 		StreamToValue(S,ObjPtr->maximum_floor_height);
1202 		StreamToValue(S,ObjPtr->minimum_ceiling_height);
1203 		StreamToValue(S,ObjPtr->maximum_ceiling_height);
1204 
1205 		StreamToValue(S,ObjPtr->polygon_index);
1206 		StreamToValue(S,ObjPtr->dynamic_flags);
1207 		StreamToValue(S,ObjPtr->floor_height);
1208 		StreamToValue(S,ObjPtr->ceiling_height);
1209 		StreamToValue(S,ObjPtr->ticks_until_restart);
1210 
1211 		for (int k=0; k<MAXIMUM_VERTICES_PER_POLYGON; k++)
1212 			StreamToEndpointOwner(S,ObjPtr->endpoint_owners[k]);
1213 
1214 		StreamToValue(S,ObjPtr->parent_platform_index);
1215 
1216 		StreamToValue(S,ObjPtr->tag);
1217 
1218 		S += 22*2;
1219 	}
1220 
1221 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_platform_data));
1222 	return S;
1223 }
1224 
pack_platform_data(uint8 * Stream,platform_data * Objects,size_t Count)1225 uint8 *pack_platform_data(uint8 *Stream, platform_data* Objects, size_t Count)
1226 {
1227 	uint8* S = Stream;
1228 	platform_data* ObjPtr = Objects;
1229 
1230 	for (size_t k = 0; k < Count; k++, ObjPtr++)
1231 	{
1232 		ValueToStream(S,ObjPtr->type);
1233 		ValueToStream(S,ObjPtr->static_flags);
1234 		ValueToStream(S,ObjPtr->speed);
1235 		ValueToStream(S,ObjPtr->delay);
1236 		ValueToStream(S,ObjPtr->minimum_floor_height);
1237 		ValueToStream(S,ObjPtr->maximum_floor_height);
1238 		ValueToStream(S,ObjPtr->minimum_ceiling_height);
1239 		ValueToStream(S,ObjPtr->maximum_ceiling_height);
1240 
1241 		ValueToStream(S,ObjPtr->polygon_index);
1242 		ValueToStream(S,ObjPtr->dynamic_flags);
1243 		ValueToStream(S,ObjPtr->floor_height);
1244 		ValueToStream(S,ObjPtr->ceiling_height);
1245 		ValueToStream(S,ObjPtr->ticks_until_restart);
1246 
1247 		for (int k=0; k<MAXIMUM_VERTICES_PER_POLYGON; k++)
1248 			EndpointOwnerToStream(S,ObjPtr->endpoint_owners[k]);
1249 
1250 		ValueToStream(S,ObjPtr->parent_platform_index);
1251 
1252 		ValueToStream(S,ObjPtr->tag);
1253 
1254 		S += 22*2;
1255 	}
1256 
1257 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_platform_data));
1258 	return S;
1259 }
1260 
1261 struct platform_definition *original_platform_definitions = NULL;
1262 
reset_mml_platforms()1263 void reset_mml_platforms()
1264 {
1265 	if (original_platform_definitions) {
1266 		for (int i = 0; i < NUMBER_OF_PLATFORM_TYPES; i++)
1267 			platform_definitions[i] = original_platform_definitions[i];
1268 		free(original_platform_definitions);
1269 		original_platform_definitions = NULL;
1270 	}
1271 }
1272 
parse_mml_platforms(const InfoTree & root)1273 void parse_mml_platforms(const InfoTree& root)
1274 {
1275 	// back up old values first
1276 	if (!original_platform_definitions) {
1277 		original_platform_definitions = (struct platform_definition *) malloc(sizeof(struct platform_definition) * NUMBER_OF_PLATFORM_TYPES);
1278 		assert(original_platform_definitions);
1279 		for (int i = 0; i < NUMBER_OF_PLATFORM_TYPES; i++)
1280 			original_platform_definitions[i] = platform_definitions[i];
1281 	}
1282 
1283 	BOOST_FOREACH(InfoTree ptree, root.children_named("platform"))
1284 	{
1285 		int16 index;
1286 		if (!ptree.read_indexed("index", index, NUMBER_OF_PLATFORM_TYPES))
1287 			continue;
1288 		platform_definition& def = platform_definitions[index];
1289 
1290 		ptree.read_indexed("start_extend", def.starting_extension, SHRT_MAX+1, true);
1291 		ptree.read_indexed("start_contract", def.starting_contraction, SHRT_MAX+1, true);
1292 		ptree.read_indexed("stop_extend", def.stopping_extension, SHRT_MAX+1, true);
1293 		ptree.read_indexed("stop_contract", def.stopping_contraction, SHRT_MAX+1, true);
1294 		ptree.read_indexed("obstructed", def.obstructed_sound, SHRT_MAX+1, true);
1295 		ptree.read_indexed("uncontrollable", def.uncontrollable_sound, SHRT_MAX+1, true);
1296 		ptree.read_indexed("moving", def.moving_sound, SHRT_MAX+1, true);
1297 		ptree.read_indexed("item", def.key_item_index, NUMBER_OF_DEFINED_ITEMS, true);
1298 
1299 		BOOST_FOREACH(InfoTree dmg, ptree.children_named("damage"))
1300 		{
1301 			dmg.read_damage(def.damage);
1302 		}
1303 	}
1304 }
1305