1 /*
2 ITEMS.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 Monday, January 3, 1994 10:06:08 PM
22 
23 Monday, September 5, 1994 2:17:43 PM
24 	razed.
25 Friday, October 21, 1994 3:44:11 PM
26 	changed inventory updating mechanism, added maximum counts of items.
27 Wednesday, November 2, 1994 3:49:57 PM (Jason)
28 	object_was_just_destroyed is now called immediately on powerups.
29 Tuesday, January 31, 1995 1:24:10 PM  (Jason')
30 	can only hold unlimited ammo on total carnage (not everything)
31 Wednesday, October 11, 1995 3:10:34 PM  (Jason)
32 	network-only items
33 
34 Feb 4, 2000 (Loren Petrich):
35 	Changed halt() to assert(false) for better debugging
36 
37 Feb 15, 2000 (Loren Petrich):
38 	Added item-animation handling
39 	Non-animated items ought to be randomized, but one problem is that
40 	randomize_object_sequence() only works when the shapes are loaded,
41 	and the shapes are usually not loaded when the map items are created.
42 
43 May 16, 2000 (Loren Petrich):
44 	Added XML support for configuring various item features
45 
46 May 26, 2000 (Loren Petrich):
47 	Added XML shapes support
48 
49 Jul 1, 2000 (Loren Petrich):
50 	Did some inlining of the item-definition accessor
51 
52 	Added Benad's netgame-type changes
53 
54 Aug 10, 2000 (Loren Petrich):
55 	Added Chris Pruett's Pfhortran changes
56 
57 Feb 11, 2001 (Loren Petrich):
58 	Reversed the "polarity" of the "facing" member of "object",
59 	which is used as a flag in the case of randomized unanimated objects.
60 	It will become NONE when these objects are inited.
61 */
62 
63 #include "cseries.h"
64 
65 #include "map.h"
66 #include "interface.h"
67 #include "monsters.h"
68 #include "player.h"
69 #include "SoundManager.h"
70 #include "platforms.h"
71 #include "fades.h"
72 #include "FilmProfile.h"
73 #include "items.h"
74 #include "flood_map.h"
75 #include "effects.h"
76 #include "game_window.h"
77 #include "weapons.h" /* needed for process_new_item_for_reloading */
78 #include "network_games.h"
79 #include "InfoTree.h"
80 
81 // LP addition: for the XML stuff
82 #include <string.h>
83 #include <limits.h>
84 
85 //MH: Lua scripting
86 #include "lua_script.h"
87 
88 /* ---------- structures */
89 
90 #define strITEM_NAME_LIST 150
91 #define strHEADER_NAME_LIST 151
92 
93 #define MAXIMUM_ARM_REACH (3*WORLD_ONE_FOURTH)
94 
95 /* ---------- private prototypes */
96 
97 /* ---------- globals */
98 
99 #include "item_definitions.h"
100 
101 /* ---------- private prototypes */
102 
103 // Item-definition accessor
104 static item_definition *get_item_definition(
105 	const short type);
106 
107 static bool get_item(short player_index, short object_index);
108 
109 static bool test_item_retrieval(short polygon_index1, world_point3d *location1, world_point3d *location2);
110 
111 static int32 item_trigger_cost_function(short source_polygon_index, short line_index,
112 	short destination_polygon_index, void *unused);
113 
114 /* ---------- code */
115 
116 // Item-definition accessor
get_item_definition(const short type)117 item_definition *get_item_definition(
118 	const short type)
119 {
120 	return GetMemberWithBounds(item_definitions,type,NUMBER_OF_DEFINED_ITEMS);
121 }
122 
123 //a non-inlined version for external use
get_item_definition_external(const short type)124 item_definition *get_item_definition_external(
125 	const short type)
126 {
127 	return get_item_definition(type);
128 }
129 
new_item(struct object_location * location,short type)130 short new_item(
131 	struct object_location *location,
132 	short type)
133 {
134 	short object_index;
135 	struct item_definition *definition= get_item_definition(type);
136 	// LP change: added idiot-proofing
137 	if (!definition) return false;
138 
139 	bool add_item= true;
140 
141 	assert(sizeof(item_definitions)/sizeof(struct item_definition)==NUMBER_OF_DEFINED_ITEMS);
142 
143 	/* Do NOT add items that are network-only in a single player game, and vice-versa */
144 	if (dynamic_world->player_count>1)
145 	{
146 		if (definition->invalid_environments & _environment_network) add_item= false;
147 		if (get_item_kind(type)==_ball && !current_game_has_balls()) add_item= false;
148 	}
149 	else
150 	{
151 		if (definition->invalid_environments & _environment_single_player) add_item= false;
152 	}
153 
154 	if (add_item)
155 	{
156 		/* add the object to the map */
157 		object_index= new_map_object(location, definition->base_shape);
158 		if (object_index!=NONE)
159 		{
160 			struct object_data *object= get_object_data(object_index);
161 
162 			// LP addition: using the facing direction as a flag in the "unanimated" case:
163 			// will be initially zero, but will become nonzero when initialized,
164 			// so that the shape randomization will be done only once.
165 
166 			SET_OBJECT_OWNER(object, _object_is_item);
167 			object->permutation= type;
168 
169 			if ((location->flags&_map_object_is_network_only) && dynamic_world->player_count<=1)
170 			{
171 //				dprintf("killed #%d;g;", type);
172 				SET_OBJECT_INVISIBILITY(object, true);
173 				object->permutation= NONE;
174 			}
175 			else if ((get_item_kind(type) == _ball) && !static_world->ball_in_play)
176 			{
177 				static_world->ball_in_play = true;
178 				SoundManager::instance()->PlayLocalSound(_snd_got_ball);
179 			}
180 
181 			/* let PLACEMENT.C keep track of how many there are */
182 			object_was_just_added(_object_is_item, type);
183 			// and let Lua know too
184 			L_Call_Item_Created(object_index);
185  		}
186 	}
187 	else
188 	{
189 		object_index= NONE;
190 	}
191 
192 	return object_index;
193 }
194 
trigger_nearby_items(short polygon_index)195 void trigger_nearby_items(
196 	short polygon_index)
197 {
198 	polygon_index= flood_map(polygon_index, INT32_MAX, item_trigger_cost_function, _breadth_first, (void *) NULL);
199 	while (polygon_index!=NONE)
200 	{
201 		struct object_data *object;
202 		short object_index;
203 
204 		for (object_index= get_polygon_data(polygon_index)->first_object; object_index!=NONE; object_index= object->next_object)
205 		{
206 			object= get_object_data(object_index);
207 			switch (GET_OBJECT_OWNER(object))
208 			{
209 				case _object_is_item:
210 					if (OBJECT_IS_INVISIBLE(object) && object->permutation!=NONE)
211 					{
212 						teleport_object_in(object_index);
213 					}
214 					break;
215 			}
216 		}
217 
218 		polygon_index= flood_map(NONE, INT32_MAX, item_trigger_cost_function, _breadth_first, (void *) NULL);
219 	}
220 }
221 
222 /* returns the color of the ball or NONE if they don't have one */
find_player_ball_color(short player_index)223 short find_player_ball_color(
224 	short player_index)
225 {
226 	struct player_data *player= get_player_data(player_index);
227 	short ball_color= NONE;
228 	short index;
229 
230 	for(index= BALL_ITEM_BASE; ball_color==NONE && index<BALL_ITEM_BASE+MAXIMUM_NUMBER_OF_PLAYERS; ++index)
231 	{
232 		if(player->items[index]>0)
233 		{
234 			ball_color= index-BALL_ITEM_BASE;
235 		}
236 	}
237 
238 	return ball_color;
239 }
240 
get_item_name(char * buffer,short item_id,bool plural)241 void get_item_name(
242 	char *buffer,
243 	short item_id,
244 	bool plural)
245 {
246 	struct item_definition *definition= get_item_definition(item_id);
247 	// LP change: added idiot-proofing
248 	if (!definition)
249 	{
250 		if (plural)
251 			sprintf(buffer,"Unlisted items with ID %d",item_id);
252 		else
253 			sprintf(buffer,"Unlisted item with ID %d",item_id);
254 
255 		return;
256 	}
257 
258 	getcstr(buffer, strITEM_NAME_LIST, plural ? definition->plural_name_id :
259 		definition->singular_name_id);
260 }
261 
get_header_name(char * buffer,short type)262 void get_header_name(
263 	char *buffer,
264 	short type)
265 {
266 	getcstr(buffer, strHEADER_NAME_LIST, type);
267 }
268 
calculate_player_item_array(short player_index,short type,short * items,short * counts,short * array_count)269 void calculate_player_item_array(
270 	short player_index,
271 	short type,
272 	short *items,
273 	short *counts,
274 	short *array_count)
275 {
276 	struct player_data *player= get_player_data(player_index);
277 	short loop;
278 	short count= 0;
279 
280 	for(loop=0; loop<NUMBER_OF_DEFINED_ITEMS; ++loop)
281 	{
282 		if (loop==_i_knife) continue;
283 	 	if(player->items[loop] != NONE)
284 		{
285 			if(get_item_kind(loop)==type)
286 			{
287 				items[count]= loop;
288 				counts[count]= player->items[loop];
289 				count++;
290 			}
291 		}
292 	}
293 
294 	*array_count= count;
295 }
296 
count_inventory_lines(short player_index)297 short count_inventory_lines(
298 	short player_index)
299 {
300 	struct player_data *player= get_player_data(player_index);
301 	bool types[NUMBER_OF_ITEM_TYPES];
302 	short count= 0;
303 	short loop;
304 
305 	/* Clean out the header array, so we can count properly */
306 	for(loop=0; loop<NUMBER_OF_ITEM_TYPES; ++loop)
307 	{
308 		types[loop]= false;
309 	}
310 
311 	for(loop=0; loop<NUMBER_OF_DEFINED_ITEMS; ++loop)
312 	{
313 		if (loop==_i_knife) continue;
314 		if (player->items[loop] != NONE)
315 		{
316 			count++;
317 			types[get_item_kind(loop)]= true;
318 		}
319 	}
320 
321 	/* Now add in the header lines.. */
322 	for(loop= 0; loop<NUMBER_OF_ITEM_TYPES; ++loop)
323 	{
324 		if(types[loop]) count++;
325 	}
326 
327 	return count;
328 }
329 
a1_swipe_nearby_items(short player_index)330 static void a1_swipe_nearby_items(
331 	short player_index)
332 {
333 	struct object_data *object;
334 	struct object_data *player_object;
335 	struct player_data *player= get_player_data(player_index);
336 	short next_object;
337 	struct polygon_data *polygon;
338 	short *neighbor_indexes;
339 	short i;
340 
341 	player_object= get_object_data(get_monster_data(player->monster_index)->object_index);
342 
343 	polygon= get_polygon_data(player_object->polygon);
344 	neighbor_indexes= get_map_indexes(polygon->first_neighbor_index, polygon->neighbor_count);
345 
346 	// Skip this step if neighbor indexes were not found
347 	if (!neighbor_indexes) return;
348 
349 	for (i=0;i<polygon->neighbor_count;++i)
350 	{
351 
352 		struct polygon_data *neighboring_polygon= get_polygon_data(*neighbor_indexes++);
353 
354 		/*
355 			LP change: since precalculate_map_indexes() and its associated routine
356 			intersecting_flood_proc() appear to have some bugs in them, I will
357 			instead search the neighbors of each indexed polygon.
358 
359 			Starting the search from -1 is a kludge designed to include a search
360 			for the current polygon.
361 		*/
362 		struct polygon_data *source_polygon = neighboring_polygon;
363 		for (int ngbr_indx = -1; ngbr_indx<source_polygon->vertex_count; ngbr_indx++)
364 		{
365 		if (ngbr_indx >= 0)
366 		{
367 			// Be sure to check on whether there is a valid polygon on the other side
368 			short adjacent_index = source_polygon->adjacent_polygon_indexes[ngbr_indx];
369 			if (adjacent_index == NONE) continue;
370 			neighboring_polygon = get_polygon_data(adjacent_index);
371 		}
372 		else
373 			neighboring_polygon = source_polygon;
374 
375 		if (!POLYGON_IS_DETACHED(neighboring_polygon))
376 		{
377 			next_object= neighboring_polygon->first_object;
378 
379 			while(next_object != NONE)
380 			{
381 				object= get_object_data(next_object);
382 				if (GET_OBJECT_OWNER(object)==_object_is_item && !OBJECT_IS_INVISIBLE(object))
383 				{
384 					if (guess_distance2d((world_point2d *) &player->location, (world_point2d *) &object->location)<=MAXIMUM_ARM_REACH)
385 					{
386 						world_distance radius, height;
387 
388 						get_monster_dimensions(player->monster_index, &radius, &height);
389 
390 						if (object->location.z >= player->location.z - MAXIMUM_ARM_REACH && object->location.z <= player->location.z + height &&
391 							test_item_retrieval(player_object->polygon, &player_object->location, &object->location))
392 						{
393 							if(get_item(player_index, next_object))
394 							{
395 								/* Start the search again.. */
396 								next_object= neighboring_polygon->first_object;
397 								continue;
398 							}
399 						}
400 					}
401 				}
402 
403 				next_object= object->next_object;
404 			}
405 		}
406 		// LP addition: end of that kludgy search loop
407 		}
408 	}
409 }
410 
m2_swipe_nearby_items(short player_index)411 static void m2_swipe_nearby_items(
412 	short player_index)
413 {
414 	struct object_data *object;
415 	struct object_data *player_object;
416 	struct player_data *player= get_player_data(player_index);
417 	short next_object;
418 	struct polygon_data *polygon;
419 	short *neighbor_indexes;
420 	short i;
421 
422 	player_object= get_object_data(get_monster_data(player->monster_index)->object_index);
423 
424 	polygon= get_polygon_data(player_object->polygon);
425 	neighbor_indexes= get_map_indexes(polygon->first_neighbor_index, polygon->neighbor_count);
426 
427 	// Skip this step if neighbor indexes were not found
428 	if (!neighbor_indexes) return;
429 
430 	for (i=0;i<polygon->neighbor_count;++i)
431 	{
432 
433 		struct polygon_data *neighboring_polygon= get_polygon_data(*neighbor_indexes++);
434 
435 		if (!POLYGON_IS_DETACHED(neighboring_polygon))
436 		{
437 			next_object= neighboring_polygon->first_object;
438 
439 			while(next_object != NONE)
440 			{
441 				object= get_object_data(next_object);
442 				if (GET_OBJECT_OWNER(object)==_object_is_item && !OBJECT_IS_INVISIBLE(object))
443 				{
444 					if (guess_distance2d((world_point2d *) &player->location, (world_point2d *) &object->location)<=MAXIMUM_ARM_REACH)
445 					{
446 						world_distance radius, height;
447 
448 						get_monster_dimensions(player->monster_index, &radius, &height);
449 
450 						if (object->location.z >= player->location.z - MAXIMUM_ARM_REACH && object->location.z <= player->location.z + height &&
451 							test_item_retrieval(player_object->polygon, &player_object->location, &object->location))
452 						{
453 							if(get_item(player_index, next_object))
454 							{
455 								/* Start the search again.. */
456 								next_object= neighboring_polygon->first_object;
457 								continue;
458 							}
459 						}
460 					}
461 				}
462 
463 				next_object= object->next_object;
464 			}
465 		}
466 	}
467 }
468 
swipe_nearby_items(short player_index)469 void swipe_nearby_items(short player_index)
470 {
471 	if (film_profile.swipe_nearby_items_fix)
472 	{
473 		a1_swipe_nearby_items(player_index);
474 	}
475 	else
476 	{
477 		m2_swipe_nearby_items(player_index);
478 	}
479 }
480 
481 
mark_item_collections(bool loading)482 void mark_item_collections(
483 	bool loading)
484 {
485 	mark_collection(_collection_items, loading);
486 }
487 
unretrieved_items_on_map(void)488 bool unretrieved_items_on_map(
489 	void)
490 {
491 	bool found_item= false;
492 	struct object_data *object;
493 	short object_index;
494 
495 	for (object_index= 0, object= objects; object_index<MAXIMUM_OBJECTS_PER_MAP; ++object_index, ++object)
496 	{
497 		if (SLOT_IS_USED(object) && GET_OBJECT_OWNER(object)==_object_is_item)
498 		{
499 			if (get_item_kind(object->permutation)==_item)
500 			{
501 				found_item= true;
502 				break;
503 			}
504 		}
505 	}
506 
507 	return found_item;
508 }
509 
item_valid_in_current_environment(short item_type)510 bool item_valid_in_current_environment(
511 	short item_type)
512 {
513 	bool valid= true;
514 	struct item_definition *definition= get_item_definition(item_type);
515 	// LP change: added idiot-proofing
516 	if (!definition) return false;
517 
518 	if (definition->invalid_environments & static_world->environment_flags)
519 	{
520 		valid= false;
521 	}
522 
523 	return valid;
524 }
525 
get_item_kind(short item_id)526 short get_item_kind(
527 	short item_id)
528 {
529 	struct item_definition *definition= get_item_definition(item_id);
530 	// LP change: added idiot-proofing
531 	if (!definition) return NONE;
532 
533 	return definition->item_kind;
534 }
535 
get_item_shape(short item_id)536 short get_item_shape(
537 	short item_id)
538 {
539 	struct item_definition *definition= get_item_definition(item_id);
540 	// LP change: added idiot-proofing
541 	if (!definition) return NONE;
542 
543 	return definition->base_shape;
544 }
545 
try_and_add_player_item(short player_index,short type)546 bool try_and_add_player_item(
547 	short player_index,
548 	short type)
549 {
550 	struct item_definition *definition= get_item_definition(type);
551 	// LP change: added idiot-proofing
552 	if (!definition) return false;
553 
554 	struct player_data *player= get_player_data(player_index);
555 	short grabbed_sound_index= NONE;
556 	bool success= false;
557 
558 	switch (definition->item_kind)
559 	{
560 		case _powerup: /* powerups don�t get added to your inventory */
561 			if (legal_player_powerup(player_index, type))
562 			{
563 				process_player_powerup(player_index, type);
564 				object_was_just_destroyed(_object_is_item, type);
565 				grabbed_sound_index= Sound_GotPowerup();
566 				success= true;
567 			}
568 			break;
569 
570 		case _ball:
571 			// START Benad
572 			/* Note that you can only carry ONE ball (ever) */
573 			if(find_player_ball_color(player_index)==NONE)
574 			{
575 				struct player_data *player= get_player_data(player_index);
576 
577 				// When taking ball of your own team, it returns to its original
578 				// position on the map, unless it's already in our base (or hill).
579 				if ( (GET_GAME_TYPE() == _game_of_capture_the_flag) &&
580 					 (type - BALL_ITEM_BASE == player->team)  )
581 				{
582 					// START Benad modified oct. 1st
583 					struct polygon_data *polygon= get_polygon_data(player->supporting_polygon_index);
584 					if (polygon->type!=_polygon_is_base)
585 					{
586 						object_was_just_destroyed(_object_is_item, type);
587 						grabbed_sound_index= Sound_GotItem();
588 						success= true;
589 						goto DONE;
590 					}
591 					else // _polygon_is_base and base == player->team
592 						 // base != player->team taken care of in update_net_game
593 						 // (your ball should NEVER get there)
594 					{
595 						success= false;
596 						goto DONE;
597 					}
598 					// END Benad modified oct. 1st
599 				}
600 				else if (GET_GAME_TYPE() == _game_of_rugby)
601 				{
602 					// ghs: work around for SF 2894880
603 
604 					// if you're in an enemy base
605 					// and pick up the ball, you
606 					// score
607 					struct polygon_data* polygon = get_polygon_data(player->supporting_polygon_index);
608 					if (polygon->type == _polygon_is_base && polygon->permutation != player->team)
609 					{
610 						/* Goal! */
611 
612 						// defined in network_games.cpp
613 						const int _points_scored = 0;
614 						player->netgame_parameters[_points_scored]++;
615 						team_netgame_parameters[player->team][_points_scored]++;
616 						object_was_just_destroyed(_object_is_item, type);
617 						grabbed_sound_index = Sound_GotItem();
618 						success = true;
619 						goto DONE;
620 					}
621 				}
622 
623 				player->items[type]= 1;
624 
625 				// OK, since only for loading weapon. Ignores item_type, cares
626 				// only about item_kind (here, _ball).
627 				/* Load the ball weapon.. */
628 				process_new_item_for_reloading(player_index, _i_red_ball);
629 
630 				/* Tell the interface to redraw next time it has to */
631 				mark_player_inventory_as_dirty(player_index, type);
632 
633 				success= true;
634 			}
635 			grabbed_sound_index= NONE;
636 			break;
637 			// END Benad
638 
639 		case _weapon:
640 		case _ammunition:
641 		case _item:
642 			/* Increment the count */
643 			assert(type>=0 && type<NUMBER_OF_ITEMS);
644 			if(player->items[type]==NONE)
645 			{
646 				/* just got the first one.. */
647 				player->items[type]= 1;
648 				success= true;
649 			}
650 			else if(player->items[type]+1<=definition->maximum_count_per_player ||
651 				(dynamic_world->game_information.difficulty_level==_total_carnage_level && ((static_world->environment_flags & _environment_m1_weapons) || definition->item_kind==_ammunition)))
652 			{
653 				/* Increment your count.. */
654 				player->items[type]++;
655 				success= true;
656 			} else {
657 				/* You have exceeded the count of these items */
658 			}
659 
660 			grabbed_sound_index= Sound_GotItem();
661 
662 			if(success)
663 			{
664 				/* Reload or whatever.. */
665 				process_new_item_for_reloading(player_index, type);
666 
667 				/* Tell the interface to redraw next time it has to */
668 				mark_player_inventory_as_dirty(player_index, type);
669 			}
670 			break;
671 
672 		default:
673 			assert(false);
674 			break;
675 	}
676 	// Benad. Burk.
677 	DONE:
678 
679 	//CP Addition: call any script traps available
680 	// jkvw: but only if we actually got the item
681 	if (success)
682 	{
683 		//MH: Call Lua script hook
684 		L_Call_Got_Item(type, player_index);
685 	}
686 
687 	/* Play the pickup sound */
688 	if (success && player_index==current_player_index)
689 	{
690 		SoundManager::instance()->PlayLocalSound(grabbed_sound_index);
691 
692 		/* Flash screen */
693 		start_fade(_fade_bonus);
694 	}
695 
696 	return success;
697 }
698 
699 /* ---------- private code */
700 
701 
item_trigger_cost_function(short source_polygon_index,short line_index,short destination_polygon_index,void * unused)702 static int32 item_trigger_cost_function(
703 	short source_polygon_index,
704 	short line_index,
705 	short destination_polygon_index,
706 	void *unused)
707 {
708 	struct polygon_data *destination_polygon= get_polygon_data(destination_polygon_index);
709 //	struct polygon_data *source_polygon= get_polygon_data(source_polygon_index);
710 //	struct line_data *line= get_line_data(line_index);
711 	int32 cost= 1;
712 
713 	(void) (unused);
714 	(void) (source_polygon_index);
715 	(void) (line_index);
716 
717 	if (destination_polygon->type==_polygon_is_zone_border) cost= -1;
718 
719 	return cost;
720 }
721 
get_item(short player_index,short object_index)722 static bool get_item(
723 	short player_index,
724 	short object_index)
725 {
726 	struct object_data *object= get_object_data(object_index);
727 	bool success;
728 
729 	assert(GET_OBJECT_OWNER(object)==_object_is_item);
730 
731 	success= try_and_add_player_item(player_index, object->permutation);
732 	if (success)
733 	{
734 		/* remove it */
735 		remove_map_object(object_index);
736 	}
737 
738 	return success;
739 }
740 
test_item_retrieval(short polygon_index1,world_point3d * location1,world_point3d * location2)741 static bool test_item_retrieval(
742 	short polygon_index1,
743 	world_point3d *location1,
744 	world_point3d *location2)
745 {
746 	bool valid_retrieval= true;
747 	short polygon_index= polygon_index1;
748 
749 	do
750 	{
751 		short line_index= find_line_crossed_leaving_polygon(polygon_index, (world_point2d *) location1,
752 			(world_point2d *) location2);
753 
754 		if (line_index!=NONE)
755 		{
756 			polygon_index= find_adjacent_polygon(polygon_index, line_index);
757 			if (LINE_IS_SOLID(get_line_data(line_index))) valid_retrieval= false;
758 			if (polygon_index!=NONE)
759 			{
760 				struct polygon_data *polygon= get_polygon_data(polygon_index);
761 
762 				if (polygon->type==_polygon_is_platform)
763 				{
764 					struct platform_data *platform= get_platform_data(polygon->permutation);
765 
766 					if (PLATFORM_IS_MOVING(platform)) valid_retrieval= false;
767 				}
768 			}
769 		}
770 		else
771 		{
772 			polygon_index= NONE;
773 		}
774 	}
775 	while (polygon_index!=NONE && valid_retrieval);
776 
777 	return valid_retrieval;
778 }
779 
780 
781 // LP addition: initializer
initialize_items(void)782 void initialize_items(void) {
783 }
784 
785 // LP addition: animator
animate_items(void)786 void animate_items(void) {
787 
788 	short object_index;
789 	object_data *object;
790 	for (object_index= 0, object= objects; object_index<MAXIMUM_OBJECTS_PER_MAP; ++object_index, ++object)
791 	{
792 		if (SLOT_IS_USED(object) && GET_OBJECT_OWNER(object)==_object_is_item && !OBJECT_IS_INVISIBLE(object))
793 		{
794 			short type = object->permutation;
795 			if (get_item_kind(type) != NONE)
796 			{
797 				struct item_definition *ItemDef = get_item_definition(type);
798 				// LP change: added idiot-proofing
799 				if (!ItemDef) continue;
800 
801 				shape_descriptor shape = ItemDef->base_shape;
802 				struct shape_animation_data *animation= get_shape_animation_data(shape);
803 				if (!animation) continue;
804 
805 				// Randomize if non-animated; do only once
806 				if (object->facing >= 0) {
807 					if (randomize_object_sequence(object_index,shape))
808 					{
809 						object->facing = NONE;
810 					}
811 				}
812 				// Now the animation
813 				if (object->facing >= 0)
814 					animate_object(object_index);
815 			}
816 		}
817 	}
818 }
819 
820 struct item_definition *original_item_definitions = NULL;
821 
reset_mml_items()822 void reset_mml_items()
823 {
824 	if (original_item_definitions) {
825 		for (unsigned i = 0; i < NUMBER_OF_DEFINED_ITEMS; i++)
826 			item_definitions[i] = original_item_definitions[i];
827 		free(original_item_definitions);
828 		original_item_definitions = NULL;
829 	}
830 }
831 
parse_mml_items(const InfoTree & root)832 void parse_mml_items(const InfoTree& root)
833 {
834 	// back up old values first
835 	if (!original_item_definitions) {
836 		original_item_definitions = (struct item_definition *) malloc(sizeof(struct item_definition) * NUMBER_OF_DEFINED_ITEMS);
837 		assert(original_item_definitions);
838 		for (unsigned i = 0; i < NUMBER_OF_DEFINED_ITEMS; i++)
839 			original_item_definitions[i] = item_definitions[i];
840 	}
841 
842 	BOOST_FOREACH(InfoTree itree, root.children_named("item"))
843 	{
844 		int16 index;
845 		if (!itree.read_indexed("index", index, NUMBER_OF_DEFINED_ITEMS))
846 			continue;
847 
848 		item_definition& def = item_definitions[index];
849 		itree.read_attr("singular", def.singular_name_id);
850 		itree.read_attr("plural", def.plural_name_id);
851 		itree.read_indexed("maximum", def.maximum_count_per_player, SHRT_MAX+1);
852 		itree.read_attr("invalid", def.invalid_environments);
853 		itree.read_indexed("type", def.item_kind, NUMBER_OF_ITEM_TYPES);
854 
855 		BOOST_FOREACH(InfoTree shape, itree.children_named("shape"))
856 			shape.read_shape(def.base_shape);
857 	}
858 }
859