1//**************************************************************************
2//**
3//**    ##   ##    ##    ##   ##   ####     ####   ###     ###
4//**    ##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5//**     ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6//**     ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7//**      ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8//**       #    ##    ##    #      ####     ####   ##       ##
9//**
10//**    $Id: EntityEx.Inventory.vc 4024 2009-06-20 16:11:22Z firebrand_kh $
11//**
12//**    Copyright (C) 1999-2006 Jānis Legzdiņš
13//**
14//**    This program is free software; you can redistribute it and/or
15//**  modify it under the terms of the GNU General Public License
16//**  as published by the Free Software Foundation; either version 2
17//**  of the License, or (at your option) any later version.
18//**
19//**    This program is distributed in the hope that it will be useful,
20//**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21//**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22//**  GNU General Public License for more details.
23//**
24//**************************************************************************
25
26//==========================================================================
27//
28//	AddInventory
29//
30//==========================================================================
31
32void AddInventory(Inventory Item)
33{
34	if (Item.Owner)
35	{
36		if (Item.Owner == self)
37		{
38			//	Already in the inventory.
39			return;
40		}
41		//	Remove from current owner's inventory.
42		EntityEx(Item.Owner).RemoveInventory(Item);
43	}
44
45	//	Add it at the top of the inventory.
46	Item.Inventory = Inventory;
47	Inventory = Item;
48
49	//	Set itsm's owner
50	Item.Owner = self;
51}
52
53//==========================================================================
54//
55//	RemoveInventory
56//
57//==========================================================================
58
59void RemoveInventory(Inventory Item)
60{
61	//	Find previous item or owner itself, if it's the first item.
62	EntityEx Prev = self;
63	while (Prev && Prev.Inventory != Item)
64	{
65		Prev = Prev.Inventory;
66	}
67
68	//	Unlink and remove owner.
69	Prev.Inventory = Item.Inventory;
70	Item.DetachedFromOwner();
71	Item.Owner = none;
72}
73
74//==========================================================================
75//
76//	FindInventory
77//
78//==========================================================================
79
80final Inventory FindInventory(class<Inventory> ItemClass)
81{
82	Inventory	Check;
83
84	for (Check = Inventory; Check; Check = Check.Inventory)
85	{
86		if (Check.Class == ItemClass)
87		{
88			return Check;
89		}
90	}
91	return none;
92}
93
94//==========================================================================
95//
96//	GiveInventoryType
97//
98//==========================================================================
99
100final Inventory GiveInventoryType(class<Inventory> Type)
101{
102	Inventory Item = Spawn(Type,,,, false);
103	//	This shouldn't count for the item statistics
104	if (Item.bCountItem)
105	{
106		Item.bCountItem = false;
107		Level.TotalItems--;
108	}
109	Item.bDropped = true;
110	if (!Item.TryPickup(self))
111	{
112		Item.Destroy();
113		return none;
114	}
115	return Item;
116}
117
118//==========================================================================
119//
120//	DestroyAllInventory
121//
122//==========================================================================
123
124final void DestroyAllInventory()
125{
126	while (Inventory)
127	{
128		Inventory.Destroy();
129	}
130}
131
132//==========================================================================
133//
134//	UseInventory
135//
136//==========================================================================
137
138bool UseInventory(Inventory Item)
139{
140	//	Don't use items if you are dead.
141	if (Health <= 0)
142	{
143		return false;
144	}
145	//	Don't use item if don't actually have it.
146	if (Item.Amount <= 0)
147	{
148		return false;
149	}
150	if (!Item.Use(false))
151	{
152		return false;
153	}
154	//	Item was used - remove it from inventory
155	Item.Amount--;
156	if (Item.Amount <= 0 && !Item.bKeepDepleted)
157	{
158		Item.Destroy();
159	}
160	return true;
161}
162
163//==========================================================================
164//
165//	DropInventory
166//
167//==========================================================================
168
169EntityEx DropInventory(Inventory SrcItem)
170{
171	TVec			Dir;
172
173	AngleVector(&Angles, &Dir);
174	Dir.z = 0.0;
175	Dir = Normalise(Dir);
176	Inventory Item = SrcItem.CreateTossable();
177	if (Item)
178	{
179		Item.SetOrigin2(Origin + vector(0.0, 0.0, 32.0) +
180			Dir * (Radius + 32.0));
181		Item.Angles.yaw = Angles.yaw;
182		Item.Velocity = Dir * 128.0;
183	}
184	return Item;
185}
186
187//==========================================================================
188//
189//	ObtainInventory
190//
191//==========================================================================
192
193void ObtainInventory(EntityEx Other)
194{
195	//	Actor should not have any inventory.
196	if (Inventory)
197	{
198		Error("ObtainInventory called while still having an inventory");
199	}
200
201	Inventory = Other.Inventory;
202	Other.Inventory = none;
203	Inventory Item;
204	for (Item = Inventory; Item; Item = Item.Inventory)
205	{
206		Item.Owner = self;
207	}
208}
209
210//===========================================================================
211//
212//  ClearInventory
213//
214//===========================================================================
215
216final void ClearInventory()
217{
218	Inventory Item = Inventory;
219	while (Item)
220	{
221		Inventory Next = Item.Inventory;
222		if (!Item.bUndroppable)
223		{
224			//	IMHO any item that should be kept should not be dropped,
225			// not just ammo.
226			if (Item.bKeepDepleted)
227			{
228				Item.Amount = 0;
229			}
230			else
231			{
232				Item.Destroy();
233			}
234		}
235		else if (HexenArmor(Item))
236		{
237			HexenArmor A = HexenArmor(Item);
238			A.Slots[0] = 0.0;
239			A.Slots[1] = 0.0;
240			A.Slots[2] = 0.0;
241			A.Slots[3] = 0.0;
242		}
243		Item = Next;
244	}
245
246	if (bIsPlayer)
247	{
248		PlayerEx(Player).ReadyWeapon = none;
249		PlayerEx(Player).PendingWeapon = none;
250		Player.SetViewState(ps_weapon, none);
251		Player.SetViewState(ps_flash, none);
252	}
253}
254
255//===========================================================================
256//
257//  GiveInventory
258//
259//===========================================================================
260
261final void GiveInventory(name ItemName, int Amount)
262{
263	if (Amount <= 0)
264	{
265		return;
266	}
267	if (ItemName == 'armor')
268	{
269		ItemName = 'basicarmor';
270	}
271
272	//	Get class of the item.
273	class EClass = FindClassLowerCase(ItemName);
274	if (!EClass)
275	{
276		print("Unknown item type %n", ItemName);
277		return;
278	}
279	class<Inventory> ItemClass = class<Inventory>(EClass);
280	if (!ItemClass)
281	{
282		print("%n is not an inventory class", ItemName);
283		return;
284	}
285
286	Weapon SavedPendingWeapon = none;
287	bool HadWeapon = true;
288	if (bIsPlayer)
289	{
290		SavedPendingWeapon = PlayerEx(Player).PendingWeapon;
291		HadWeapon = !!PlayerEx(Player).ReadyWeapon;
292	}
293
294	Inventory Item = Spawn(ItemClass,,,, false);
295	//	This shouldn't count for the item statistics
296	if (Item.bCountItem)
297	{
298		Item.bCountItem = false;
299		Level.TotalItems--;
300	}
301	if (BasicArmorPickup(Item))
302	{
303		BasicArmorPickup(Item).SaveAmount *= Amount;
304	}
305	else if (BasicArmorBonus(Item))
306	{
307		BasicArmorBonus(Item).SaveAmount *= Amount;
308	}
309	else
310	{
311		Item.Amount = Amount;
312	}
313
314	if (!Item.TryPickup(self))
315	{
316		Item.Destroy();
317	}
318	//	Don't automatically bring up weapon.
319	if (bIsPlayer && HadWeapon)
320	{
321		PlayerEx(Player).PendingWeapon = SavedPendingWeapon;
322	}
323}
324
325//===========================================================================
326//
327//  TakeInventory
328//
329//===========================================================================
330
331final void TakeInventory(name ItemName, int Amount)
332{
333	if (Amount <= 0)
334	{
335		return;
336	}
337	if (ItemName == 'armor')
338	{
339		ItemName = 'basicarmor';
340	}
341
342	//	Get class of the item.
343	class<Inventory> ItemClass = class<Inventory>(FindClassLowerCase(
344		ItemName));
345	if (!ItemClass)
346	{
347		return;
348	}
349
350	Inventory Item = FindInventory(ItemClass);
351	if (!Item)
352	{
353		return;
354	}
355
356	Item.Amount -= Amount;
357	if (Item.Amount <= 0)
358	{
359		if (Item.bKeepDepleted)
360		{
361			Item.Amount = 0;
362		}
363		else
364		{
365			Item.Destroy();
366		}
367	}
368}
369
370//===========================================================================
371//
372//  CheckInventory
373//
374//===========================================================================
375
376final int CheckInventory(name ItemName)
377{
378	if (ItemName == 'armor')
379	{
380		ItemName = 'basicarmor';
381	}
382	else if (ItemName == 'health')
383	{
384		return Health;
385	}
386
387	class<Inventory> ItemClass = class<Inventory>(FindClassLowerCase(
388		ItemName));
389	if (ItemClass)
390	{
391		Inventory Item = FindInventory(ItemClass);
392		return Item ? Item.Amount : 0;
393	}
394	return 0;
395}
396
397//===========================================================================
398//
399//  UseInventoryName
400//
401//===========================================================================
402
403final int UseInventoryName(name ItemName)
404{
405	class<Inventory> ItemClass = class<Inventory>(FindClassLowerCase(
406		ItemName));
407	if (ItemClass)
408	{
409		Inventory Item = FindInventory(ItemClass);
410		if (Item)
411		{
412			return UseInventory(Item);
413		}
414	}
415	return 0;
416}
417
418//===========================================================================
419//
420//  GetArmorPoints
421//
422//===========================================================================
423
424final int GetArmorPoints()
425{
426	Inventory A = FindInventory(BasicArmor);
427	return A ? A.Amount : 0;
428}
429
430//===========================================================================
431//
432//  CheckNamedWeapon
433//
434//===========================================================================
435
436final int CheckNamedWeapon(name Name)
437{
438	if (!bIsPlayer || !PlayerEx(Player).ReadyWeapon)
439	{
440		return false;
441	}
442	return !stricmp(va("%n", Name), va("%n", GetClassName(
443		PlayerEx(Player).ReadyWeapon.Class)));
444}
445
446//===========================================================================
447//
448//  SetNamedWeapon
449//
450//===========================================================================
451
452final int SetNamedWeapon(name Name)
453{
454	if (!bIsPlayer)
455	{
456		return false;
457	}
458
459	Weapon Wpn = Weapon(FindInventory(class<Inventory>(FindClassLowerCase(
460		Name))));
461	if (!Wpn)
462	{
463		return false;
464	}
465	//	Check if it's already active.
466	if (PlayerEx(Player).ReadyWeapon == Wpn)
467	{
468		//	Make sure player doesn't switch away from it.
469		PlayerEx(Player).PendingWeapon = none;
470		return true;
471	}
472	//	Check if weapon has enough ammo.
473	if (!Wpn.CheckAmmo(Weapon::FIRE_Either, false))
474	{
475		return false;
476	}
477	PlayerEx(Player).PendingWeapon = Wpn;
478	return true;
479}
480
481//===========================================================================
482//
483//  GetAmmoCapacity
484//
485//===========================================================================
486
487final int GetAmmoCapacity(name Name)
488{
489	class<Ammo> AmmoType = class<Ammo>(FindClassLowerCase(Name));
490	if (!AmmoType)
491	{
492		return 0;
493	}
494	//	Only direct descendants of Ammo class.
495	if (GetClassParent(AmmoType) != Ammo)
496	{
497		return 0;
498	}
499	//	If we have this ammo, return current max amount, otherwise return
500	// default for this ammo type.
501	Inventory Item = FindInventory(AmmoType);
502	if (Item)
503	{
504		return Item.MaxAmount;
505	}
506	else
507	{
508		return AmmoType.default.MaxAmount;
509	}
510}
511
512//===========================================================================
513//
514//  SetAmmoCapacity
515//
516//===========================================================================
517
518final void SetAmmoCapacity(name Name, int Amount)
519{
520	class<Ammo> AmmoType = class<Ammo>(FindClassLowerCase(Name));
521	if (!AmmoType)
522	{
523		return;
524	}
525	//	Only direct descendants of Ammo class.
526	if (GetClassParent(AmmoType) != Ammo)
527	{
528		return;
529	}
530
531	//	Make sure new ammo capacity is not negative.
532	if (Amount < 0)
533	{
534		Amount = 0;
535	}
536
537	Inventory Item = FindInventory(AmmoType);
538	if (!Item)
539	{
540		Item = GiveInventoryType(AmmoType);
541		Item.Amount = 0;
542	}
543	Item.MaxAmount = Amount;
544	//	We also should make sure that the current amount doesn't exceed the
545	// new limit.
546	if (Item.Amount > Item.MaxAmount)
547	{
548		Item.Amount = Item.MaxAmount;
549	}
550}
551