1 /*
2 #include "actor.h"
3 #include "info.h"
4 #include "a_pickups.h"
5 #include "a_artifacts.h"
6 #include "gstrings.h"
7 #include "p_local.h"
8 #include "s_sound.h"
9 #include "m_random.h"
10 #include "a_action.h"
11 #include "a_hexenglobal.h"
12 #include "w_wad.h"
13 #include "thingdef/thingdef.h"
14 #include "g_level.h"
15 */
16 
17 EXTERN_CVAR(Bool, sv_unlimited_pickup)
18 
19 static FRandom pr_poisonbag ("PoisonBag");
20 static FRandom pr_poisoncloud ("PoisonCloud");
21 static FRandom pr_poisoncloudd ("PoisonCloudDamage");
22 
23 DECLARE_ACTION(A_CheckThrowBomb)
24 
25 // Poison Bag Artifact (Flechette) ------------------------------------------
26 
27 class AArtiPoisonBag : public AInventory
28 {
29 	DECLARE_CLASS (AArtiPoisonBag, AInventory)
30 public:
31 	bool HandlePickup (AInventory *item);
32 	AInventory *CreateCopy (AActor *other);
33 	void BeginPlay ();
34 };
35 
36 IMPLEMENT_CLASS (AArtiPoisonBag)
37 
38 // Poison Bag 1 (The Cleric's) ----------------------------------------------
39 
40 class AArtiPoisonBag1 : public AArtiPoisonBag
41 {
42 	DECLARE_CLASS (AArtiPoisonBag1, AArtiPoisonBag)
43 public:
44 	bool Use (bool pickup);
45 };
46 
IMPLEMENT_CLASS(AArtiPoisonBag1)47 IMPLEMENT_CLASS (AArtiPoisonBag1)
48 
49 bool AArtiPoisonBag1::Use (bool pickup)
50 {
51 	angle_t angle = Owner->angle >> ANGLETOFINESHIFT;
52 	AActor *mo;
53 
54 	mo = Spawn ("PoisonBag", Owner->Vec3Offset(
55 		16*finecosine[angle],
56 		24*finesine[angle],
57 		-Owner->floorclip+8*FRACUNIT), ALLOW_REPLACE);
58 	if (mo)
59 	{
60 		mo->target = Owner;
61 		return true;
62 	}
63 	return false;
64 }
65 
66 // Poison Bag 2 (The Mage's) ------------------------------------------------
67 
68 class AArtiPoisonBag2 : public AArtiPoisonBag
69 {
70 	DECLARE_CLASS (AArtiPoisonBag2, AArtiPoisonBag)
71 public:
72 	bool Use (bool pickup);
73 };
74 
IMPLEMENT_CLASS(AArtiPoisonBag2)75 IMPLEMENT_CLASS (AArtiPoisonBag2)
76 
77 bool AArtiPoisonBag2::Use (bool pickup)
78 {
79 	angle_t angle = Owner->angle >> ANGLETOFINESHIFT;
80 	AActor *mo;
81 
82 	mo = Spawn ("FireBomb", Owner->Vec3Offset(
83 		16*finecosine[angle],
84 		24*finesine[angle],
85 		-Owner->floorclip+8*FRACUNIT), ALLOW_REPLACE);
86 	if (mo)
87 	{
88 		mo->target = Owner;
89 		return true;
90 	}
91 	return false;
92 }
93 
94 // Poison Bag 3 (The Fighter's) ---------------------------------------------
95 
96 class AArtiPoisonBag3 : public AArtiPoisonBag
97 {
98 	DECLARE_CLASS (AArtiPoisonBag3, AArtiPoisonBag)
99 public:
100 	bool Use (bool pickup);
101 };
102 
IMPLEMENT_CLASS(AArtiPoisonBag3)103 IMPLEMENT_CLASS (AArtiPoisonBag3)
104 
105 bool AArtiPoisonBag3::Use (bool pickup)
106 {
107 	AActor *mo;
108 
109 	mo = Spawn("ThrowingBomb", Owner->PosPlusZ(-Owner->floorclip+35*FRACUNIT + (Owner->player? Owner->player->crouchoffset : 0)), ALLOW_REPLACE);
110 	if (mo)
111 	{
112 		mo->angle = Owner->angle + (((pr_poisonbag()&7) - 4) << 24);
113 
114 		/* Original flight code from Hexen
115 		 * mo->momz = 4*FRACUNIT+((player->lookdir)<<(FRACBITS-4));
116 		 * mo->z += player->lookdir<<(FRACBITS-4);
117 		 * P_ThrustMobj(mo, mo->angle, mo->info->speed);
118 		 * mo->momx += player->mo->momx>>1;
119 		 * mo->momy += player->mo->momy>>1;
120 		 */
121 
122 		// When looking straight ahead, it uses a z velocity of 4 while the xy velocity
123 		// is as set by the projectile. To accommodate this with a proper trajectory, we
124 		// aim the projectile ~20 degrees higher than we're looking at and increase the
125 		// speed we fire at accordingly.
126 		angle_t orgpitch = angle_t(-Owner->pitch) >> ANGLETOFINESHIFT;
127 		angle_t modpitch = angle_t(0xDC00000 - Owner->pitch) >> ANGLETOFINESHIFT;
128 		angle_t angle = mo->angle >> ANGLETOFINESHIFT;
129 		fixed_t speed = fixed_t(sqrt((double)mo->Speed*mo->Speed + (4.0*65536*4*65536)));
130 		fixed_t xyscale = FixedMul(speed, finecosine[modpitch]);
131 
132 		mo->velz = FixedMul(speed, finesine[modpitch]);
133 		mo->velx = FixedMul(xyscale, finecosine[angle]) + (Owner->velx >> 1);
134 		mo->vely = FixedMul(xyscale, finesine[angle]) + (Owner->vely >> 1);
135 		mo->AddZ(FixedMul(mo->Speed, finesine[orgpitch]));
136 
137 		mo->target = Owner;
138 		mo->tics -= pr_poisonbag()&3;
139 		P_CheckMissileSpawn(mo, Owner->radius);
140 		return true;
141 	}
142 	return false;
143 }
144 
145 // Poison Bag 4 (Generic Giver) ----------------------------------------------
146 
147 class AArtiPoisonBagGiver : public AArtiPoisonBag
148 {
149 	DECLARE_CLASS (AArtiPoisonBagGiver, AArtiPoisonBag)
150 public:
151 	bool Use (bool pickup);
152 };
153 
IMPLEMENT_CLASS(AArtiPoisonBagGiver)154 IMPLEMENT_CLASS (AArtiPoisonBagGiver)
155 
156 bool AArtiPoisonBagGiver::Use (bool pickup)
157 {
158 	const PClass *MissileType = PClass::FindClass((ENamedName) this->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None));
159 	if (MissileType != NULL)
160 	{
161 		AActor *mo = Spawn (MissileType, Owner->Pos(), ALLOW_REPLACE);
162 		if (mo != NULL)
163 		{
164 			if (mo->IsKindOf(RUNTIME_CLASS(AInventory)))
165 			{
166 				AInventory *inv = static_cast<AInventory *>(mo);
167 				if (inv->CallTryPickup(Owner))
168 					return true;
169 			}
170 			mo->Destroy();	// Destroy if not inventory or couldn't be picked up
171 		}
172 	}
173 	return false;
174 }
175 
176 // Poison Bag 5 (Generic Thrower) ----------------------------------------------
177 
178 class AArtiPoisonBagShooter : public AArtiPoisonBag
179 {
180 	DECLARE_CLASS (AArtiPoisonBagShooter, AArtiPoisonBag)
181 public:
182 	bool Use (bool pickup);
183 };
184 
IMPLEMENT_CLASS(AArtiPoisonBagShooter)185 IMPLEMENT_CLASS (AArtiPoisonBagShooter)
186 
187 bool AArtiPoisonBagShooter::Use (bool pickup)
188 {
189 	const PClass *MissileType = PClass::FindClass((ENamedName) this->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None));
190 	if (MissileType != NULL)
191 	{
192 		AActor *mo = P_SpawnPlayerMissile(Owner, MissileType);
193 		if (mo != NULL)
194 		{
195 			// automatic handling of seeker missiles
196 			if (mo->flags2 & MF2_SEEKERMISSILE)
197 			{
198 				mo->tracer = Owner->target;
199 			}
200 			return true;
201 		}
202 	}
203 	return false;
204 }
205 
206 //============================================================================
207 //
208 // GetFlechetteType
209 //
210 //============================================================================
211 
GetFlechetteType(AActor * other)212 const PClass *GetFlechetteType(AActor *other)
213 {
214 	const PClass *spawntype = NULL;
215 	if (other->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
216 	{
217 		spawntype = static_cast<APlayerPawn*>(other)->FlechetteType;
218 	}
219 	if (spawntype == NULL)
220 	{
221 		// default fallback if nothing valid defined.
222 		spawntype = RUNTIME_CLASS(AArtiPoisonBag3);
223 	}
224 	return spawntype;
225 }
226 
227 //============================================================================
228 //
229 // AArtiPoisonBag :: HandlePickup
230 //
231 //============================================================================
232 
HandlePickup(AInventory * item)233 bool AArtiPoisonBag::HandlePickup (AInventory *item)
234 {
235 	// Only do special handling when picking up the base class
236 	if (item->GetClass() != RUNTIME_CLASS(AArtiPoisonBag))
237 	{
238 		return Super::HandlePickup (item);
239 	}
240 
241 	if (GetClass() == GetFlechetteType(Owner))
242 	{
243 		if (Amount < MaxAmount || sv_unlimited_pickup)
244 		{
245 			Amount += item->Amount;
246 			if (Amount > MaxAmount && !sv_unlimited_pickup)
247 			{
248 				Amount = MaxAmount;
249 			}
250 			item->ItemFlags |= IF_PICKUPGOOD;
251 		}
252 		return true;
253 	}
254 	if (Inventory != NULL)
255 	{
256 		return Inventory->HandlePickup (item);
257 	}
258 	return false;
259 }
260 
261 //============================================================================
262 //
263 // AArtiPoisonBag :: CreateCopy
264 //
265 //============================================================================
266 
CreateCopy(AActor * other)267 AInventory *AArtiPoisonBag::CreateCopy (AActor *other)
268 {
269 	// Only the base class gets special handling
270 	if (GetClass() != RUNTIME_CLASS(AArtiPoisonBag))
271 	{
272 		return Super::CreateCopy (other);
273 	}
274 
275 	AInventory *copy;
276 
277 	const PClass *spawntype = GetFlechetteType(other);
278 	copy = static_cast<AInventory *>(Spawn (spawntype, 0, 0, 0, NO_REPLACE));
279 	copy->Amount = Amount;
280 	copy->MaxAmount = MaxAmount;
281 	GoAwayAndDie ();
282 	return copy;
283 }
284 
285 //============================================================================
286 //
287 // AArtiPoisonBag :: BeginPlay
288 //
289 //============================================================================
290 
BeginPlay()291 void AArtiPoisonBag::BeginPlay ()
292 {
293 	Super::BeginPlay ();
294 	// If a subclass's specific icon is not defined, let it use the base class's.
295 	if (!Icon.isValid())
296 	{
297 		AInventory *defbag;
298 		// Why doesn't this work?
299 		//defbag = GetDefault<AArtiPoisonBag>();
300 		defbag = (AInventory *)GetDefaultByType (RUNTIME_CLASS(AArtiPoisonBag));
301 		Icon = defbag->Icon;
302 	}
303 }
304 
305 // Poison Cloud -------------------------------------------------------------
306 
307 class APoisonCloud : public AActor
308 {
309 	DECLARE_CLASS (APoisonCloud, AActor)
310 public:
311 	int DoSpecialDamage (AActor *target, int damage, FName damagetype);
312 	void BeginPlay ();
313 };
314 
IMPLEMENT_CLASS(APoisonCloud)315 IMPLEMENT_CLASS (APoisonCloud)
316 
317 void APoisonCloud::BeginPlay ()
318 {
319 	velx = 1; // missile objects must move to impact other objects
320 	special1 = 24+(pr_poisoncloud()&7);
321 	special2 = 0;
322 }
323 
DoSpecialDamage(AActor * victim,int damage,FName damagetype)324 int APoisonCloud::DoSpecialDamage (AActor *victim, int damage, FName damagetype)
325 {
326 	if (victim->player)
327 	{
328 		bool mate = (target != NULL && victim->player != target->player && victim->IsTeammate (target));
329 		bool dopoison;
330 
331 		if (!mate)
332 		{
333 			dopoison = victim->player->poisoncount < 4;
334 		}
335 		else
336 		{
337 			dopoison = victim->player->poisoncount < (int)(4.f * level.teamdamage);
338 		}
339 
340 		if (dopoison)
341 		{
342 			int damage = 15 + (pr_poisoncloudd()&15);
343 			if (mate)
344 			{
345 				damage = (int)((float)damage * level.teamdamage);
346 			}
347 			// Handle passive damage modifiers (e.g. PowerProtection)
348 			if (victim->Inventory != NULL)
349 			{
350 				victim->Inventory->ModifyDamage(damage, damagetype, damage, true);
351 			}
352 			// Modify with damage factors
353 			damage = FixedMul(damage, victim->DamageFactor);
354 			if (damage > 0)
355 			{
356 				damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, damagetype, victim->GetClass()->ActorInfo->DamageFactors);
357 			}
358 			if (damage > 0)
359 			{
360 				P_PoisonDamage (victim->player, this,
361 					15+(pr_poisoncloudd()&15), false); // Don't play painsound
362 
363 				// If successful, play the poison sound.
364 				if (P_PoisonPlayer (victim->player, this, this->target, 50))
365 					S_Sound (victim, CHAN_VOICE, "*poison", 1, ATTN_NORM);
366 			}
367 		}
368 		return -1;
369 	}
370 	else if (!(victim->flags3 & MF3_ISMONSTER))
371 	{ // only damage monsters/players with the poison cloud
372 		return -1;
373 	}
374 	return damage;
375 }
376 
377 //===========================================================================
378 //
379 // A_PoisonBagInit
380 //
381 //===========================================================================
382 
DEFINE_ACTION_FUNCTION(AActor,A_PoisonBagInit)383 DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagInit)
384 {
385 	AActor *mo;
386 
387 	mo = Spawn<APoisonCloud> (self->PosPlusZ(28*FRACUNIT), ALLOW_REPLACE);
388 	if (mo)
389 	{
390 		mo->target = self->target;
391 	}
392 }
393 
394 //===========================================================================
395 //
396 // A_PoisonBagCheck
397 //
398 //===========================================================================
399 
DEFINE_ACTION_FUNCTION(AActor,A_PoisonBagCheck)400 DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagCheck)
401 {
402 	if (--self->special1 <= 0)
403 	{
404 		self->SetState (self->FindState ("Death"));
405 	}
406 	else
407 	{
408 		return;
409 	}
410 }
411 
412 //===========================================================================
413 //
414 // A_PoisonBagDamage
415 //
416 //===========================================================================
417 
DEFINE_ACTION_FUNCTION(AActor,A_PoisonBagDamage)418 DEFINE_ACTION_FUNCTION(AActor, A_PoisonBagDamage)
419 {
420 	int bobIndex;
421 
422 	P_RadiusAttack (self, self->target, 4, 40, self->DamageType, RADF_HURTSOURCE);
423 	bobIndex = self->special2;
424 	self->AddZ(finesine[bobIndex << BOBTOFINESHIFT] >> 1);
425 	self->special2 = (bobIndex + 1) & 63;
426 }
427 
428 //===========================================================================
429 //
430 // A_CheckThrowBomb
431 //
432 //===========================================================================
433 
DEFINE_ACTION_FUNCTION(AActor,A_CheckThrowBomb)434 DEFINE_ACTION_FUNCTION(AActor, A_CheckThrowBomb)
435 {
436 	if (--self->health <= 0)
437 	{
438 		self->SetState (self->FindState(NAME_Death));
439 	}
440 }
441 
442 //===========================================================================
443 //
444 // A_CheckThrowBomb2
445 //
446 //===========================================================================
447 
DEFINE_ACTION_FUNCTION(AActor,A_CheckThrowBomb2)448 DEFINE_ACTION_FUNCTION(AActor, A_CheckThrowBomb2)
449 {
450 	// [RH] Check using actual velocity, although the velz < 2 check still stands
451 	//if (abs(self->velx) < FRACUNIT*3/2 && abs(self->vely) < FRACUNIT*3/2
452 	//	&& self->velz < 2*FRACUNIT)
453 	if (self->velz < 2*FRACUNIT &&
454 		TMulScale32 (self->velx, self->velx, self->vely, self->vely, self->velz, self->velz)
455 		< (3*3)/(2*2))
456 	{
457 		self->SetState (self->SpawnState + 6);
458 		self->SetZ(self->floorz);
459 		self->velz = 0;
460 		self->BounceFlags = BOUNCE_None;
461 		self->flags &= ~MF_MISSILE;
462 	}
463 	CALL_ACTION(A_CheckThrowBomb, self);
464 }
465