1 #include <assert.h>
2 
3 #include "info.h"
4 #include "gi.h"
5 #include "a_pickups.h"
6 #include "templates.h"
7 #include "g_level.h"
8 #include "d_player.h"
9 #include "farchive.h"
10 
11 
12 IMPLEMENT_CLASS (AArmor)
IMPLEMENT_CLASS(ABasicArmor)13 IMPLEMENT_CLASS (ABasicArmor)
14 IMPLEMENT_CLASS (ABasicArmorPickup)
15 IMPLEMENT_CLASS (ABasicArmorBonus)
16 IMPLEMENT_CLASS (AHexenArmor)
17 
18 //===========================================================================
19 //
20 // ABasicArmor :: Serialize
21 //
22 //===========================================================================
23 
24 void ABasicArmor::Serialize (FArchive &arc)
25 {
26 	Super::Serialize (arc);
27 	arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType;
28 
29 	if (SaveVersion >= 4511)
30 	{
31 		 arc << ActualSaveAmount;
32 	}
33 }
34 
35 //===========================================================================
36 //
37 // ABasicArmor :: Tick
38 //
39 // If BasicArmor is given to the player by means other than a
40 // BasicArmorPickup, then it may not have an icon set. Fix that here.
41 //
42 //===========================================================================
43 
Tick()44 void ABasicArmor::Tick ()
45 {
46 	Super::Tick ();
47 	AbsorbCount = 0;
48 	if (!Icon.isValid())
49 	{
50 		FString icon = gameinfo.ArmorIcon1;
51 
52 		if (SavePercent >= gameinfo.Armor2Percent && gameinfo.ArmorIcon2.Len() != 0)
53 			icon = gameinfo.ArmorIcon2;
54 
55 		if (icon[0] != 0)
56 			Icon = TexMan.CheckForTexture (icon, FTexture::TEX_Any);
57 	}
58 }
59 
60 //===========================================================================
61 //
62 // ABasicArmor :: CreateCopy
63 //
64 //===========================================================================
65 
CreateCopy(AActor * other)66 AInventory *ABasicArmor::CreateCopy (AActor *other)
67 {
68 	// BasicArmor that is in use is stored in the inventory as BasicArmor.
69 	// BasicArmor that is in reserve is not.
70 	ABasicArmor *copy = Spawn<ABasicArmor> (0, 0, 0, NO_REPLACE);
71 	copy->SavePercent = SavePercent != 0 ? SavePercent : FRACUNIT/3;
72 	copy->Amount = Amount;
73 	copy->MaxAmount = MaxAmount;
74 	copy->Icon = Icon;
75 	copy->BonusCount = BonusCount;
76 	copy->ArmorType = ArmorType;
77 	copy->ActualSaveAmount = ActualSaveAmount;
78 	GoAwayAndDie ();
79 	return copy;
80 }
81 
82 //===========================================================================
83 //
84 // ABasicArmor :: HandlePickup
85 //
86 //===========================================================================
87 
HandlePickup(AInventory * item)88 bool ABasicArmor::HandlePickup (AInventory *item)
89 {
90 	if (item->GetClass() == RUNTIME_CLASS(ABasicArmor))
91 	{
92 		// You shouldn't be picking up BasicArmor anyway.
93 		return true;
94 	}
95 	if (item->IsKindOf(RUNTIME_CLASS(ABasicArmorBonus)) && !(item->ItemFlags & IF_IGNORESKILL))
96 	{
97 		ABasicArmorBonus *armor = static_cast<ABasicArmorBonus*>(item);
98 
99 		armor->SaveAmount = FixedMul(armor->SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
100 	}
101 	else if (item->IsKindOf(RUNTIME_CLASS(ABasicArmorPickup)) && !(item->ItemFlags & IF_IGNORESKILL))
102 	{
103 		ABasicArmorPickup *armor = static_cast<ABasicArmorPickup*>(item);
104 
105 		armor->SaveAmount = FixedMul(armor->SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
106 	}
107 	if (Inventory != NULL)
108 	{
109 		return Inventory->HandlePickup (item);
110 	}
111 	return false;
112 }
113 
114 //===========================================================================
115 //
116 // ABasicArmor :: AbsorbDamage
117 //
118 //===========================================================================
119 
AbsorbDamage(int damage,FName damageType,int & newdamage)120 void ABasicArmor::AbsorbDamage (int damage, FName damageType, int &newdamage)
121 {
122 	int saved;
123 
124 	if (!DamageTypeDefinition::IgnoreArmor(damageType))
125 	{
126 		int full = MAX(0, MaxFullAbsorb - AbsorbCount);
127 		if (damage < full)
128 		{
129 			saved = damage;
130 		}
131 		else
132 		{
133 			saved = full + FixedMul (damage - full, SavePercent);
134 			if (MaxAbsorb > 0 && saved + AbsorbCount > MaxAbsorb)
135 			{
136 				saved = MAX(0,  MaxAbsorb - AbsorbCount);
137 			}
138 		}
139 
140 		if (Amount < saved)
141 		{
142 			saved = Amount;
143 		}
144 		newdamage -= saved;
145 		Amount -= saved;
146 		AbsorbCount += saved;
147 		if (Amount == 0)
148 		{
149 			// The armor has become useless
150 			SavePercent = 0;
151 			ArmorType = NAME_None; // Not NAME_BasicArmor.
152 			// Now see if the player has some more armor in their inventory
153 			// and use it if so. As in Strife, the best armor is used up first.
154 			ABasicArmorPickup *best = NULL;
155 			AInventory *probe = Owner->Inventory;
156 			while (probe != NULL)
157 			{
158 				if (probe->IsKindOf (RUNTIME_CLASS(ABasicArmorPickup)))
159 				{
160 					ABasicArmorPickup *inInv = static_cast<ABasicArmorPickup*>(probe);
161 					if (best == NULL || best->SavePercent < inInv->SavePercent)
162 					{
163 						best = inInv;
164 					}
165 				}
166 				probe = probe->Inventory;
167 			}
168 			if (best != NULL)
169 			{
170 				Owner->UseInventory (best);
171 			}
172 		}
173 		damage = newdamage;
174 	}
175 
176 	// Once the armor has absorbed its part of the damage, then apply its damage factor, if any, to the player
177 	if ((damage > 0) && (ArmorType != NAME_None)) // BasicArmor is not going to have any damage factor, so skip it.
178 	{
179 		// This code is taken and adapted from APowerProtection::ModifyDamage().
180 		// The differences include not using a default value, and of course the way
181 		// the damage factor info is obtained.
182 		const fixed_t *pdf = NULL;
183 		DmgFactors *df = PClass::FindClass(ArmorType)->ActorInfo->DamageFactors;
184 		if (df != NULL && df->CountUsed() != 0)
185 		{
186 			pdf = df->CheckFactor(damageType);
187 			if (pdf != NULL)
188 			{
189 				damage = newdamage = FixedMul(damage, *pdf);
190 			}
191 		}
192 	}
193 	if (Inventory != NULL)
194 	{
195 		Inventory->AbsorbDamage (damage, damageType, newdamage);
196 	}
197 }
198 
199 //===========================================================================
200 //
201 // ABasicArmorPickup :: Serialize
202 //
203 //===========================================================================
204 
Serialize(FArchive & arc)205 void ABasicArmorPickup::Serialize (FArchive &arc)
206 {
207 	Super::Serialize (arc);
208 	arc << SavePercent << SaveAmount << MaxAbsorb << MaxFullAbsorb;
209 	arc << DropTime;
210 }
211 
212 //===========================================================================
213 //
214 // ABasicArmorPickup :: CreateCopy
215 //
216 //===========================================================================
217 
CreateCopy(AActor * other)218 AInventory *ABasicArmorPickup::CreateCopy (AActor *other)
219 {
220 	ABasicArmorPickup *copy = static_cast<ABasicArmorPickup *> (Super::CreateCopy (other));
221 
222 	if (!(ItemFlags & IF_IGNORESKILL))
223 	{
224 		SaveAmount = FixedMul(SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
225 	}
226 
227 	copy->SavePercent = SavePercent;
228 	copy->SaveAmount = SaveAmount;
229 	copy->MaxAbsorb = MaxAbsorb;
230 	copy->MaxFullAbsorb = MaxFullAbsorb;
231 
232 	return copy;
233 }
234 
235 //===========================================================================
236 //
237 // ABasicArmorPickup :: Use
238 //
239 // Either gives you new armor or replaces the armor you already have (if
240 // the SaveAmount is greater than the amount of armor you own). When the
241 // item is auto-activated, it will only be activated if its max amount is 0
242 // or if you have no armor active already.
243 //
244 //===========================================================================
245 
Use(bool pickup)246 bool ABasicArmorPickup::Use (bool pickup)
247 {
248 	ABasicArmor *armor = Owner->FindInventory<ABasicArmor> ();
249 
250 	if (armor == NULL)
251 	{
252 		armor = Spawn<ABasicArmor> (0,0,0, NO_REPLACE);
253 		armor->BecomeItem ();
254 		Owner->AddInventory (armor);
255 	}
256 	else
257 	{
258 		// If you already have more armor than this item gives you, you can't
259 		// use it.
260 		if (armor->Amount >= SaveAmount + armor->BonusCount)
261 		{
262 			return false;
263 		}
264 		// Don't use it if you're picking it up and already have some.
265 		if (pickup && armor->Amount > 0 && MaxAmount > 0)
266 		{
267 			return false;
268 		}
269 	}
270 	armor->SavePercent = SavePercent;
271 	armor->Amount = SaveAmount + armor->BonusCount;
272 	armor->MaxAmount = SaveAmount;
273 	armor->Icon = Icon;
274 	armor->MaxAbsorb = MaxAbsorb;
275 	armor->MaxFullAbsorb = MaxFullAbsorb;
276 	armor->ArmorType = this->GetClass()->TypeName;
277 	armor->ActualSaveAmount = SaveAmount;
278 	return true;
279 }
280 
281 //===========================================================================
282 //
283 // ABasicArmorBonus :: Serialize
284 //
285 //===========================================================================
286 
Serialize(FArchive & arc)287 void ABasicArmorBonus::Serialize (FArchive &arc)
288 {
289 	Super::Serialize (arc);
290 	arc << SavePercent << SaveAmount << MaxSaveAmount << BonusCount << BonusMax
291 		 << MaxAbsorb << MaxFullAbsorb;
292 }
293 
294 //===========================================================================
295 //
296 // ABasicArmorBonus :: CreateCopy
297 //
298 //===========================================================================
299 
CreateCopy(AActor * other)300 AInventory *ABasicArmorBonus::CreateCopy (AActor *other)
301 {
302 	ABasicArmorBonus *copy = static_cast<ABasicArmorBonus *> (Super::CreateCopy (other));
303 
304 	if (!(ItemFlags & IF_IGNORESKILL))
305 	{
306 		SaveAmount = FixedMul(SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
307 	}
308 
309 	copy->SavePercent = SavePercent;
310 	copy->SaveAmount = SaveAmount;
311 	copy->MaxSaveAmount = MaxSaveAmount;
312 	copy->BonusCount = BonusCount;
313 	copy->BonusMax = BonusMax;
314 	copy->MaxAbsorb = MaxAbsorb;
315 	copy->MaxFullAbsorb = MaxFullAbsorb;
316 
317 	return copy;
318 }
319 
320 //===========================================================================
321 //
322 // ABasicArmorBonus :: Use
323 //
324 // Tries to add to the amount of BasicArmor a player has.
325 //
326 //===========================================================================
327 
Use(bool pickup)328 bool ABasicArmorBonus::Use (bool pickup)
329 {
330 	ABasicArmor *armor = Owner->FindInventory<ABasicArmor> ();
331 	bool result = false;
332 
333 	if (armor == NULL)
334 	{
335 		armor = Spawn<ABasicArmor> (0,0,0, NO_REPLACE);
336 		armor->BecomeItem ();
337 		armor->Amount = 0;
338 		armor->MaxAmount = MaxSaveAmount;
339 		Owner->AddInventory (armor);
340 	}
341 
342 	if (BonusCount > 0 && armor->BonusCount < BonusMax)
343 	{
344 		armor->BonusCount = MIN (armor->BonusCount + BonusCount, BonusMax);
345 		result = true;
346 	}
347 
348 	int saveAmount = MIN (SaveAmount, MaxSaveAmount);
349 
350 	if (saveAmount <= 0)
351 	{ // If it can't give you anything, it's as good as used.
352 		return BonusCount > 0 ? result : true;
353 	}
354 
355 	// If you already have more armor than this item can give you, you can't
356 	// use it.
357 	if (armor->Amount >= MaxSaveAmount + armor->BonusCount)
358 	{
359 		return result;
360 	}
361 
362 	if (armor->Amount <= 0)
363 	{ // Should never be less than 0, but might as well check anyway
364 		armor->Amount = 0;
365 		armor->Icon = Icon;
366 		armor->SavePercent = SavePercent;
367 		armor->MaxAbsorb = MaxAbsorb;
368 		armor->ArmorType = this->GetClass()->TypeName;
369 		armor->MaxFullAbsorb = MaxFullAbsorb;
370 		armor->ActualSaveAmount = MaxSaveAmount;
371 	}
372 
373 	armor->Amount = MIN(armor->Amount + saveAmount, MaxSaveAmount + armor->BonusCount);
374 	armor->MaxAmount = MAX (armor->MaxAmount, MaxSaveAmount);
375 	return true;
376 }
377 
378 //===========================================================================
379 //
380 // AHexenArmor :: Serialize
381 //
382 //===========================================================================
383 
Serialize(FArchive & arc)384 void AHexenArmor::Serialize (FArchive &arc)
385 {
386 	Super::Serialize (arc);
387 	arc << Slots[0] << Slots[1] << Slots[2] << Slots[3]
388 		<< Slots[4]
389 		<< SlotsIncrement[0] << SlotsIncrement[1] << SlotsIncrement[2]
390 		<< SlotsIncrement[3];
391 }
392 
393 //===========================================================================
394 //
395 // AHexenArmor :: CreateCopy
396 //
397 //===========================================================================
398 
CreateCopy(AActor * other)399 AInventory *AHexenArmor::CreateCopy (AActor *other)
400 {
401 	// Like BasicArmor, HexenArmor is used in the inventory but not the map.
402 	// health is the slot this armor occupies.
403 	// Amount is the quantity to give (0 = normal max).
404 	AHexenArmor *copy = Spawn<AHexenArmor> (0, 0, 0, NO_REPLACE);
405 	copy->AddArmorToSlot (other, health, Amount);
406 	GoAwayAndDie ();
407 	return copy;
408 }
409 
410 //===========================================================================
411 //
412 // AHexenArmor :: CreateTossable
413 //
414 // Since this isn't really a single item, you can't drop it. Ever.
415 //
416 //===========================================================================
417 
CreateTossable()418 AInventory *AHexenArmor::CreateTossable ()
419 {
420 	return NULL;
421 }
422 
423 //===========================================================================
424 //
425 // AHexenArmor :: HandlePickup
426 //
427 //===========================================================================
428 
HandlePickup(AInventory * item)429 bool AHexenArmor::HandlePickup (AInventory *item)
430 {
431 	if (item->IsKindOf (RUNTIME_CLASS(AHexenArmor)))
432 	{
433 		if (AddArmorToSlot (Owner, item->health, item->Amount))
434 		{
435 			item->ItemFlags |= IF_PICKUPGOOD;
436 		}
437 		return true;
438 	}
439 	else if (Inventory != NULL)
440 	{
441 		return Inventory->HandlePickup (item);
442 	}
443 	return false;
444 }
445 
446 //===========================================================================
447 //
448 // AHexenArmor :: AddArmorToSlot
449 //
450 //===========================================================================
451 
AddArmorToSlot(AActor * actor,int slot,int amount)452 bool AHexenArmor::AddArmorToSlot (AActor *actor, int slot, int amount)
453 {
454 	APlayerPawn *ppawn;
455 	int hits;
456 
457 	if (actor->player != NULL)
458 	{
459 		ppawn = static_cast<APlayerPawn *>(actor);
460 	}
461 	else
462 	{
463 		ppawn = NULL;
464 	}
465 
466 	if (slot < 0 || slot > 3)
467 	{
468 		return false;
469 	}
470 	if (amount <= 0)
471 	{
472 		hits = SlotsIncrement[slot];
473 		if (Slots[slot] < hits)
474 		{
475 			Slots[slot] = hits;
476 			return true;
477 		}
478 	}
479 	else
480 	{
481 		hits = amount * 5 * FRACUNIT;
482 		fixed_t total = Slots[0]+Slots[1]+Slots[2]+Slots[3]+Slots[4];
483 		fixed_t max = SlotsIncrement[0]+SlotsIncrement[1]+SlotsIncrement[2]+SlotsIncrement[3]+Slots[4]+4*5*FRACUNIT;
484 		if (total < max)
485 		{
486 			Slots[slot] += hits;
487 			return true;
488 		}
489 	}
490 	return false;
491 }
492 
493 //===========================================================================
494 //
495 // AHexenArmor :: AbsorbDamage
496 //
497 //===========================================================================
498 
AbsorbDamage(int damage,FName damageType,int & newdamage)499 void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage)
500 {
501 	if (!DamageTypeDefinition::IgnoreArmor(damageType))
502 	{
503 		fixed_t savedPercent = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4];
504 
505 		if (savedPercent)
506 		{ // armor absorbed some damage
507 			if (savedPercent > 100*FRACUNIT)
508 			{
509 				savedPercent = 100*FRACUNIT;
510 			}
511 			for (int i = 0; i < 4; i++)
512 			{
513 				if (Slots[i])
514 				{
515 					// 300 damage always wipes out the armor unless some was added
516 					// with the dragon skin bracers.
517 					if (damage < 10000)
518 					{
519 #if __APPLE__ && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__  == 1
520 						// -O1 optimizer bug work around. Only needed for
521 						// GCC 4.2.1 on OS X for 10.4/10.5 tools compatibility.
522 						volatile fixed_t tmp = 300;
523 						Slots[i] -= Scale (damage, SlotsIncrement[i], tmp);
524 #else
525 						Slots[i] -= Scale (damage, SlotsIncrement[i], 300);
526 #endif
527 						if (Slots[i] < 2*FRACUNIT)
528 						{
529 							Slots[i] = 0;
530 						}
531 					}
532 					else
533 					{
534 						Slots[i] = 0;
535 					}
536 				}
537 			}
538 			int saved = Scale (damage, savedPercent, 100*FRACUNIT);
539 			if (saved > savedPercent >> (FRACBITS-1))
540 			{
541 				saved = savedPercent >> (FRACBITS-1);
542 			}
543 			newdamage -= saved;
544 			damage = newdamage;
545 		}
546 	}
547 	if (Inventory != NULL)
548 	{
549 		Inventory->AbsorbDamage (damage, damageType, newdamage);
550 	}
551 }
552 
553