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