1 /*
2  *
3  *   Copyright (c) 2002, 2003 Johannes Prix
4  *   Copyright (c) 2004-2010 Arthur Huillet
5  *
6  *
7  *  This file is part of Freedroid
8  *
9  *  Freedroid is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  Freedroid is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with Freedroid; see the file COPYING. If not, write to the
21  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  *  MA  02111-1307  USA
23  *
24  */
25 
26 /**
27  * This file contains all the functions managing the items in the game.
28  */
29 
30 #define _items_c
31 
32 #include "system.h"
33 
34 #include "defs.h"
35 #include "struct.h"
36 #include "global.h"
37 #include "proto.h"
38 
39 /**
40  * \brief Initializes an empty item.
41  * \param item_ Item.
42  */
init_item(item * it)43 void init_item(item *it)
44 {
45 	memset(it, 0, sizeof(item));
46 	it->type = -1;
47 	it->pos.x = -1;
48 	it->pos.y = -1;
49 	it->pos.z = -1;
50 	it->inventory_position.x = -1;
51 	it->inventory_position.y = -1;
52 }
53 
create_item_with_id(const char * item_id,int full_durability,int multiplicity)54 item create_item_with_id(const char *item_id, int full_durability, int multiplicity)
55 {
56 	item new_item;
57 
58 	init_item(&new_item);
59 	new_item.type = get_item_type_by_id(item_id);
60 	if (new_item.type < 0 || new_item.type >= Number_Of_Item_Types) {
61 		error_message(__FUNCTION__, "No items with the name \"%s\" exist in the game. Cannot create item.\nChanging item type to bug item to avoid crash.", PLEASE_INFORM, item_id);
62 		new_item.type = 0;
63 	}
64 
65 	FillInItemProperties(&new_item, full_durability, multiplicity);
66 
67 	return new_item;
68 }
69 
70 /**
71  * This function checks if the item can be equipped
72  * \param it item to check
73  * \return TRUE if the item can be installed in one slot, FALSE otherwise
74  */
equippable_item(item * it)75 static int equippable_item(item *it)
76 {
77 	return ItemMap[it->type].slot != NO_SLOT;
78 }
79 
equip_item(item * new_item)80 void equip_item(item *new_item)
81 {
82 	item *old_item;
83 	itemspec *new_itemspec;
84 
85 	new_itemspec = &ItemMap[new_item->type];
86 
87 	// If the item can't be equipped, stop now and throw a warning.
88 	if (!equippable_item(new_item)) {
89 		error_message(__FUNCTION__, "Tried to equip the item \"%s\" which can't be equipped.",
90 				PLEASE_INFORM, new_itemspec->id);
91 		return;
92  	}
93 
94 	// If there's an existing item in the equipment slot, drop it to the
95 	// inventory or to the floor.
96 	old_item = get_equipped_item_in_slot_for(new_item->type);
97 	if (old_item->type != -1) {
98 		give_item(old_item);
99 	}
100 
101 	// If we're equipping a two-handed weapon, we need to unequip the shield
102 	// as well. We drop the shield to the inventory or to the floor.
103 	if (new_itemspec->weapon_needs_two_hands && Me.shield_item.type != -1) {
104 		give_item(&Me.shield_item);
105 	}
106 
107 	// Before equipping a shield, if a two-handed weapon is equipped, you need to drop it.
108 	if (new_itemspec->slot == SHIELD_SLOT && Me.weapon_item.type != -1
109 			&& ItemMap[Me.weapon_item.type].weapon_needs_two_hands) {
110 		give_item(&Me.weapon_item);
111 	}
112 
113 	// Move the new item to the now empty equipment slot.
114 	MoveItem(new_item, old_item);
115 }
116 
117 
118 /**
119  * Gets a pointer to the currently equipped item of the
120  * specified type
121  */
get_equipped_item_in_slot_for(int item_type)122 item *get_equipped_item_in_slot_for(int item_type)
123 {
124 	item *equipped_item;
125 	itemspec *spec;
126 
127 	spec = &ItemMap[item_type];
128 
129 	if (spec->slot == WEAPON_SLOT) {
130 		equipped_item = &Me.weapon_item;
131 	}
132 	else if (spec->slot == BOOT_SLOT) {
133 		equipped_item = &Me.drive_item;
134 	}
135 	else if (spec->slot == ARMOR_SLOT) {
136 		equipped_item = &Me.armour_item;
137 	}
138 	else if (spec->slot == SHIELD_SLOT) {
139 		equipped_item = &Me.shield_item;
140 	}
141 	else if (spec->slot == HELM_SLOT) {
142 		equipped_item = &Me.special_item;
143 	}
144 	else
145 	{
146 		equipped_item = NULL;
147 	}
148 
149 	return equipped_item;
150 }
151 
152 /**
153  * This function does the home made item repair, i.e. the item repair via
154  * the repair skill in contrast to the item repair via the shop, which of
155  * course works much better.
156  */
self_repair_item(item * it)157 static void self_repair_item(item *it)
158 {
159 	int wear = 0, used = 0;
160 	int my_skill_level = 0;
161 	int percent_chance = MyRandom(100);
162 
163 	if (it->max_durability == -1) {
164 		play_sound("effects/tux_ingame_comments/Tux_Item_Cant_Be_0.ogg");
165 		return;
166 	}
167 
168 	my_skill_level = Me.skill_level[get_program_index_with_name("Repair equipment")];
169 
170 /* if Tux's repair ability is at the full skill level
171  * make it so that most of the time the item is fully repaired. Give it a 5 % chance
172  * of improving the item and a 3% chance of making it worse.
173  * otherwise give him a skill level *10 percent chance of repairing the item completely
174  */
175 	if(my_skill_level >= NUMBER_OF_SKILL_LEVELS-1) {
176 		if(percent_chance > 94)
177 		{
178 			it->max_durability++;
179 		}
180 		else if (percent_chance < 3)
181 		{
182 			it->max_durability--;
183 		}
184 	}
185 	else if(my_skill_level*10 < percent_chance) {
186 		used = it->max_durability - it->current_durability;
187 		/* Self repair formula: decrease max_durability between 1 and 11-skill_level*/
188 		wear = 1 + MyRandom(10 - my_skill_level);
189 		//never decrease more than current missing durability
190 		it->max_durability -= min(wear, used);
191 		if (it->max_durability < 1) {
192 			it->max_durability = 1;
193 		}
194 	}
195 	//when you wear off all extra durability, the item become normal again
196 	if (it->quality == GOOD_QUALITY && it->max_durability < ItemMap[it->type].base_item_durability) {
197 		it->quality = NORMAL_QUALITY;
198 	}
199 	//if you add extra durability, the item becomes good
200 	if (it->quality == NORMAL_QUALITY && it->max_durability > ItemMap[it->type].base_item_durability) {
201 		it->quality = GOOD_QUALITY;
202 	}
203 
204 	it->current_durability = it->max_durability;
205 	play_sound("effects/tux_ingame_comments/Tux_This_Quick_Fix_0.ogg");
206 }
207 
208 /**
209  * This function calculates the price of a single given item, taking into account
210  * (*) the base list price of the item
211  * (*) the base list prices of the installed add-ons
212  */
calculate_item_buy_price(item * BuyItem)213 unsigned long calculate_item_buy_price(item * BuyItem)
214 {
215 	int i;
216 	int price = ItemMap[BuyItem->type].base_list_price;
217 
218 	// Add the prices of the add-ons to the total price.
219 	for (i = 0; i < BuyItem->upgrade_sockets.size; i++) {
220 		const char *addon = BuyItem->upgrade_sockets.arr[i].addon;
221 		if (addon) {
222 			int type = get_item_type_by_id(addon);
223 			price += ItemMap[type].base_list_price;
224 		}
225 	}
226 
227 	return price;
228 }
229 
230 /**
231  * This function calculates the sell price of a single given item, taking into account
232  * the markdown (currently 0.3 for all NPCs)
233  */
calculate_item_sell_price(item * BuyItem)234 unsigned long calculate_item_sell_price(item * BuyItem)
235 {
236 	int price;
237 	// Some items cannot be sold
238 	if (!(price = calculate_item_buy_price(BuyItem)))
239 		return 0;
240 
241 	// Items sell for less than the full price of the item.
242 	price = floor(0.3 * price);
243 
244 	// Prices have to be non-zero so the item can be sold
245 	return price ? price : 1;
246 }
247 
248 /**
249  * This function calculates the price of a given item, taking into account
250  * (*) the base list price of the item
251  * (*) the base list prices of the installed add-ons
252  * (*) AND THE CURRENT DURABILITY of the item in relation to its max durability.
253  */
calculate_item_repair_price(item * repair_item)254 unsigned long calculate_item_repair_price(item * repair_item)
255 {
256 	// For repair, it's not the full 'buy' cost...
257 	//
258 #define REPAIR_PRICE_FACTOR (0.5)
259 
260 	// This is the price of the DAMAGE in the item, haha
261 	// This can only be requested for repair items
262 	//
263 	if (repair_item->max_durability != (-1)) {
264 		unsigned long price = (calculate_item_buy_price(repair_item) *
265 			REPAIR_PRICE_FACTOR * (repair_item->max_durability - repair_item->current_durability) / repair_item->max_durability);
266 
267 		// Never repair for free, minimum price is 1
268 		return price ? price : 1;
269 	}
270 	return 0;
271 };				// long calculate_item_repair_price ( item* repair_item )
272 
273 /**
274  * \brief Returns a random quality multiplier.
275  * \return a quality indicator.
276  */
random_item_quality()277 static enum item_quality random_item_quality()
278 {
279 	// In order to make normal quality items more common than others, we first
280 	// choose a quality level by indexing a probability distribution array.
281 	const enum item_quality quality_distribution[] = {
282 		BAD_QUALITY,
283 		BAD_QUALITY, 	//20%
284 		NORMAL_QUALITY,
285 		NORMAL_QUALITY,
286 		NORMAL_QUALITY,
287 		NORMAL_QUALITY,
288 		NORMAL_QUALITY,
289 		NORMAL_QUALITY,
290 		NORMAL_QUALITY, //70%
291 		GOOD_QUALITY};	//10%
292 	int max_index = (sizeof(quality_distribution) / sizeof(quality_distribution[0])) - 1;
293 	int quality_index = MyRandom(max_index);
294 	return quality_distribution[quality_index];
295 }
296 
297 /**
298  * \brief Initializes the properties of the item.
299  *
300  * Sets the durability, armor rating, and other such properties based on the
301  * type of the item. The caller must ensure that the type field has been set.
302  *
303  * \param it Item whose properties to initialize.
304  * \param full_durability TRUE to make the item fully repaired.
305  * \param multiplicity Multiplicity of the item.
306  */
FillInItemProperties(item * it,int full_durability,int multiplicity)307 void FillInItemProperties(item *it, int full_durability, int multiplicity)
308 {
309 	// Some basic error checking of item type.
310 	if ((it->type < 0) || (it->type >= Number_Of_Item_Types)) {
311 		error_message(__FUNCTION__, "Cannot fill in information for item with invalid type.", PLEASE_INFORM | IS_FATAL);
312 		return;
313 	}
314 
315 	itemspec *spec = &ItemMap[it->type];
316 
317 	it->multiplicity = multiplicity;
318 	it->ammo_clip = 0;
319 	it->throw_time = 0;
320 	it->quality = NORMAL_QUALITY;
321 	it->max_durability = -1;
322 
323 	if (!equippable_item(it)) {
324 		return;
325 	}
326 
327 	it->quality = random_item_quality();
328 
329 	// Add random bullets to the clip if the item is a gun.
330 	if (spec->weapon_ammo_type && spec->weapon_ammo_clip_size) {
331 		it->ammo_clip = MyRandom(spec->weapon_ammo_clip_size);
332 	}
333 
334 	// Set the base damage reduction by using the item spec and a random multiplier.
335 	it->armor_class = spec->base_armor_class + MyRandom(spec->armor_class_modifier);
336 
337 	// Set the maximum and current durabilities of the item.
338 	if (spec->base_item_durability != -1) {
339 		// The maximum durability is within the range specified by the item spec.
340 		it->max_durability = spec->base_item_durability + MyRandom(spec->item_durability_modifier);
341 
342 		int half = it->max_durability / 2;
343 		if (it->quality == BAD_QUALITY) {
344 			//between 50% and 100%
345 			it->max_durability = max(1, half + MyRandom(half));
346 		} else if (it->quality == GOOD_QUALITY) {
347 			//between 100% and 150%
348 			it->max_durability +=  MyRandom(half);
349 		}
350 
351 		if (full_durability) {
352 			it->current_durability = it->max_durability;
353 		} else {
354 			int quarter = it->max_durability / 4;
355 			//between 25% and 100%
356 			it->current_durability = max(1, quarter + MyRandom(quarter*3));
357 		}
358 
359 	} else {
360 		it->max_durability = -1;
361 		it->current_durability = 1;
362 	}
363 
364 	// Calculate the item bonuses affected by add-ons.
365 	// This will set the final armor rating and weapon damage, among other things.
366 	calculate_item_bonuses(it);
367 }
368 
append_item_name(item * ShowItem,struct auto_string * str)369 void append_item_name(item * ShowItem, struct auto_string *str)
370 {
371 	// If the item has upgrade sockets, use a different color for the name.
372 	if (ShowItem->upgrade_sockets.size) {
373 		autostr_append(str, "%s", font_switchto_blue);
374 	} else {
375 		autostr_append(str, "%s", font_switchto_neon);
376 	}
377 
378 	if (item_spec_eq_id(ShowItem->type, "Valuable Circuits"))
379 		autostr_append(str, "%d ", ShowItem->multiplicity);
380 
381 	autostr_append(str, "%s", D_(item_specs_get_name(ShowItem->type)));
382 
383 	if (ShowItem->quality == GOOD_QUALITY) {
384 		autostr_append(str, "\n%s", font_switchto_blue);
385 		autostr_append(str, _("Good quality"));
386 	} else if (ShowItem->quality == BAD_QUALITY) {
387 		autostr_append(str, "\n%s", font_switchto_red);
388 		autostr_append(str, _("Bad quality"));
389 	}
390 
391 	// Now that the item name is out, we can switch back to the standard font color...
392 	autostr_append(str, "%s", font_switchto_neon);
393 }
394 
395 /**
396  * This function drops an item at a given place.
397  */
DropItemAt(int ItemType,int level_num,float x,float y,int multiplicity)398 item *DropItemAt(int ItemType, int level_num, float x, float y, int multiplicity)
399 {
400 	gps item_pos;
401 
402 	if (ItemType < 0 || ItemType >= Number_Of_Item_Types)
403 		error_message(__FUNCTION__, "\
404 Received item type %d that is outside the range of allowed item types.",
405 			     PLEASE_INFORM | IS_FATAL, ItemType);
406 
407 	// Fix virtual position (e.g. from a dying robot)
408 	item_pos.x = x;
409 	item_pos.y = y;
410 	item_pos.z = level_num;
411 	if (!resolve_virtual_position(&item_pos, &item_pos))
412 		return NULL;
413 
414 	// Construct the new item
415 	item tmp_item;
416 	init_item(&tmp_item);
417 	tmp_item.type = ItemType;
418 	FillInItemProperties(&tmp_item, FALSE, multiplicity);
419 
420 	play_item_sound(ItemType, &item_pos);
421 
422 	return drop_item(&tmp_item, item_pos.x, item_pos.y, item_pos.z);
423 }
424 
get_random_item_type(int class)425 static int get_random_item_type(int class)
426 {
427 	int a = MyRandom(item_count_per_class[class] - 1) + 1;
428 
429 	//printf("Choosing in class %d among %d items, taking item %d\n", class, item_count_per_class[class], a);
430 
431 	int i;
432 	for (i = 0; i < Number_Of_Item_Types; i++) {
433 		if (ItemMap[i].min_drop_class != -1) {
434 			if (ItemMap[i].min_drop_class <= class && ItemMap[i].max_drop_class >= class)
435 				a--;
436 			if (!a)
437 				break;
438 		}
439 	}
440 
441 	if (a) {
442 		error_message(__FUNCTION__, "Looking for random item with class %d, a = %d after the loop.", PLEASE_INFORM | IS_FATAL, class,
443 			     a);
444 	}
445 	//printf("Dropping item %s (%d <= class <= %d), class %d\n", ItemMap[i].item_name, ItemMap[i].min_drop_class, ItemMap[i].max_drop_class, class);
446 	return i;
447 }
448 
449 /**
450  * This function drops a random item to the floor of the current level
451  * at position ( x , y ).
452  *
453  * The strategy in dropping the item is that one can easily set up and
454  * modify the table of items to be dropped.
455  *
456  */
DropRandomItem(int level_num,float x,float y,int class,int force_magical)457 void DropRandomItem(int level_num, float x, float y, int class, int force_magical)
458 {
459 	int DropDecision;
460 	int drop_item_type = 1;
461 	int drop_item_multiplicity = 1;
462 
463 	if (class == -1)
464 		return; // -1 is the value to not drop item.
465 
466 	// Drop item only if the class is between 0 and MAX_DROP_CLASS.
467 	if (class < 0 || class > MAX_DROP_CLASS) {
468 		error_message(__FUNCTION__, "The drop class %d is invalid (out of 0 to MAX_DROP_CLASS: %d).\n", PLEASE_INFORM, class, MAX_DROP_CLASS);
469 		return;
470 	}
471 
472 	// First we determine if there is something dropped at all or not,
473 	// cause in the later case, we can return immediately.  If a drop is
474 	// forced, we need not check for not do drop.
475 	//
476 	DropDecision = MyRandom(100);
477 
478 	// We decide whether we drop something at all or not
479 	//
480 	if ((DropDecision < 100 - GOLD_DROP_PERCENTAGE) && (DropDecision > ITEM_DROP_PERCENTAGE))
481 		return;
482 
483 	// Perhaps it's some gold that will be dropped.  That's rather
484 	// simple, so we do this first.
485 	//
486 	if ((DropDecision > 100 - GOLD_DROP_PERCENTAGE)) {
487 		// If class == 0, we want to avoid to drop 0-1 valuable circuits
488 		int how_many = (class == 0) ? 2 : 0;
489 		how_many += MONEY_PER_BOT_CLASS * class + MyRandom(MONEY_PER_BOT_CLASS - 1);
490 		DropItemAt(get_item_type_by_id("Valuable Circuits"), level_num, x, y, how_many);
491 	}
492 
493 	if ((DropDecision < ITEM_DROP_PERCENTAGE)) {
494 		drop_item_type = get_random_item_type(class);
495 
496 		// Determine the multiplicity for the item
497 		drop_item_multiplicity = ItemMap[drop_item_type].drop_amount + MyRandom(ItemMap[drop_item_type].drop_amount_max - ItemMap[drop_item_type].drop_amount);
498 
499 		// Create the item and place it to the map. This can fail under certain
500 		// conditions so we need to check for errors and give up if one occurred.
501 		item *it = DropItemAt(drop_item_type, level_num, x, y, drop_item_multiplicity);
502 		if (!it) {
503 			return;
504 		}
505 
506 		// Create sockets occasionally if the item is of a customizable type.
507 		int socket_drop_decision = MyRandom(100);
508 		if (item_can_be_customized(it) && (force_magical ||
509 		            socket_drop_decision < SOCKET_DROP_PERCENTAGE)) {
510 			// Decide how many sockets to create. We randomly index an array of
511 			// socket counts to implement a non-uniform probability distribution.
512 			const int create_count_array[] = { 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4 };
513 			int random_index = MyRandom(10);
514 			int create_count = create_count_array[random_index];
515 
516 			// Create the desired number of sockets of random types.
517 			while (create_count--) {
518 				int socket_type = MyRandom(UPGRADE_SOCKET_TYPE_UNIVERSAL);
519 				create_upgrade_socket(it, socket_type, NULL);
520 			}
521 		}
522 	}
523 }
524 
525 /**
526  * When the influencer gets hit, all of his equipment suffers some damage.
527  * This is exactly what this function does:  apply the damage.
528  */
DamageItem(item * CurItem)529 void DamageItem(item * CurItem)
530 {
531 
532 	// If the item mentioned as parameter exists and if it is of
533 	// a destructable sort, then we apply the usual damage to it
534 	if ((CurItem->type != (-1)) && (CurItem->max_durability != (-1))) {
535 		CurItem->current_durability -= (MyRandom(100) < ARMOUR_DURABILITYLOSS_PERCENTAGE_WHEN_HIT) ? 1 : 0;
536 
537 		// Make sound denoting some protective item was damaged
538 		BulletReflectedSound();
539 
540 		// If the item has gone over its threshold of durability, it finally
541 		// breaks and vaporizes
542 		//
543 		if (rintf(CurItem->current_durability) <= 0) {
544 			DeleteItem(CurItem);
545 		}
546 	}
547 
548 };				// void DamageItem( item* CurItem )
549 
550 /* Now we do the same for a weapon that has been fired */
DamageWeapon(item * CurItem)551 void DamageWeapon(item * CurItem)
552 {
553 
554 	if ((CurItem->type != (-1)) && (CurItem->max_durability != (-1))) {
555 		CurItem->current_durability -= (MyRandom(100) < WEAPON_DURABILITYLOSS_PERCENTAGE_WHEN_USED) ? 1 : 0;
556 		if (rintf(CurItem->current_durability) <= 0) {
557 			DeleteItem(CurItem);
558 		}
559 	}
560 
561 };				// void DamageWeapon( item* CurItem )
562 
563 /**
564  * When the influencer gets hit, some of his equipment might suffer some damage.
565  * This is exactly what this function does:  apply the damage.
566  */
DamageProtectiveEquipment()567 void DamageProtectiveEquipment()
568 {
569 	int ItemHit = MyRandom(6);
570 
571 	if (ItemHit < 2)
572 		DamageItem(&(Me.armour_item));
573 	else if (ItemHit < 4)
574 		DamageItem(&(Me.shield_item));
575 	else if (ItemHit < 5)
576 		DamageItem(&(Me.drive_item));
577 	else
578                 DamageItem(&(Me.special_item));
579 
580 };				// void DamageProtectiveEquipment( void )
581 
582 /**
583  * This function is used when an equipment EXCHANGE is performed, i.e.
584  * one weapon equipped is replaced by a new item using the mouse.  This
585  * leads to an exchange in the items.  Yet, if the new item came from
586  * inventory, the old item can't be just put in the same place where the
587  * new item was, cause it might be bigger.  So, attempting to solve the
588  * problem, the old item from the slot can just be made into an item on
589  * the floor, but not visible yet of course, cause it still gets the
590  * held in hand attribute.
591  */
MakeHeldFloorItemOutOf(item * SourceItem)592 static void MakeHeldFloorItemOutOf(item * SourceItem)
593 {
594 	int i;
595 
596 	for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
597 		if (CURLEVEL()->ItemList[i].type == (-1))
598 			break;
599 	}
600 	if (i >= MAX_ITEMS_PER_LEVEL) {
601 		DebugPrintf(0, "\n No free position to drop item!!! ");
602 		i = 0;
603 		Terminate(EXIT_FAILURE);
604 	}
605 	// Now we enter the item into the item list of this level
606 	//
607 	CopyItem(SourceItem, &(CURLEVEL()->ItemList[i]));
608 
609 	CURLEVEL()->ItemList[i].pos.x = Me.pos.x;
610 	CURLEVEL()->ItemList[i].pos.y = Me.pos.y;
611 	CURLEVEL()->ItemList[i].pos.z = Me.pos.z;
612 
613 	item_held_in_hand = &(CURLEVEL()->ItemList[i]);
614 
615 	DeleteItem(SourceItem);
616 };				// void MakeHeldFloorItemOutOf( item* SourceItem )
617 
618 /**
619  * This function DELETES an item from the source location.
620  */
DeleteItem(item * it)621 void DeleteItem(item *it)
622 {
623 	delete_upgrade_sockets(it);
624 	init_item(it);
625 }
626 
627 /**
628  * This function COPIES an item from the source location to the destination
629  * location.
630  */
CopyItem(item * SourceItem,item * DestItem)631 void CopyItem(item * SourceItem, item * DestItem)
632 {
633 
634 	memcpy(DestItem, SourceItem, sizeof(item));
635 
636 	// Create a soft copy of the upgrade sockets. Memcpy just copied the
637 	// pointer but we want the actual data to be duplicated.
638 	copy_upgrade_sockets(SourceItem, DestItem);
639 }
640 
641 /**
642  * This function MOVES an item from the source location to the destination
643  * location.  The source location is then marked as unused inventory
644  * entry.
645  */
MoveItem(item * source_item,item * dest_item)646 void MoveItem(item *source_item, item *dest_item)
647 {
648 	if (source_item != dest_item) {
649 		memcpy(dest_item, source_item, sizeof(item));
650 		init_item(source_item);
651 	}
652 }
653 
654 /**
655  * This function applies a given item (to the influencer) and maybe
656  * eliminates the item after that, if it's an item that gets used up.
657  */
Quick_ApplyItem(int ItemKey)658 void Quick_ApplyItem(int ItemKey)
659 {
660 	int FoundItemNr;
661 
662 	if (ItemKey == 0) {
663 		//quick_inventory0 is slot 10
664 		ItemKey = 10;
665 	}
666 
667 	FoundItemNr = GetInventoryItemAt(ItemKey - 1, INVENTORY_GRID_HEIGHT - 1);
668 	if (FoundItemNr == (-1))
669 		return;
670 
671 	apply_item(&(Me.Inventory[FoundItemNr]));
672 
673 };				// void Quick_ApplyItem( item* CurItem )
674 
675 /**
676  * This function checks whether a given item has the name specified. This is
677  * used to match an item which its type in a flexible way (match by name instead
678  * of matching by index value)
679  */
get_item_type_by_id(const char * id)680 int get_item_type_by_id(const char *id)
681 {
682 	int cidx = 0;
683 	for (; cidx < Number_Of_Item_Types; cidx++) {
684 		if (!strcmp(ItemMap[cidx].id, id))
685 			return cidx;
686 	}
687 
688 	error_message(__FUNCTION__, "Unable to find item id %s", PLEASE_INFORM, id);
689 	return -1;
690 }
691 
692 /**
693  * This function checks whether a given item has the name specified. This is
694  * used to match an item which its type in a flexible way (match by name instead
695  * of matching by index value)
696  */
item_spec_eq_id(int type,const char * id)697 int item_spec_eq_id(int type, const char *id)
698 {
699 	if (type < 0 || type >= Number_Of_Item_Types)
700 		return FALSE;
701 
702 	if (!strcmp(ItemMap[type].id, id))
703 		return TRUE;
704 	else
705 		return FALSE;
706 }
707 
708 /**
709  * This function applies a given item (to the influencer) and maybe
710  * eliminates the item after that, if it's an item that gets used up.
711  */
apply_item(item * CurItem)712 void apply_item(item * CurItem)
713 {
714 	int failed_usage = 0; // if an item cannot be applied, to not remove it from inventory
715 
716 	// If the inventory slot is not at all filled, we need not do anything more...
717 	if (CurItem->type < 0)
718 		return;
719 
720 	if (!ItemMap[CurItem->type].right_use.tooltip) {
721 		Me.TextVisibleTime = 0;
722 		Me.TextToBeDisplayed = _("I can't use this item here.");
723 		return;
724 	}
725 
726 	// Forbid using items while paralyzed
727 	if (Me.paralyze_duration) {
728 		append_new_game_message(_("You can not use any items while paralyzed."));
729 		return;
730 	}
731 
732 	if (Me.busy_time > 0) {
733 		char *msg;
734 		switch (Me.busy_type) {
735 		case DRINKING_POTION:
736 			msg = _("You are drinking a potion!");
737 			break;
738 		case WEAPON_FIREWAIT:
739 			msg = _("Your are waiting for your weapon to fire again!");
740 			break;
741 		case WEAPON_RELOAD:
742 			msg = _("You are reloading your weapon!");
743 			break;
744 		case THROWING_GRENADE:
745 			msg = _("You are throwing a grenade!");
746 			break;
747 		case RUNNING_PROGRAM:
748 			msg = _("You are running a program!");
749 			break;
750 		case TAKING_PILL:
751 			msg = _("You are taking a pill!");
752 			break;
753 		default:
754 			msg = _("You are doing something so weird the game does not understand what it is");
755 		}
756 		// TRANSLATORS: the trailing %s is what the player is currently already doing (drinking, running a program, ...)
757 		append_new_game_message(_("How do you expect to do two things at a time? %s"), msg);
758 		return;		//if the player is busy reloading or anything
759 	}
760 	// At this point we know that the item is applicable in combat situation
761 	// and therefore all we need to do from here on is execute the item effect
762 	// upon the influencer or his environment.
763 	//
764 	if (item_spec_eq_id(CurItem->type, "Barf's Energy Drink")) {
765 		Me.energy += 15;
766 		Me.temperature -= 15;
767 		Me.running_power += 15;
768 	} else if (item_spec_eq_id(CurItem->type, "Diet supplement")) {
769 		Me.energy += 25;
770 		play_sound("effects/new_healing_sound.ogg");
771 	} else if (item_spec_eq_id(CurItem->type, "Antibiotic")) {
772 		Me.energy += 50;
773 		play_sound("effects/new_healing_sound.ogg");
774 	} else if (item_spec_eq_id(CurItem->type, "Doc-in-a-can")) {
775 		Me.energy += Me.maxenergy;
776 		play_sound("effects/new_healing_sound.ogg");
777 	} else if (item_spec_eq_id(CurItem->type, "Bottled ice")) {
778 		Me.temperature -= 50;
779 	} else if (item_spec_eq_id(CurItem->type, "Industrial coolant")) {
780 		Me.temperature -= 100;
781 	} else if (item_spec_eq_id(CurItem->type, "Liquid nitrogen")) {
782 		Me.temperature = 0;
783 	} else if (item_spec_eq_id(CurItem->type, "Running Power Capsule")) {
784 		Me.running_power = Me.max_running_power;
785 		Me.running_must_rest = FALSE;
786 	} else if (item_spec_eq_id(CurItem->type, "Strength Capsule")) {
787 		Me.current_power_bonus = 30;
788 		Me.power_bonus_end_date = Me.current_game_date + 2.0 * 60;
789 	} else if (item_spec_eq_id(CurItem->type, "Dexterity Capsule")) {
790 		Me.current_dexterity_bonus = 30;
791 		Me.dexterity_bonus_end_date = Me.current_game_date + 2.0 * 60;
792 	} else if (item_spec_eq_id(CurItem->type, "Map Maker")) {
793 		Me.map_maker_is_present = TRUE;
794 		GameConfig.Automap_Visible = TRUE;
795 		Play_Spell_ForceToEnergy_Sound();
796 	} else if (item_spec_eq_id(CurItem->type, "Strength Pill")) {
797 		Me.base_strength++;
798 	} else if (item_spec_eq_id(CurItem->type, "Dexterity Pill")) {
799 		Me.base_dexterity++;
800 	} else if (item_spec_eq_id(CurItem->type, "Code Pill")) {
801 		Me.base_cooling++;
802 	} else if (item_spec_eq_id(CurItem->type, "Brain Enlargement Pill")) {
803 		Me.base_cooling = 5;
804 		Me.base_strength = 5;
805 		Me.base_dexterity = 5;
806 		Me.base_physique = 5;
807 		Takeover_Game_Lost_Sound();
808 		append_new_game_message(_("The doctor warned you. You are now weak and sickly."));
809 	}
810 
811 	// Do the skill
812 	if (ItemMap[CurItem->type].right_use.skill) {
813 		failed_usage = !DoSkill(get_program_index_with_name(ItemMap[CurItem->type].right_use.skill), 0);
814 	// Improve the skill
815 	} else if (ItemMap[CurItem->type].right_use.add_skill) {
816 		failed_usage = improve_program(get_program_index_with_name(ItemMap[CurItem->type].right_use.add_skill));
817 
818 		if(failed_usage == 0) {
819 			Play_Spell_ForceToEnergy_Sound();
820 		} else {
821 			// TRANSLATORS: the trailing %s is a program name
822 			append_new_game_message(_("You have reached the maximum skill level for %s"),
823 									D_(ItemMap[CurItem->type].right_use.add_skill));
824 			Takeover_Game_Deadlock_Sound();
825 		}
826 	}
827 
828 	if (!failed_usage) {
829 
830 		play_item_sound(CurItem->type, &Me.pos);
831 
832 		// Apply busy time and busy type
833 		Me.busy_time = ItemMap[CurItem->type].right_use.busy_time;
834 		Me.busy_type = ItemMap[CurItem->type].right_use.busy_type;
835 
836 		// In some cases the item concerned is a one-shot-device like a health potion, which should
837 		// evaporize after the first application.  Therefore we delete the item from the inventory list.
838 		//
839 		if (CurItem->multiplicity > 1)
840 			CurItem->multiplicity--;
841 		else
842 			DeleteItem(CurItem);
843 	}
844 
845 	while (MouseRightPressed())
846 		SDL_Delay(1);
847 }
848 
849 /**
850  * This function checks if a given coordinate within the influencers
851  * inventory grid can be considered as free or as occupied by some item.
852  */
Inv_Pos_Is_Free(int x,int y)853 int Inv_Pos_Is_Free(int x, int y)
854 {
855 	int i;
856 	int item_width;
857 	int item_height;
858 
859 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY - 1; i++) {
860 		if (Me.Inventory[i].type == (-1))
861 			continue;
862 
863 		if (item_held_in_hand == &Me.Inventory[i])
864 			continue;
865 
866 		// for ( item_height = 0 ; item_height < ItemSizeTable[ Me.Inventory[ i ].type ].y ; item_height ++ )
867 		for (item_height = 0; item_height < ItemMap[Me.Inventory[i].type].inv_size.y; item_height++) {
868 			for (item_width = 0; item_width < ItemMap[Me.Inventory[i].type].inv_size.x; item_width++) {
869 				if (((Me.Inventory[i].inventory_position.x + item_width) == x) &&
870 				    ((Me.Inventory[i].inventory_position.y + item_height) == y))
871 					return (FALSE);
872 			}
873 		}
874 	}
875 	return TRUE;
876 
877 };				// int Inv_Pos_Is_Free( Inv_Loc.x , Inv_Loc.y )
878 
879 /**
880  * This function returns the index in the inventory list of the object
881  * at the inventory position x y.  If no object is found to occupy that
882  * square, an index of (-1) is returned.
883  *
884  * NOTE: The mentioned coordinates refer to the squares of the inventory grid!!
885  *
886  */
GetInventoryItemAt(int x,int y)887 int GetInventoryItemAt(int x, int y)
888 {
889 	int i;
890 	int item_width;
891 	int item_height;
892 
893 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY - 1; i++) {
894 		if (Me.Inventory[i].type == (-1))
895 			continue;
896 
897 		for (item_height = 0; item_height < ItemMap[Me.Inventory[i].type].inv_size.y; item_height++) {
898 			for (item_width = 0; item_width < ItemMap[Me.Inventory[i].type].inv_size.x; item_width++) {
899 				if (((Me.Inventory[i].inventory_position.x + item_width) == x) &&
900 				    ((Me.Inventory[i].inventory_position.y + item_height) == y)) {
901 					return (i);
902 				}
903 			}
904 		}
905 	}
906 	return (-1);		// Nothing found at this grabbing location!!
907 
908 };				// int GetInventoryItemAt ( int x , int y )
909 
910 /**
911  *
912  * Often, especially in dialogs and in order to determine if some answer
913  * should be allowed for the Tux or not, it is important to know if the
914  * Tux has some special item of a given type in inventory or not and also
915  * how many of those items the Tux really has.
916  *
917  * This function is now intended to count the number of items of a given
918  * type in the inventory of the Me.
919  *
920  */
CountItemtypeInInventory(int Itemtype)921 int CountItemtypeInInventory(int Itemtype)
922 {
923 	int i;
924 	int NumberOfItemsFound = 0;
925 
926 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
927 		if (Me.Inventory[i].type == Itemtype)
928 			NumberOfItemsFound += Me.Inventory[i].multiplicity;
929 	}
930 	return NumberOfItemsFound;
931 
932 };				// int CountItemtypeInInventory( int Itemtype )
933 
934 /**
935  *
936  *
937  */
FindFirstInventoryIndexWithItemType(int Itemtype)938 static int FindFirstInventoryIndexWithItemType(int Itemtype)
939 {
940 	int i;
941 
942 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
943 		if (Me.Inventory[i].type == Itemtype)
944 			return (i);
945 	}
946 
947 	// Severe error:  Item type NOT found in inventory!!!
948 	//
949 	fprintf(stderr, "\n\nItemType: '%d'.\n", Itemtype);
950 	error_message(__FUNCTION__, "\
951 There was an item code for an item to locate in inventory, but inventory\n\
952 did not contain this item type at all!  This indicates a severe bug in FreedroidRPG.", PLEASE_INFORM | IS_FATAL);
953 
954 	return (-1);
955 
956 };				// int FindFirstInventoryIndexWithItemType ( ItemPointer->type , PLAYER_NR_0 )
957 
958 /**
959  * At some point the Tux will hand over all his items of a given type
960  * to a dialog partner.  This function is intended to do exactly this:
961  * To remove all items of a given type from the inventory of a given
962  * player.
963  */
DeleteInventoryItemsOfType(int Itemtype,int amount)964 void DeleteInventoryItemsOfType(int Itemtype, int amount)
965 {
966 	int i;
967 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
968 		if (Me.Inventory[i].type == Itemtype) {
969 			if (Me.Inventory[i].multiplicity > amount)
970 				Me.Inventory[i].multiplicity -= amount;
971 			else
972 				DeleteItem(&(Me.Inventory[i]));
973 			return;
974 		}
975 	}
976 };				// void DeleteInventoryItemsOfType( int Itemtype )
977 
978 /**
979  * This deletes ONE item of the given type, like one bullet that has
980  * just been expended.
981  */
DeleteOneInventoryItemsOfType(int Itemtype)982 void DeleteOneInventoryItemsOfType(int Itemtype)
983 {
984 	int i;
985 	for (i = 0; i < MAX_ITEMS_IN_INVENTORY; i++) {
986 		if (Me.Inventory[i].type == Itemtype) {
987 			if (Me.Inventory[i].multiplicity > 1)
988 				Me.Inventory[i].multiplicity--;
989 			else
990 				DeleteItem(&(Me.Inventory[i]));
991 			return;
992 		}
993 	}
994 
995 	// This point must never be reached or a severe error has occurred...
996 	//
997 	fprintf(stderr, "\n\nItemType: '%d'.\n", Itemtype);
998 	error_message(__FUNCTION__, "\
999 One single item of all the items of a given type in the Tux inventory\n\
1000 should be removed, but there was not even one such item ever found in\n\
1001 Tux inventory.  Something must have gone awry...", PLEASE_INFORM | IS_FATAL);
1002 
1003 };				// void DeleteOneInventoryItemsOfType( int Itemtype  )
1004 
MouseCursorIsInSkiERect(int x,int y)1005 static int MouseCursorIsInSkiERect(int x, int y)
1006 {
1007 	if (x > 320 || x < 0)
1008 		return FALSE;
1009 	if (y > 480 || y < 0)
1010 		return FALSE;
1011 
1012 	return TRUE;
1013 }
1014 
1015 /**
1016  * This function checks if a given screen position lies within the inventory
1017  * rectangle or not.
1018  */
MouseCursorIsInInvRect(int x,int y)1019 int MouseCursorIsInInvRect(int x, int y)
1020 {
1021 	if (!GameConfig.Inventory_Visible)
1022 		return FALSE;
1023 	if (x > InventoryRect.x + InventoryRect.w)
1024 		return (FALSE);
1025 	if (x < InventoryRect.x)
1026 		return (FALSE);
1027 	if (y > InventoryRect.y + InventoryRect.h)
1028 		return (FALSE);
1029 	if (y < InventoryRect.y)
1030 		return (FALSE);
1031 	return TRUE;
1032 };				// int MouseCursorIsInInvRect( int x , int y )
1033 
1034 /**
1035  * This function checks if a given screen position lies within the character
1036  * rectangle or not.
1037  */
MouseCursorIsInChaRect(int x,int y)1038 int MouseCursorIsInChaRect(int x, int y)
1039 {
1040 	if (!GameConfig.CharacterScreen_Visible)
1041 		return FALSE;
1042 	if (x > CharacterRect.x + CharacterRect.w)
1043 		return (FALSE);
1044 	if (x < CharacterRect.x)
1045 		return (FALSE);
1046 	if (y > CharacterRect.y + CharacterRect.h)
1047 		return (FALSE);
1048 	if (y < CharacterRect.y)
1049 		return (FALSE);
1050 	return TRUE;
1051 };				// int MouseCursorIsInChaRect( int x , int y )
1052 
1053 /**
1054  * This function checks if a given screen position lies within the skill
1055  * rectangle or not.
1056  */
MouseCursorIsInSkiRect(int x,int y)1057 int MouseCursorIsInSkiRect(int x, int y)
1058 {
1059 	if (!GameConfig.SkillScreen_Visible)
1060 		return FALSE;
1061 	if (x > SkillScreenRect.x + SkillScreenRect.w)
1062 		return (FALSE);
1063 	if (x < SkillScreenRect.x)
1064 		return (FALSE);
1065 	if (y > SkillScreenRect.y + SkillScreenRect.h)
1066 		return (FALSE);
1067 	if (y < SkillScreenRect.y)
1068 		return (FALSE);
1069 	return TRUE;
1070 };				// int MouseCursorIsInSkiRect( int x , int y )
1071 
1072 /**
1073  * This function checks if a given screen position lies within the grid
1074  * where the inventory of the player is usually located or not.
1075  */
MouseCursorIsInInventoryGrid(int x,int y)1076 int MouseCursorIsInInventoryGrid(int x, int y)
1077 {
1078 	if (!GameConfig.Inventory_Visible)
1079 		return FALSE;
1080 	if ((x >= INVENTORY_RECT_X) && (x <= INVENTORY_RECT_X + INVENTORY_GRID_WIDTH * INV_SUBSQUARE_WIDTH)) {
1081 		if ((y >= User_Rect.y + INVENTORY_RECT_Y) &&
1082 		    (y <= User_Rect.y + INVENTORY_RECT_Y + INV_SUBSQUARE_HEIGHT * INVENTORY_GRID_HEIGHT)) {
1083 			return TRUE;
1084 		}
1085 	}
1086 	return (FALSE);
1087 };				// int MouseCursorIsInInventoryGrid( int x , int y )
1088 
1089 /**
1090  * This function checks if a given screen position lies within the user
1091  * i.e. combat rectangle or not.
1092  */
MouseCursorIsInUserRect(int x,int y)1093 int MouseCursorIsInUserRect(int x, int y)
1094 {
1095 	// no interaction with the game when the world is frozen
1096 	if (world_frozen())
1097 		return FALSE;
1098 
1099 	if (y < User_Rect.y)
1100 		return (FALSE);
1101 	if (y > User_Rect.y + User_Rect.h)
1102 		return (FALSE);
1103 
1104 	if ((!GameConfig.Inventory_Visible) && (!GameConfig.CharacterScreen_Visible) && (!GameConfig.SkillScreen_Visible)) {
1105 		if (x > User_Rect.x + User_Rect.w)
1106 			return (FALSE);
1107 		if (x < User_Rect.x)
1108 			return (FALSE);
1109 		return TRUE;
1110 	}
1111 	if ((GameConfig.Inventory_Visible && MouseCursorIsInInvRect(x, y))
1112 	    || (GameConfig.CharacterScreen_Visible && MouseCursorIsInChaRect(x, y)) || (GameConfig.SkillScreen_Visible
1113 											&& MouseCursorIsInSkiRect(x, y))
1114 	    || (GameConfig.skill_explanation_screen_visible && MouseCursorIsInSkiERect(x, y)))
1115 		return FALSE;
1116 	return TRUE;
1117 };				// int MouseCursorIsInUserRect( int x , int y )
1118 
1119 /**
1120  * This function gives the x coordinate of the inventory square that
1121  * corresponds to the mouse cursor location given to the function.
1122  */
GetInventorySquare_x(int x)1123 int GetInventorySquare_x(int x)
1124 {
1125 	return ((x - INVENTORY_RECT_X) / INV_SUBSQUARE_WIDTH);
1126 };				// int GetInventorySquare_x( x )
1127 
1128 /**
1129  * This function gives the y coordinate of the inventory square that
1130  * corresponds to the mouse cursor location given to the function.
1131  */
GetInventorySquare_y(int y)1132 int GetInventorySquare_y(int y)
1133 {
1134 	return ((y - (User_Rect.y + INVENTORY_RECT_Y)) / INV_SUBSQUARE_HEIGHT);
1135 };				// int GetInventorySquare_y( y )
1136 
1137 /**
1138  * This function checks if a given item type could be dropped into the
1139  * inventory grid at location x y.  Only the space is taken into account
1140  * and if other items block the way or not.
1141  */
ItemCanBeDroppedInInv(int ItemType,int InvPos_x,int InvPos_y)1142 int ItemCanBeDroppedInInv(int ItemType, int InvPos_x, int InvPos_y)
1143 {
1144 	int item_height;
1145 	int item_width;
1146 
1147 	// Perhaps the item reaches even outside the inventory grid.  Then of course
1148 	// it does not fit and we need/should not even test the details...
1149 	//
1150 	if (InvPos_x < 0 || InvPos_y < 0)
1151 		return FALSE;
1152 	if (ItemMap[ItemType].inv_size.x - 1 + InvPos_x >= INVENTORY_GRID_WIDTH)
1153 		return (FALSE);
1154 	if (ItemMap[ItemType].inv_size.y - 1 + InvPos_y >= INVENTORY_GRID_HEIGHT)
1155 		return (FALSE);
1156 
1157 	// Now that we know, that the desired position is at least inside the inventory
1158 	// grid, we can start to test for the details of the available inventory space
1159 	//
1160 	for (item_height = 0; item_height < ItemMap[ItemType].inv_size.y; item_height++) {
1161 		for (item_width = 0; item_width < ItemMap[ItemType].inv_size.x; item_width++) {
1162 			if (!Inv_Pos_Is_Free(InvPos_x + item_width, InvPos_y + item_height))
1163 				return (FALSE);
1164 		}
1165 	}
1166 	return TRUE;
1167 
1168 };				// int ItemCanBeDroppedInInv ( int ItemType , int InvPos_x , int InvPos_y )
1169 
1170 /**
1171  * Find a free index in the item array of the drop level.
1172  */
find_free_floor_index(level * drop_level)1173 static int find_free_floor_index(level* drop_level)
1174 {
1175 	int i;
1176 	for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
1177 		if (drop_level->ItemList[i].type == -1) {
1178 			return i;
1179 		}
1180 	}
1181 
1182 	// We did not find a free index.
1183 	error_message(__FUNCTION__, "The item array for level %d was full.",
1184 	            PLEASE_INFORM, drop_level->levelnum);
1185 	return -1;
1186 }
1187 
1188 /**
1189  * Drop an item to the floor in the given location.  No checks are done to
1190  * verify this location is unobstructed or otherwise reasonable.
1191  */
drop_item(item * item_pointer,float x,float y,int level_num)1192 item *drop_item(item *item_pointer, float x, float y, int level_num)
1193 {
1194 	level *drop_level = curShip.AllLevels[level_num];
1195 
1196 	int index = find_free_floor_index(drop_level);
1197 
1198 	// Cancel the drop if we did not find an empty index to use.
1199 	if (index == -1) {
1200 		return NULL;
1201 	}
1202 
1203 	// Create the item
1204 	init_item(&(drop_level->ItemList[index]));
1205 	MoveItem(item_pointer, &(drop_level->ItemList[index]));
1206 
1207 	// Place item on level
1208 	drop_level->ItemList[index].inventory_position.x = -1;
1209 	drop_level->ItemList[index].inventory_position.y = -1;
1210 	drop_level->ItemList[index].pos.x = x;
1211 	drop_level->ItemList[index].pos.y = y;
1212 	drop_level->ItemList[index].pos.z = level_num;
1213 	drop_level->ItemList[index].throw_time = 0.01;  // something > 0
1214 
1215 	timeout_from_item_drop = 0.4;
1216 
1217 	if (item_pointer == item_held_in_hand)
1218 		item_held_in_hand = NULL;
1219 
1220 	return &(drop_level->ItemList[index]);
1221 }
1222 
1223 /**
1224  * Drop held item to the floor.
1225  *
1226  * Before calling this function, make sure item_held_in_hand != NULL
1227  */
drop_held_item(void)1228 static void drop_held_item(void)
1229 {
1230 	float x = translate_pixel_to_map_location(input_axis.x, input_axis.y, TRUE);
1231 	float y = translate_pixel_to_map_location(input_axis.x, input_axis.y, FALSE);
1232 
1233 	gps pos = { x, y, Me.pos.z };
1234 	gps rpos;
1235 
1236 	/* The vector from Tux to the map position where the player clicked */
1237 	float ax = x - Me.pos.x;
1238 	float ay = y - Me.pos.y;
1239 	/* The length of mentioned vector */
1240 	float length = sqrt(ax * ax + ay * ay);
1241 	/* The vector with the same direction but length ITEM_TAKE_DIST */
1242 	float ux = ITEM_TAKE_DIST * (ax / length);
1243 	float uy = ITEM_TAKE_DIST * (ay / length);
1244 
1245 	/* Don't let the player drop the item farther than ITEM_TAKE_DIST */
1246 	if (length > ITEM_TAKE_DIST) {
1247 		pos.x = Me.pos.x + ux;
1248 		pos.y = Me.pos.y + uy;
1249 	}
1250 
1251 	/* If we have an invalid drop position, attempt positions closer to Tux
1252 	 * until we have a good position. */
1253 	colldet_filter margin = WalkableWithMarginPassFilter;
1254 	margin.extra_margin = 0.2;
1255 	int collision;
1256 	while ((collision = !(resolve_virtual_position(&rpos, &pos) &&
1257 						  DirectLineColldet(Me.pos.x, Me.pos.y, pos.x, pos.y, Me.pos.z, &margin)))
1258 		   && sqrt(ux * ux + uy * uy) > 0.1) // length > 0.1
1259 	{
1260 		ux *= 0.9;
1261 		uy *= 0.9;
1262 		pos.x = Me.pos.x + ux;
1263 		pos.y = Me.pos.y + uy;
1264 	}
1265 
1266 	// Fall back to Tux's feet if the position is still invalid
1267 	if (collision) {
1268 		rpos.x = Me.pos.x;
1269 		rpos.y = Me.pos.y;
1270 		rpos.z = Me.pos.z;
1271 	}
1272 
1273 	// Finally, drop the item
1274 	drop_item(item_held_in_hand, rpos.x, rpos.y, rpos.z);
1275 	item_held_in_hand = NULL;
1276 	timeout_from_item_drop = 0.4;
1277 }
1278 
1279 /**
1280  * This function checks if the usage requirements for a given item are
1281  * met by the influencer or not.
1282  */
ItemUsageRequirementsMet(item * UseItem,int MakeSound)1283 int ItemUsageRequirementsMet(item * UseItem, int MakeSound)
1284 {
1285 	if (Me.strength < ItemMap[UseItem->type].item_require_strength && ItemMap[UseItem->type].item_require_strength > 0) {
1286 		if (MakeSound)
1287 			Not_Enough_Power_Sound();
1288 		return (FALSE);
1289 	}
1290 	if (Me.dexterity < ItemMap[UseItem->type].item_require_dexterity && ItemMap[UseItem->type].item_require_dexterity > 0) {
1291 		if (MakeSound)
1292 			Not_Enough_Dist_Sound();
1293 		return (FALSE);
1294 	}
1295 	if (Me.cooling < ItemMap[UseItem->type].item_require_cooling && ItemMap[UseItem->type].item_require_cooling > 0) {
1296 		return (FALSE);
1297 	}
1298 	return TRUE;
1299 };				// int ItemUsageRequirementsMet( item* UseItem )
1300 
1301 /**
1302  * This function checks, if the influencer mets the requirements of the
1303  * item currently held in hand by the player/influencer.  Which item this
1304  * is will be found out by the function.
1305  */
HeldItemUsageRequirementsMet(void)1306 static int HeldItemUsageRequirementsMet(void)
1307 {
1308 	// Check validity of HeldItem
1309 	if (item_held_in_hand == NULL) {
1310 		DebugPrintf(0, "\nvoid HeldItemUsageRequirementsMet ( void ) : No item in inventory seems to be currently held in hand...");
1311 		return (FALSE);
1312 	}
1313 
1314 	return (ItemUsageRequirementsMet(item_held_in_hand, TRUE));
1315 };				// int HeldItemUsageRequirementsMet( void )
1316 
1317 /**
1318  * This function installs an item into a slot.  The given parameter is
1319  * only the slot where this item should be installed.  The source item
1320  * will be found out from inside this function.  Very convenient.
1321  */
DropHeldItemToSlot(item * SlotItem)1322 static void DropHeldItemToSlot(item * SlotItem)
1323 {
1324 	item *DropItemPointer; // temporary storage
1325 
1326 	// Chech validity of held item
1327 	if (item_held_in_hand == NULL) {
1328 		DebugPrintf(0, "\nvoid DropHeldItemToSlot ( void ) : No item in inventory seems to be currently held in hand...");
1329 		return;
1330 	}
1331 
1332 	// If there is an old item in the slot, we make a held item on the
1333 	// floor out of it and also set the HeldItemType accordingly, so that
1334 	// after the new item was placed successfully, the old item will
1335 	// be out of all inventory slots, but still in the hand of the
1336 	// player and ready to be put somewhere else
1337 	//
1338 	// But this may only be done of course, if the 'old item' is not
1339 	// the item we want to put there itself!!!!  HAHAHAHA!!!!
1340 	//
1341 	DropItemPointer = item_held_in_hand;
1342 	if ((SlotItem->type != (-1)) && (item_held_in_hand != SlotItem))
1343 		MakeHeldFloorItemOutOf(SlotItem);
1344 	else
1345 		item_held_in_hand = NULL;
1346 
1347 	// Move the item to the slot and mark it as no longer grabbed.
1348 	MoveItem(DropItemPointer, SlotItem);
1349 	play_item_sound(SlotItem->type, &Me.pos);
1350 }
1351 
1352 /**
1353  * This function looks for a free inventory index.  Since there are more
1354  * inventory indices than squares in the inventory grid, the function
1355  * should always be able to find a free inventory index.  If not, this is
1356  * considered a severe program error, which will cause immediate
1357  * termination of FreedroidRPG.
1358  */
GetFreeInventoryIndex(void)1359 int GetFreeInventoryIndex(void)
1360 {
1361 	int InvPos;
1362 
1363 	// We find out the first free inventory index:
1364 	//
1365 	for (InvPos = 0; InvPos < MAX_ITEMS_IN_INVENTORY - 1; InvPos++) {
1366 		if (Me.Inventory[InvPos].type == (-1)) {
1367 			return (InvPos);
1368 		}
1369 	}
1370 
1371 	// If this point is reached, the severe error mentioned above has
1372 	// occurred, an error message must be printed out and the program
1373 	// must be terminated.
1374 	//
1375 	error_message(__FUNCTION__, "\
1376 A FREE INVENTORY INDEX POSITION COULD NOT BE FOUND.\n\
1377 This is an internal error, that must never happen unless there are\n\
1378 severe bugs in the inventory system.", PLEASE_INFORM | IS_FATAL);
1379 	return (-1);		// just to make compilers happy.
1380 };				// int GetFreeInventoryIndex( void )
1381 
1382 /**
1383  * If an item is held and then clicked again in the inventory field, this
1384  * item should be dropped into the inventory field, provided there is room
1385  * enough in it at that location.  If that is the case, then the item is
1386  * dropped onto this inventory location, else nothing is done.
1387  */
DropHeldItemToInventory(void)1388 void DropHeldItemToInventory(void)
1389 {
1390 	point CurPos;
1391 	int FreeInvIndex;
1392 	int i;
1393 	FreeInvIndex = GetFreeInventoryIndex();
1394 
1395 	// First we check validity of held item
1396 	//
1397 	if (item_held_in_hand == NULL) {
1398 		DebugPrintf(0, "\nvoid DropHeldItemToInventory ( void ) : No item in inventory seems to be currently held in hand...");
1399 		return;
1400 	}
1401 
1402 	// Now we want to drop the item to the right location again.
1403 	// Therefore we need to find out the right position, which of course
1404 	// depends as well on current mouse cursor location as well as the
1405 	// size of the dropped item.
1406 	//
1407 	CurPos.x = GetMousePos_x() - (16 * (ItemMap[item_held_in_hand->type].inv_size.x - 1));
1408 	CurPos.y = GetMousePos_y() - (16 * (ItemMap[item_held_in_hand->type].inv_size.y - 1));
1409 
1410 	if (ItemCanBeDroppedInInv(item_held_in_hand->type, GetInventorySquare_x(CurPos.x), GetInventorySquare_y(CurPos.y))) {
1411 		CopyItem(item_held_in_hand, &(Me.Inventory[FreeInvIndex]));
1412 		play_item_sound(item_held_in_hand->type, &Me.pos);
1413 		Me.Inventory[FreeInvIndex].inventory_position.x = GetInventorySquare_x(CurPos.x);
1414 		Me.Inventory[FreeInvIndex].inventory_position.y = GetInventorySquare_y(CurPos.y);
1415 
1416 		// Now that we know that the item could be dropped directly to inventory
1417 		// without swapping any spaces, we can as well make the item
1418 		// 'not held in hand' immediately and return
1419 		//
1420 		DeleteItem(item_held_in_hand);
1421 		item_held_in_hand = NULL;
1422 		return;
1423 	} else {
1424 		// So the item could not be placed into inventory directly, but maybe
1425 		// it can be placed there if we swap our dropitem with some other item.
1426 		// Let's test this opportunity here.
1427 		//
1428 		for (i = 0; i < MAX_ITEMS_IN_INVENTORY - 1; i++) {
1429 			// FIRST: Security check against segfaults:  It might happen that we
1430 			// delete the Dropitem itself while trying several items as candidates
1431 			// for removal.  This would cause testing dropability with a -1 item
1432 			// type and a SEGFAULT would result...
1433 			//
1434 			if (&(Me.Inventory[i]) == item_held_in_hand)
1435 				continue;
1436 
1437 			// So we make a copy of each of the items we remove in order to
1438 			// try to create new space for the drop item.  After that, we can
1439 			// remove it.
1440 			//
1441 			CopyItem(&(Me.Inventory[i]), &(Me.Inventory[MAX_ITEMS_IN_INVENTORY - 1]));
1442 			Me.Inventory[i].type = (-1);
1443 
1444 			if (ItemCanBeDroppedInInv(item_held_in_hand->type, GetInventorySquare_x(CurPos.x), GetInventorySquare_y(CurPos.y))) {
1445 
1446 				// Copy the HelItem to the now free position
1447 				CopyItem(item_held_in_hand, &(Me.Inventory[FreeInvIndex]));
1448 				play_item_sound(item_held_in_hand->type, &Me.pos);
1449 				Me.Inventory[FreeInvIndex].inventory_position.x = GetInventorySquare_x(CurPos.x);
1450 				Me.Inventory[FreeInvIndex].inventory_position.y = GetInventorySquare_y(CurPos.y);
1451 				DeleteItem(item_held_in_hand);
1452 
1453 				// The removed item Nr. i is put in hand in replacement of the
1454 				// prior HeldItem.
1455 				MakeHeldFloorItemOutOf(&(Me.Inventory[MAX_ITEMS_IN_INVENTORY - 1]));
1456 
1457 				return;
1458 			}
1459 
1460 			// But if even the removal of one item was not enough, so that the new
1461 			// item would fit into the inventory, then of course we should re-add the
1462 			// removed item to the inventory, so that no other items get lost.
1463 			//
1464 			CopyItem(&(Me.Inventory[MAX_ITEMS_IN_INVENTORY - 1]), &(Me.Inventory[i]));
1465 
1466 		}		// for: try all items if removal is the solution
1467 	}			// if not immediately place findable
1468 };				// void DropHeldItemToInventory( void )
1469 
1470 /**
1471  *
1472  *
1473  */
get_floor_item_index_under_mouse_cursor(level ** item_lvl)1474 int get_floor_item_index_under_mouse_cursor(level **item_lvl)
1475 {
1476 	gps mouse_pos;
1477 	int i;
1478 
1479 	// no interaction with the game when the world is frozen
1480 	if (world_frozen())
1481 		return -1;
1482 
1483 	// In the case that X was pressed, we don't use the item positions but rather
1484 	// we use the item slot rectangles from the item texts.
1485 	//
1486 	if (XPressed() || GameConfig.show_item_labels) {
1487 		struct visible_level *vis_lvl, *n;
1488 
1489 		BROWSE_VISIBLE_LEVELS(vis_lvl, n) {
1490 			level *lvl = vis_lvl->lvl_pointer;
1491 
1492 			for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
1493 				if (lvl->ItemList[i].type == (-1))
1494 					continue;
1495 
1496 				if (MouseCursorIsInRect(&(lvl->ItemList[i].text_slot_rectangle), GetMousePos_x(), GetMousePos_y())) {
1497 					*item_lvl = lvl;
1498 					return (i);
1499 				}
1500 			}
1501 		}
1502 	}
1503 	// If no X was pressed, we only use the floor position the mouse
1504 	// has pointed to and see if we can find an item that has geographically
1505 	// that very same (or a similar enough) position.
1506 	//
1507 	else {
1508 		mouse_pos.x = translate_pixel_to_map_location(input_axis.x, input_axis.y, TRUE);
1509 		mouse_pos.y = translate_pixel_to_map_location(input_axis.x, input_axis.y, FALSE);
1510 		mouse_pos.z = Me.pos.z;
1511 
1512 		gps virt_mouse_pos;
1513 		struct visible_level *vis_lvl, *n;
1514 
1515 		BROWSE_VISIBLE_LEVELS(vis_lvl, n) {
1516 
1517 			level *lvl = vis_lvl->lvl_pointer;
1518 			update_virtual_position(&virt_mouse_pos, &mouse_pos, lvl->levelnum);
1519 
1520 			for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
1521 				if (lvl->ItemList[i].type == (-1))
1522 					continue;
1523 
1524 				if ((fabsf(virt_mouse_pos.x - lvl->ItemList[i].pos.x) < 0.5) &&
1525 					(fabsf(virt_mouse_pos.y - lvl->ItemList[i].pos.y) < 0.5)) {
1526 					*item_lvl = lvl;
1527 					return (i);
1528 				}
1529 			}
1530 		}
1531 	}
1532 
1533 	return (-1);
1534 }
1535 
1536 /**
1537  * Handle inventory screen and related things: interact with items in inventory
1538  * grid, in inventory slots and in hand. Also handle dropping items in hand and
1539  * apply (right click) items.
1540  */
HandleInventoryScreen(void)1541 void HandleInventoryScreen(void)
1542 {
1543 	point CurPos;
1544 
1545 	struct {
1546 		int buttonidx;
1547 		item *slot;
1548 	} allslots[] = {		/*list of all slots and their associated item */
1549 			{ WEAPON_RECT_BUTTON, &(Me.weapon_item) },
1550 			{ DRIVE_RECT_BUTTON, &(Me.drive_item) },
1551 		   	{ SHIELD_RECT_BUTTON, &(Me.shield_item) },
1552 			{ ARMOUR_RECT_BUTTON, &(Me.armour_item) },
1553 			{ HELMET_RECT_BUTTON, &(Me.special_item) },
1554 	};
1555 
1556 	if (Me.energy <= 0) {
1557 		return;
1558 	}
1559 
1560 	// If the inventory is not visible there is nothing to do
1561 	if (GameConfig.Inventory_Visible == FALSE) {
1562 		item_held_in_hand = NULL;
1563 		return;
1564 	}
1565 
1566 	// We will need the current mouse position on several spots...
1567 	CurPos.x = GetMousePos_x();
1568 	CurPos.y = GetMousePos_y();
1569 
1570 	// Case 1: The user left-clicks while not holding an item
1571 	if (MouseLeftClicked() && item_held_in_hand == NULL) {
1572 
1573 		// Forbid using the inventory while paralyzed
1574 		if (Me.paralyze_duration) {
1575 			append_new_game_message(_("You can not use the inventory while paralyzed."));
1576 			return;
1577 		}
1578 
1579 		// Case 1.1: The user left-clicks on the inventory grid
1580 		if (MouseCursorIsInInventoryGrid(CurPos.x, CurPos.y)) {
1581 			point Inv_GrabLoc;
1582 			int Grabbed_InvPos;
1583 
1584 			Inv_GrabLoc.x = GetInventorySquare_x(CurPos.x);
1585 			Inv_GrabLoc.y = GetInventorySquare_y(CurPos.y);
1586 			Grabbed_InvPos = GetInventoryItemAt(Inv_GrabLoc.x, Inv_GrabLoc.y);
1587 
1588 			if (Grabbed_InvPos == (-1)) {
1589 				/* No item under the cursor */
1590 				return;
1591 			}
1592 
1593 			// At this point we know, that we have just grabbed something from the inventory
1594 			// So we set, that something should be displayed in the 'hand', and it should of
1595 			// course be the image of the item grabbed from inventory.
1596 			item_held_in_hand = &(Me.Inventory[Grabbed_InvPos]);
1597 			play_item_sound(item_held_in_hand->type, &Me.pos);
1598 
1599 			return;
1600 		}
1601 
1602 		// Case 1.2: The user left-clicks on one of the equipment slots
1603 		unsigned int i;
1604 
1605 		for (i = 0; i < sizeof(allslots) / sizeof(allslots[0]); i++) {
1606 			if (MouseCursorIsOnButton(allslots[i].buttonidx, CurPos.x, CurPos.y)) {
1607 				if (allslots[i].slot->type > 0) {
1608 					MakeHeldFloorItemOutOf(allslots[i].slot);
1609 					return;
1610 				}
1611 			}
1612 		}
1613 
1614 		// Case 1.3: The user left-clicks on an item on the floor
1615 		int item_idx;
1616 		level *item_lvl = NULL;
1617 
1618 		if (MouseCursorIsInUserRect(GetMousePos_x(), GetMousePos_y())
1619 		   && (item_idx = get_floor_item_index_under_mouse_cursor(&item_lvl)) != -1) {
1620 			// Try to auto-put or auto-equip the item. If it's not possible,
1621 			// the item will be 'put in hand'.
1622 			if (check_for_items_to_pickup(item_lvl, item_idx)) {
1623 				item *it = &item_lvl->ItemList[item_idx];
1624 				if (!try_give_item(it)) {
1625 					item_held_in_hand = it;
1626 				}
1627 			}
1628 			return;
1629 		}
1630 
1631 		// No item was picked
1632 		return;
1633 	}
1634 
1635 	// Case 2: The user left-clicks somewhere to drop a held item
1636 	//
1637 	if (MouseLeftClicked() && (item_held_in_hand != NULL)) {
1638 
1639 		// The left-click is on the inventory grid -> we must see if
1640 		// the item was dropped onto a correct inventory location and
1641 		// should from then on not only no longer be in the players
1642 		// hand but also remain at the newly assigned position.
1643 		if (MouseCursorIsInInventoryGrid(CurPos.x, CurPos.y)) {
1644 			DropHeldItemToInventory();
1645 			return;
1646 		}
1647 
1648 		// The user left-clicks in the weapon's equipment slot or the bottom left corner
1649 		// HUD weapon display.
1650 		if (MouseCursorIsOnButton(WEAPON_RECT_BUTTON, CurPos.x, CurPos.y)
1651 			|| MouseCursorIsOnButton(WEAPON_MODE_BUTTON, CurPos.x, CurPos.y)) {
1652 
1653 			// Check if the item can be installed in the weapon slot
1654 			if (ItemMap[item_held_in_hand->type].slot != WEAPON_SLOT) {
1655 				append_new_game_message(_("You cannot fight with this!"));
1656 				return;
1657 			}
1658 
1659 			// Check if the user has enough skill to use the weapon
1660 			if (!HeldItemUsageRequirementsMet()) {
1661 				append_new_game_message(_("You cannot yet fight with this!"));
1662 				return;
1663 			}
1664 
1665 			// Now a weapon is about to be dropped to the weapons rectangle and obviously
1666 			// the stat requirements for usage are met.  But maybe this is a 2-handed weapon.
1667 			// In this case we need to do some extra check.  If it isn't a 2-handed weapon,
1668 			// then we can just go ahead and equip the item
1669 			if (!ItemMap[item_held_in_hand->type].weapon_needs_two_hands) {
1670 				DropHeldItemToSlot(&(Me.weapon_item));
1671 				return;
1672 			}
1673 
1674 			// So, this is a 2-handed weapon. If the shield slot is just empty,
1675 			// that makes matters a lot simpler, because then we can just drop
1676 			// this 2-handed weapon to the weapon slot and all is fine, because
1677 			//no conflicts will result...
1678 			if (Me.shield_item.type == (-1)) {
1679 				DropHeldItemToSlot(&(Me.weapon_item));
1680 				return;
1681 			}
1682 
1683 			// But if there is something in the shield slot too, then we need to be
1684 			// a bit more sophisticated and either swap the 2-handed item in for just
1685 			// the shield alone, which then will be held OR we need to refuse completely
1686 			// because there might be a weapon AND a shield equipped already.
1687 			if (Me.weapon_item.type == (-1)) {
1688 				// first of all check requirements again but without the shield :
1689 				// virtually remove the shield, compute requirements, if
1690 				// everything is okay, proceed otherwise we inform the player
1691 				int shield_item_type = Me.shield_item.type;
1692 				Me.shield_item.type = (-1);
1693 				update_all_primary_stats();
1694 				if (HeldItemUsageRequirementsMet()) {
1695 					DropHeldItemToSlot(&(Me.weapon_item));
1696 					Me.shield_item.type = shield_item_type;
1697 					MakeHeldFloorItemOutOf(&(Me.shield_item));
1698 				} else {
1699 					append_new_game_message(
1700 							_("Two-handed weapon requirements not met: shield bonus doesn't count."));
1701 					Me.shield_item.type = shield_item_type;
1702 				}
1703 			} else {
1704 				play_sound("effects/tux_ingame_comments/ThisItemRequiresBothHands.ogg");
1705 			}
1706 		}
1707 
1708 		// The user left-clicks in the shield's equipment slot
1709 		if (MouseCursorIsOnButton(SHIELD_RECT_BUTTON, CurPos.x, CurPos.y)) {
1710 
1711 			// Check if the item can be installed in the shield slot
1712 			if (ItemMap[item_held_in_hand->type].slot != SHIELD_SLOT) {
1713 				append_new_game_message(_("You cannot equip this!"));
1714 				return;
1715 			}
1716 
1717 			// Check if the user has enough skill to use the shield
1718 			if (!HeldItemUsageRequirementsMet()) {
1719 				append_new_game_message(_("You cannot equip this yet!"));
1720 				return;
1721 			}
1722 
1723 			// Now if there isn't any weapon equipped right now, the matter
1724 			// is rather simple and we just need to do the normal drop-to-slot-thing.
1725 			if (Me.weapon_item.type == (-1)) {
1726 				DropHeldItemToSlot(&(Me.shield_item));
1727 				return;
1728 			}
1729 
1730 			// A shield, when equipped, will push out any 2-handed weapon currently
1731 			// equipped from it's weapon slot.... So first check if a 2-handed
1732 			// weapon is equipped.
1733 			if (!ItemMap[Me.weapon_item.type].weapon_needs_two_hands) {
1734 				DropHeldItemToSlot(&(Me.shield_item));
1735 				return;
1736 			}
1737 
1738 			// There is a 2-handed weapon equipped, so first of all check
1739 			// requirements again but without the weapon :
1740 			// virtually remove the weapon, compute requirements, if
1741 			// everything is okay, proceed otherwise we inform the player
1742 			int weapon_item_type = Me.weapon_item.type;
1743 			Me.weapon_item.type = (-1);
1744 			update_all_primary_stats();
1745 			if (HeldItemUsageRequirementsMet()) {
1746 				DropHeldItemToSlot(&(Me.shield_item));
1747 				Me.weapon_item.type = weapon_item_type;
1748 				MakeHeldFloorItemOutOf(&(Me.weapon_item));
1749 			} else {
1750 				append_new_game_message(_
1751 							("Shield requirements not met: two-handed weapon bonus doesn't count."));
1752 				Me.weapon_item.type = weapon_item_type;
1753 			}
1754 		}
1755 
1756 		// The user left-clicks in another equipment slot
1757 		itemspec *tocheck = &ItemMap[item_held_in_hand->type];
1758 		struct {
1759 			int btnidx;
1760 			char propcheck;
1761 			item *slot;
1762 		} dropslots[] = { {
1763 		DRIVE_RECT_BUTTON, (tocheck->slot == BOOT_SLOT), &(Me.drive_item)}, {
1764 		ARMOUR_RECT_BUTTON, (tocheck->slot == ARMOR_SLOT), &(Me.armour_item)}, {
1765 		HELMET_RECT_BUTTON, (tocheck->slot == HELM_SLOT), &(Me.special_item)},};
1766 		int i;
1767 		for (i = 0; i < sizeof(dropslots) / sizeof(dropslots[0]); i++) {
1768 			if (MouseCursorIsOnButton(dropslots[i].btnidx, CurPos.x, CurPos.y) && dropslots[i].propcheck
1769 			    && HeldItemUsageRequirementsMet()) {
1770 				DropHeldItemToSlot(dropslots[i].slot);
1771 				return;
1772 			}
1773 		}
1774 
1775 		// The user left-clicks in the "UserRect" -> the item should
1776 		// be dropped to the floor
1777 		if (MouseCursorIsInUserRect(CurPos.x, CurPos.y)) {
1778 			drop_held_item();
1779 			return;
1780 		}
1781 
1782 		// The left-click did not lead to anything useful
1783 		return;
1784 	}
1785 
1786 	// Case 3: The user is right-clicking inside the inventory rectangle which
1787 	// would mean for us that he is applying the item under the mouse button
1788 	//
1789 	if (MouseRightClicked()) {
1790 
1791 		if (Me.readied_skill == get_program_index_with_name("Repair equipment") && Me.busy_time <= 0) {
1792 			// Here we know, that the repair skill is selected, therefore we try to
1793 			// repair the item currently under the mouse cursor.
1794 			//
1795 			if (MouseCursorIsInInventoryGrid(CurPos.x, CurPos.y)) {
1796 				point Inv_GrabLoc;
1797 				int Grabbed_InvPos;
1798 
1799 				Inv_GrabLoc.x = GetInventorySquare_x(CurPos.x);
1800 				Inv_GrabLoc.y = GetInventorySquare_y(CurPos.y);
1801 
1802 				DebugPrintf(0, "\nTrying to repair item at inv-pos: %d %d.", Inv_GrabLoc.x, Inv_GrabLoc.y);
1803 
1804 				Grabbed_InvPos = GetInventoryItemAt(Inv_GrabLoc.x, Inv_GrabLoc.y);
1805 				DebugPrintf(0, "\nTrying to repair inventory entry no.: %d.", Grabbed_InvPos);
1806 
1807 				if (Grabbed_InvPos == (-1)) {
1808 					// Nothing grabbed, so we need not do anything more here..
1809 					DebugPrintf(0, "\nRepairing in INVENTORY grid FAILED:  NO ITEM AT THIS POSITION FOUND!");
1810 				} else {
1811 					if (Me.Inventory[Grabbed_InvPos].max_durability != -1)
1812 						self_repair_item(&(Me.Inventory[Grabbed_InvPos]));
1813 					else
1814 						apply_item(&(Me.Inventory[Grabbed_InvPos]));
1815 				}
1816 			} else {
1817 				int i;
1818 				for (i = 0; i < sizeof(allslots) / sizeof(allslots[0]); i++) {
1819 					if (MouseCursorIsOnButton(allslots[i].buttonidx, CurPos.x, CurPos.y)
1820 					    && allslots[i].slot->type != -1) {
1821 						self_repair_item(allslots[i].slot);
1822 						break;
1823 					}
1824 				}
1825 			}
1826 		} else {
1827 			if (MouseCursorIsInInventoryGrid(CurPos.x, CurPos.y)) {
1828 				point Inv_GrabLoc;
1829 				int Grabbed_InvPos;
1830 
1831 				Inv_GrabLoc.x = GetInventorySquare_x(CurPos.x);
1832 				Inv_GrabLoc.y = GetInventorySquare_y(CurPos.y);
1833 
1834 				Grabbed_InvPos = GetInventoryItemAt(Inv_GrabLoc.x, Inv_GrabLoc.y);
1835 
1836 				if ((Grabbed_InvPos != -1) && (&(Me.Inventory[Grabbed_InvPos]) != item_held_in_hand)) {
1837 					// At this point we know, that we have just applied something from the inventory
1838 					apply_item(&(Me.Inventory[Grabbed_InvPos]));
1839 				}
1840 			}
1841 		}
1842 	}
1843 }
1844 
1845 /**
1846  *
1847  *
1848  */
raw_move_picked_up_item_to_entry(item * ItemPointer,item * TargetPointer,point Inv_Loc)1849 static void raw_move_picked_up_item_to_entry(item * ItemPointer, item * TargetPointer, point Inv_Loc)
1850 {
1851 	// We add the new item to the inventory
1852 	CopyItem(ItemPointer, TargetPointer);
1853 	TargetPointer->inventory_position.x = Inv_Loc.x;
1854 	TargetPointer->inventory_position.y = Inv_Loc.y;
1855 
1856 	// We make the sound of an item being taken
1857 	// PlayItemSound( ItemMap[ ItemPointer->type ].sound_number );
1858 	play_item_sound(ItemPointer->type, &Me.pos);
1859 
1860 	DeleteItem(ItemPointer);
1861 };				// void move_picked_up_item_to_entry ( ItemPointer , TargetPointer )
1862 
1863 /**
1864  *
1865  */
place_item_on_this_position_if_you_can(item * ItemPointer,point Inv_Loc,int InvPos)1866 static int place_item_on_this_position_if_you_can(item * ItemPointer, point Inv_Loc, int InvPos)
1867 {
1868 	int item_height;
1869 	int item_width;
1870 
1871 	for (item_height = 0; item_height < ItemMap[ItemPointer->type].inv_size.y; item_height++) {
1872 		for (item_width = 0; item_width < ItemMap[ItemPointer->type].inv_size.x; item_width++) {
1873 			DebugPrintf(1, "\nAddFloorItemDirectlyToInventory:  Checking pos: %d %d ", Inv_Loc.x + item_width,
1874 				    Inv_Loc.y + item_height);
1875 			if (!Inv_Pos_Is_Free(Inv_Loc.x + item_width, Inv_Loc.y + item_height)) {
1876 				Me.Inventory[InvPos].inventory_position.x = -1;
1877 				Me.Inventory[InvPos].inventory_position.y = -1;
1878 				// goto This_Is_No_Possible_Location;
1879 				return (FALSE);
1880 			}
1881 		}
1882 	}
1883 	// if ( !Inv_Pos_Is_Free( Inv_Loc.x , Inv_Loc.y ) ) continue;
1884 
1885 	// At this point we know we have reached a position where we can plant this item.
1886 	Me.Inventory[InvPos].inventory_position.x = Inv_Loc.x;
1887 	Me.Inventory[InvPos].inventory_position.y = Inv_Loc.y;
1888 	DebugPrintf(1, "\nAddFloorItemDirectlyToInventory:  FINE INVENTORY POSITION FOUND!!");
1889 
1890 	if ((InvPos >= MAX_ITEMS_IN_INVENTORY - 1) || (Me.Inventory[InvPos].inventory_position.x == (-1))) {
1891 		Me.TextVisibleTime = 0;
1892 		Me.TextToBeDisplayed = _("I can't carry any more.");
1893 		CantCarrySound();
1894 		// can't take any more items,
1895 	} else {
1896 		raw_move_picked_up_item_to_entry(ItemPointer, &(Me.Inventory[InvPos]), Inv_Loc);
1897 	}
1898 	return TRUE;
1899 };				// int place_item_on_this_position_if_you_can ( ... )
1900 
1901 /**
1902  * This function deals with the case, that WHILE THERE IS NO INVENTORY
1903  * SCREEN OPEN, the Tux still clicks some items on the floor to pick them
1904  * up.  So no big visible operation is required, but instead the items
1905  * picked up should be either auto-equipped, if possible, or they should
1906  * be put into the inventory items pool.
1907  *
1908  * \return -1 on error, 1 if the item was placed somewhere, 0 if there is no room
1909  *         for the item in the inventory.
1910  */
try_give_item(item * ItemPointer)1911 int try_give_item(item *ItemPointer)
1912 {
1913 	int InvPos;
1914 	point Inv_Loc = { -1, -1 };
1915 	int TargetItemIndex;
1916 
1917 	if (ItemPointer == NULL)
1918 		return -1;
1919 
1920 	// In the special case of money, we add the amount of money to our
1921 	// money counter and eliminate the item on the floor.
1922 
1923 	if (item_spec_eq_id(ItemPointer->type, "Valuable Circuits")) {
1924 		play_item_sound(ItemPointer->type, &Me.pos);
1925 		Me.Gold += ItemPointer->multiplicity;
1926 		DeleteItem(ItemPointer);
1927 		return 1;
1928 	}
1929 
1930 	// In the special case, that this is an item, that groups together with others
1931 	// of the same type AND we also have as item of this type already in inventory,
1932 	// then we just need to manipulate multiplicity a bit and we're done.  Very easy.
1933 
1934 	if (ItemMap[ItemPointer->type].item_group_together_in_inventory) {
1935 		if (CountItemtypeInInventory(ItemPointer->type)) {
1936 			TargetItemIndex = FindFirstInventoryIndexWithItemType(ItemPointer->type);
1937 			Me.Inventory[TargetItemIndex].multiplicity += ItemPointer->multiplicity;
1938 			play_item_sound(ItemPointer->type, &Me.pos);
1939 			DeleteItem(ItemPointer);
1940 			return 1;
1941 		}
1942 	}
1943 
1944 	// Maybe the item is of a kind that can be equipped right now.  Then
1945 	// we decide to directly drop it to the corresponding slot.
1946 
1947 	if ((Me.weapon_item.type == (-1)) && (ItemMap[ItemPointer->type].slot == WEAPON_SLOT)) {
1948 		if (ItemUsageRequirementsMet(ItemPointer, TRUE)) {
1949 			// Now we're picking up a weapon while no weapon is equipped.  But still
1950 			// it might be a 2-handed weapon while there is some shield equipped.  Well,
1951 			// when that is the case, we refuse to put it directly to the proper slot,
1952 			// otherwise we do it.
1953 			//
1954 			if (Me.shield_item.type == (-1)) {
1955 				raw_move_picked_up_item_to_entry(ItemPointer, &(Me.weapon_item), Inv_Loc);
1956 				return 1;
1957 			}
1958 			// So now we know that some shield item is equipped.  Let's be careful:  2-handed
1959 			// weapons will be rejected from direct addition to the slot.
1960 			//
1961 			if (!ItemMap[ItemPointer->type].weapon_needs_two_hands) {
1962 				raw_move_picked_up_item_to_entry(ItemPointer, &(Me.weapon_item), Inv_Loc);
1963 				return 1;
1964 			}
1965 		}
1966 	}
1967 
1968 	if ((Me.shield_item.type == (-1)) && (ItemMap[ItemPointer->type].slot == SHIELD_SLOT)) {
1969 		if (ItemUsageRequirementsMet(ItemPointer, TRUE)) {
1970 			// Auto-equipping shields can be done.  But only if there isn't a 2-handed
1971 			// weapon equipped already.  Well, in case of no weapon present it's easy:
1972 			//
1973 			if (Me.weapon_item.type == (-1)) {
1974 				raw_move_picked_up_item_to_entry(ItemPointer, &(Me.shield_item), Inv_Loc);
1975 				return 1;
1976 			}
1977 			// But now we know, that there is some weapon present.  We need to be careful:
1978 			// it might be a 2-handed weapon.
1979 			//
1980 			if (!ItemMap[Me.weapon_item.type].weapon_needs_two_hands) {
1981 				raw_move_picked_up_item_to_entry(ItemPointer, &(Me.shield_item), Inv_Loc);
1982 				return 1;
1983 			}
1984 		}
1985 	}
1986 
1987 	if ((Me.armour_item.type == (-1)) && (ItemMap[ItemPointer->type].slot == ARMOR_SLOT)) {
1988 		if (ItemUsageRequirementsMet(ItemPointer, TRUE)) {
1989 			raw_move_picked_up_item_to_entry(ItemPointer, &(Me.armour_item), Inv_Loc);
1990 			return 1;
1991 		}
1992 	}
1993 
1994 	if ((Me.drive_item.type == (-1)) && (ItemMap[ItemPointer->type].slot == BOOT_SLOT)) {
1995 		if (ItemUsageRequirementsMet(ItemPointer, TRUE)) {
1996 			raw_move_picked_up_item_to_entry(ItemPointer, &(Me.drive_item), Inv_Loc);
1997 			return 1;
1998 		}
1999 	}
2000 
2001 	if ((Me.special_item.type == (-1)) && (ItemMap[ItemPointer->type].slot == HELM_SLOT)) {
2002 		if (ItemUsageRequirementsMet(ItemPointer, TRUE)) {
2003 			raw_move_picked_up_item_to_entry(ItemPointer, &(Me.special_item), Inv_Loc);
2004 			return 1;
2005 		}
2006 	}
2007 
2008 	// find a free position in the inventory list
2009 	for (InvPos = 0; InvPos < MAX_ITEMS_IN_INVENTORY - 1; InvPos++) {
2010 		if (Me.Inventory[InvPos].type == (-1))
2011 			break;
2012 	}
2013 
2014 	// Maybe the item in question is something, that would best be placed inside
2015 	// the quick inventory.  If that is so, we try to put it there first.  If that
2016 	// isn't possible, it can still be placed somewhere outside of the quick
2017 	// inventory later.
2018 
2019 	if ((ItemMap[ItemPointer->type].inv_size.x == 1) &&
2020 	    (ItemMap[ItemPointer->type].inv_size.y == 1) && (ItemMap[ItemPointer->type].right_use.tooltip)) {
2021 		DebugPrintf(2, "\n\nTrying to place this item inside of the quick inventory first...");
2022 		Inv_Loc.y = INVENTORY_GRID_HEIGHT - 1;
2023 		for (Inv_Loc.x = 0; Inv_Loc.x < INVENTORY_GRID_WIDTH - ItemMap[ItemPointer->type].inv_size.x + 1; Inv_Loc.x++) {
2024 			if (place_item_on_this_position_if_you_can(ItemPointer, Inv_Loc, InvPos))
2025 				return 1;
2026 		}
2027 	}
2028 
2029 	// Find enough free squares in the inventory to fit
2030 	for (Inv_Loc.y = 0; Inv_Loc.y < INVENTORY_GRID_HEIGHT - ItemMap[ItemPointer->type].inv_size.y + 1; Inv_Loc.y++) {
2031 		for (Inv_Loc.x = 0; Inv_Loc.x < INVENTORY_GRID_WIDTH - ItemMap[ItemPointer->type].inv_size.x + 1; Inv_Loc.x++) {
2032 			if (place_item_on_this_position_if_you_can(ItemPointer, Inv_Loc, InvPos))
2033 				return 1;
2034 		}
2035 	}
2036 
2037 	// No enough free place in the inventory
2038 	if (Me.Inventory[InvPos].inventory_position.x == (-1)) {
2039 		return 0;
2040 	}
2041 
2042 	// Place the item in the inventory
2043 	raw_move_picked_up_item_to_entry(ItemPointer, &(Me.Inventory[InvPos]), Inv_Loc);
2044 
2045 	return 1;
2046 }
2047 
2048 /**
2049  * \brief Places the item to the inventory of the player or to the floor.
2050  *
2051  * If the item is of an equippable type whose requirements are met and the
2052  * matching equipment slot is empty, the item is placed to the equipment slot.
2053  * Otherwise, if there's enough room in the inventory, the item is placed there.
2054  * Otherwise, the item is dropped to the floor at the feet of the player.
2055  *
2056  * \param it The item to give to the player.
2057  *
2058  * \return TRUE if equipped or placed into the inventory, FALSE if dropped on the floor
2059  */
give_item(item * it)2060 int give_item(item *it)
2061 {
2062 	if (it == NULL)
2063 		return FALSE;
2064 
2065 	if (try_give_item(it)) {
2066 		return TRUE;
2067 	}
2068 
2069 	Me.TextVisibleTime = 0;
2070 	Me.TextToBeDisplayed = _("I can't carry any more.");
2071 	CantCarrySound();
2072 	drop_item(it, Me.pos.x, Me.pos.y, Me.pos.z);
2073 
2074 	return FALSE;
2075 }
2076 
item_is_currently_equipped(item * Item)2077 int item_is_currently_equipped(item * Item)
2078 {
2079 	if ((&(Me.weapon_item) == Item) || (&(Me.drive_item) == Item) || (&(Me.armour_item) == Item)
2080 	    || (&(Me.shield_item) == Item) || (&(Me.special_item) == Item))
2081 		return 1;
2082 
2083 	return 0;
2084 }
2085 
get_slot_type_by_name(char * name)2086 enum slot_type get_slot_type_by_name(char *name)
2087 {
2088 	struct {
2089 		const char *name;
2090 		enum slot_type slot;
2091 	} slots[] = {	{ "weapon", WEAPON_SLOT },
2092 					{"drive", 	BOOT_SLOT 	},
2093 					{"shield", 	SHIELD_SLOT },
2094 					{"armour", 	ARMOR_SLOT 	},
2095 					{"special", HELM_SLOT 	}};
2096 
2097 	int i;
2098 	for (i = 0; i < sizeof(slots)/sizeof(slots[0]); i++) {
2099 		if (!strcmp(name, slots[i].name))
2100 			return slots[i].slot;
2101 	}
2102 	return NO_SLOT;
2103 }
2104 
2105 /**
2106  * \brief Get the name of a item specs by its type.
2107  * If the item spec haven't a title, the name is used, and otherwise "UNNAMED ITEM".
2108  * \param type A valid type of itemspec (different of -1).
2109  * \return The title of the item.
2110  */
item_specs_get_name(int type)2111 const char *item_specs_get_name(int type)
2112 {
2113 	if (ItemMap[type].name)
2114 		return ItemMap[type].name;
2115 	else if (ItemMap[type].id)
2116 		return ItemMap[type].id;
2117 	return "BUG - UNNAMED ITEM";
2118 }
2119 
get_busy_type_by_name(char * name)2120 enum _busytype get_busy_type_by_name(char *name)
2121 {
2122 	if (!strcmp(name, "drinking")) {
2123 		return DRINKING_POTION;
2124 	} else if (!strcmp(name, "throwing")) {
2125 		return THROWING_GRENADE;
2126 	} else if (!strcmp(name, "pill")) {
2127 		return TAKING_PILL;
2128 	}
2129 	return NONE;
2130 }
2131 
2132 #undef _items_c
2133