1 /*
2 #include "actor.h"
3 #include "p_enemy.h"
4 #include "a_action.h"
5 #include "m_random.h"
6 #include "p_local.h"
7 #include "a_doomglobal.h"
8 #include "s_sound.h"
9 #include "r_data/r_translate.h"
10 #include "thingdef/thingdef.h"
11 #include "g_level.h"
12 */
13 
14 #define MARINE_PAIN_CHANCE 160
15 
16 static FRandom pr_m_refire ("SMarineRefire");
17 static FRandom pr_m_punch ("SMarinePunch");
18 static FRandom pr_m_gunshot ("SMarineGunshot");
19 static FRandom pr_m_saw ("SMarineSaw");
20 static FRandom pr_m_fireshotgun2 ("SMarineFireSSG");
21 
IMPLEMENT_CLASS(AScriptedMarine)22 IMPLEMENT_CLASS (AScriptedMarine)
23 
24 void AScriptedMarine::Serialize (FArchive &arc)
25 {
26 	Super::Serialize (arc);
27 
28 	if (arc.IsStoring ())
29 	{
30 		arc.WriteSprite (SpriteOverride);
31 	}
32 	else
33 	{
34 		SpriteOverride = arc.ReadSprite ();
35 	}
36 	arc << CurrentWeapon;
37 }
38 
Activate(AActor * activator)39 void AScriptedMarine::Activate (AActor *activator)
40 {
41 	if (flags2 & MF2_DORMANT)
42 	{
43 		flags2 &= ~MF2_DORMANT;
44 		tics = 1;
45 	}
46 }
47 
Deactivate(AActor * activator)48 void AScriptedMarine::Deactivate (AActor *activator)
49 {
50 	if (!(flags2 & MF2_DORMANT))
51 	{
52 		flags2 |= MF2_DORMANT;
53 		tics = -1;
54 	}
55 }
56 
GetWeaponStates(int weap,FState * & melee,FState * & missile)57 bool AScriptedMarine::GetWeaponStates(int weap, FState *&melee, FState *&missile)
58 {
59 	static ENamedName WeaponNames[] =
60 	{
61 		NAME_None,
62 		NAME_Fist,
63 		NAME_Berserk,
64 		NAME_Chainsaw,
65 		NAME_Pistol,
66 		NAME_Shotgun,
67 		NAME_SSG,
68 		NAME_Chaingun,
69 		NAME_Rocket,
70 		NAME_Plasma,
71 		NAME_Railgun,
72 		NAME_BFG
73 	};
74 
75 	if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy;
76 
77 	melee = FindState(NAME_Melee, WeaponNames[weap], true);
78 	missile = FindState(NAME_Missile, WeaponNames[weap], true);
79 
80 	return melee != NULL || missile != NULL;
81 }
82 
BeginPlay()83 void AScriptedMarine::BeginPlay ()
84 {
85 	Super::BeginPlay ();
86 
87 	// Set the current weapon
88 	for(int i=WEAPON_Dummy; i<=WEAPON_BFG; i++)
89 	{
90 		FState *melee, *missile;
91 		if (GetWeaponStates(i, melee, missile))
92 		{
93 			if (melee == MeleeState && missile == MissileState)
94 			{
95 				CurrentWeapon = i;
96 			}
97 		}
98 	}
99 }
100 
Tick()101 void AScriptedMarine::Tick ()
102 {
103 	Super::Tick ();
104 
105 	// Override the standard sprite, if desired
106 	if (SpriteOverride != 0 && sprite == SpawnState->sprite)
107 	{
108 		sprite = SpriteOverride;
109 	}
110 
111 	if (special1 != 0)
112 	{
113 		if (CurrentWeapon == WEAPON_SuperShotgun)
114 		{ // Play SSG reload sounds
115 			int ticks = level.maptime - special1;
116 			if (ticks < 47)
117 			{
118 				switch (ticks)
119 				{
120 				case 14:
121 					S_Sound (this, CHAN_WEAPON, "weapons/sshoto", 1, ATTN_NORM);
122 					break;
123 				case 28:
124 					S_Sound (this, CHAN_WEAPON, "weapons/sshotl", 1, ATTN_NORM);
125 					break;
126 				case 41:
127 					S_Sound (this, CHAN_WEAPON, "weapons/sshotc", 1, ATTN_NORM);
128 					break;
129 				}
130 			}
131 			else
132 			{
133 				special1 = 0;
134 			}
135 		}
136 		else
137 		{ // Wait for a long refire time
138 			if (level.maptime >= special1)
139 			{
140 				special1 = 0;
141 			}
142 			else
143 			{
144 				flags |= MF_JUSTATTACKED;
145 			}
146 		}
147 	}
148 }
149 
150 //============================================================================
151 //
152 // A_M_Refire
153 //
154 //============================================================================
155 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_M_Refire)156 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Refire)
157 {
158 	ACTION_PARAM_START(1);
159 	ACTION_PARAM_BOOL(ignoremissile, 0);
160 
161 	if (self->target == NULL || self->target->health <= 0)
162 	{
163 		if (self->MissileState && pr_m_refire() < 160)
164 		{ // Look for a new target most of the time
165 			if (P_LookForPlayers (self, true, NULL) && P_CheckMissileRange (self))
166 			{ // Found somebody new and in range, so don't stop shooting
167 				return;
168 			}
169 		}
170 		self->SetState (self->state + 1);
171 		return;
172 	}
173 	if (((ignoremissile || self->MissileState == NULL) && !self->CheckMeleeRange ()) ||
174 		!P_CheckSight (self, self->target) ||
175 		pr_m_refire() < 4)	// Small chance of stopping even when target not dead
176 	{
177 		self->SetState (self->state + 1);
178 	}
179 }
180 
181 //============================================================================
182 //
183 // A_M_SawRefire
184 //
185 //============================================================================
186 
DEFINE_ACTION_FUNCTION(AActor,A_M_SawRefire)187 DEFINE_ACTION_FUNCTION(AActor, A_M_SawRefire)
188 {
189 	if (self->target == NULL || self->target->health <= 0)
190 	{
191 		self->SetState (self->state + 1);
192 		return;
193 	}
194 	if (!self->CheckMeleeRange ())
195 	{
196 		self->SetState (self->state + 1);
197 	}
198 }
199 
200 //============================================================================
201 //
202 // A_MarineNoise
203 //
204 //============================================================================
205 
DEFINE_ACTION_FUNCTION(AActor,A_MarineNoise)206 DEFINE_ACTION_FUNCTION(AActor, A_MarineNoise)
207 {
208 	if (static_cast<AScriptedMarine *>(self)->CurrentWeapon == AScriptedMarine::WEAPON_Chainsaw)
209 	{
210 		S_Sound (self, CHAN_WEAPON, "weapons/sawidle", 1, ATTN_NORM);
211 	}
212 }
213 
214 //============================================================================
215 //
216 // A_MarineChase
217 //
218 //============================================================================
219 
DEFINE_ACTION_FUNCTION(AActor,A_MarineChase)220 DEFINE_ACTION_FUNCTION(AActor, A_MarineChase)
221 {
222 	CALL_ACTION(A_MarineNoise, self);
223 	A_Chase (self);
224 }
225 
226 //============================================================================
227 //
228 // A_MarineLook
229 //
230 //============================================================================
231 
DEFINE_ACTION_FUNCTION(AActor,A_MarineLook)232 DEFINE_ACTION_FUNCTION(AActor, A_MarineLook)
233 {
234 	CALL_ACTION(A_MarineNoise, self);
235 	CALL_ACTION(A_Look, self);
236 }
237 
238 //============================================================================
239 //
240 // A_M_Saw
241 //
242 //============================================================================
243 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_M_Saw)244 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Saw)
245 {
246 	ACTION_PARAM_START(4);
247 	ACTION_PARAM_SOUND(fullsound, 0);
248 	ACTION_PARAM_SOUND(hitsound, 1);
249 	ACTION_PARAM_INT(damage, 2);
250 	ACTION_PARAM_CLASS(pufftype, 3);
251 
252 	if (self->target == NULL)
253 		return;
254 
255 	if (pufftype == NULL) pufftype = PClass::FindClass(NAME_BulletPuff);
256 	if (damage == 0) damage = 2;
257 
258 	A_FaceTarget (self);
259 	if (self->CheckMeleeRange ())
260 	{
261 		angle_t 	angle;
262 		AActor		*linetarget;
263 
264 		damage *= (pr_m_saw()%10+1);
265 		angle = self->angle + (pr_m_saw.Random2() << 18);
266 
267 		P_LineAttack (self, angle, MELEERANGE+1,
268 					P_AimLineAttack (self, angle, MELEERANGE+1, &linetarget), damage,
269 					NAME_Melee, pufftype, false, &linetarget);
270 
271 		if (!linetarget)
272 		{
273 			S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
274 			return;
275 		}
276 		S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
277 
278 		// turn to face target
279 		angle = self->AngleTo(linetarget);
280 		if (angle - self->angle > ANG180)
281 		{
282 			if (angle - self->angle < (angle_t)(-ANG90/20))
283 				self->angle = angle + ANG90/21;
284 			else
285 				self->angle -= ANG90/20;
286 		}
287 		else
288 		{
289 			if (angle - self->angle > ANG90/20)
290 				self->angle = angle - ANG90/21;
291 			else
292 				self->angle += ANG90/20;
293 		}
294 	}
295 	else
296 	{
297 		S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
298 	}
299 	//A_Chase (self);
300 }
301 
302 //============================================================================
303 //
304 // A_M_Punch
305 //
306 //============================================================================
307 
MarinePunch(AActor * self,int damagemul)308 static void MarinePunch(AActor *self, int damagemul)
309 {
310 	angle_t 	angle;
311 	int 		damage;
312 	int 		pitch;
313 	AActor		*linetarget;
314 
315 	if (self->target == NULL)
316 		return;
317 
318 	damage = ((pr_m_punch()%10+1) << 1) * damagemul;
319 
320 	A_FaceTarget (self);
321 	angle = self->angle + (pr_m_punch.Random2() << 18);
322 	pitch = P_AimLineAttack (self, angle, MELEERANGE, &linetarget);
323 	P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true, &linetarget);
324 
325 	// turn to face target
326 	if (linetarget)
327 	{
328 		S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
329 		self->angle = self->AngleTo(linetarget);
330 
331 	}
332 }
333 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_M_Punch)334 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Punch)
335 {
336 	ACTION_PARAM_START(1);
337 	ACTION_PARAM_INT(mult, 0);
338 
339 	MarinePunch(self, mult);
340 }
341 
342 //============================================================================
343 //
344 // P_GunShot2
345 //
346 //============================================================================
347 
P_GunShot2(AActor * mo,bool accurate,int pitch,const PClass * pufftype)348 void P_GunShot2 (AActor *mo, bool accurate, int pitch, const PClass *pufftype)
349 {
350 	angle_t 	angle;
351 	int 		damage;
352 
353 	damage = 5*(pr_m_gunshot()%3+1);
354 	angle = mo->angle;
355 
356 	if (!accurate)
357 	{
358 		angle += pr_m_gunshot.Random2 () << 18;
359 	}
360 
361 	P_LineAttack (mo, angle, MISSILERANGE, pitch, damage, NAME_Hitscan, pufftype);
362 }
363 
364 //============================================================================
365 //
366 // A_M_FirePistol
367 //
368 //============================================================================
369 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_M_FirePistol)370 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_FirePistol)
371 {
372 	if (self->target == NULL)
373 		return;
374 
375 	ACTION_PARAM_START(1);
376 	ACTION_PARAM_BOOL(accurate, 0);
377 
378 	S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
379 	A_FaceTarget (self);
380 	P_GunShot2 (self, accurate, P_AimLineAttack (self, self->angle, MISSILERANGE),
381 		PClass::FindClass(NAME_BulletPuff));
382 }
383 
384 //============================================================================
385 //
386 // A_M_FireShotgun
387 //
388 //============================================================================
389 
DEFINE_ACTION_FUNCTION(AActor,A_M_FireShotgun)390 DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun)
391 {
392 	int pitch;
393 
394 	if (self->target == NULL)
395 		return;
396 
397 	S_Sound (self, CHAN_WEAPON,  "weapons/shotgf", 1, ATTN_NORM);
398 	A_FaceTarget (self);
399 	pitch = P_AimLineAttack (self, self->angle, MISSILERANGE);
400 	for (int i = 0; i < 7; ++i)
401 	{
402 		P_GunShot2 (self, false, pitch, PClass::FindClass(NAME_BulletPuff));
403 	}
404 	self->special1 = level.maptime + 27;
405 }
406 
407 //============================================================================
408 //
409 // A_M_CheckAttack
410 //
411 //============================================================================
412 
DEFINE_ACTION_FUNCTION(AActor,A_M_CheckAttack)413 DEFINE_ACTION_FUNCTION(AActor, A_M_CheckAttack)
414 {
415 	if (self->special1 != 0 || self->target == NULL)
416 	{
417 		self->SetState (self->FindState("SkipAttack"));
418 	}
419 	else
420 	{
421 		A_FaceTarget (self);
422 	}
423 }
424 
425 //============================================================================
426 //
427 // A_M_FireShotgun2
428 //
429 //============================================================================
430 
DEFINE_ACTION_FUNCTION(AActor,A_M_FireShotgun2)431 DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun2)
432 {
433 	int pitch;
434 
435 	if (self->target == NULL)
436 		return;
437 
438 	S_Sound (self, CHAN_WEAPON, "weapons/sshotf", 1, ATTN_NORM);
439 	A_FaceTarget (self);
440 	pitch = P_AimLineAttack (self, self->angle, MISSILERANGE);
441 	for (int i = 0; i < 20; ++i)
442 	{
443 		int damage = 5*(pr_m_fireshotgun2()%3+1);
444 		angle_t angle = self->angle + (pr_m_fireshotgun2.Random2() << 19);
445 
446 		P_LineAttack (self, angle, MISSILERANGE,
447 					  pitch + (pr_m_fireshotgun2.Random2() * 332063), damage,
448 					  NAME_Hitscan, NAME_BulletPuff);
449 	}
450 	self->special1 = level.maptime;
451 }
452 
453 //============================================================================
454 //
455 // A_M_FireCGun
456 //
457 //============================================================================
458 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_M_FireCGun)459 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_FireCGun)
460 {
461 	if (self->target == NULL)
462 		return;
463 
464 	ACTION_PARAM_START(1);
465 	ACTION_PARAM_BOOL(accurate, 0);
466 
467 	S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
468 	A_FaceTarget (self);
469 	P_GunShot2 (self, accurate, P_AimLineAttack (self, self->angle, MISSILERANGE),
470 		PClass::FindClass(NAME_BulletPuff));
471 }
472 
473 //============================================================================
474 //
475 // A_M_FireMissile
476 //
477 // Giving a marine a rocket launcher is probably a bad idea unless you pump
478 // up his health, because he's just as likely to kill himself as he is to
479 // kill anything else with it.
480 //
481 //============================================================================
482 
DEFINE_ACTION_FUNCTION(AActor,A_M_FireMissile)483 DEFINE_ACTION_FUNCTION(AActor, A_M_FireMissile)
484 {
485 	if (self->target == NULL)
486 		return;
487 
488 	if (self->CheckMeleeRange ())
489 	{ // If too close, punch it
490 		MarinePunch(self, 1);
491 	}
492 	else
493 	{
494 		A_FaceTarget (self);
495 		P_SpawnMissile (self, self->target, PClass::FindClass("Rocket"));
496 	}
497 }
498 
499 //============================================================================
500 //
501 // A_M_FireRailgun
502 //
503 //============================================================================
504 
DEFINE_ACTION_FUNCTION(AActor,A_M_FireRailgun)505 DEFINE_ACTION_FUNCTION(AActor, A_M_FireRailgun)
506 {
507 	if (self->target == NULL)
508 		return;
509 
510 	CALL_ACTION(A_MonsterRail, self);
511 	self->special1 = level.maptime + 50;
512 }
513 
514 //============================================================================
515 //
516 // A_M_FirePlasma
517 //
518 //============================================================================
519 
DEFINE_ACTION_FUNCTION(AActor,A_M_FirePlasma)520 DEFINE_ACTION_FUNCTION(AActor, A_M_FirePlasma)
521 {
522 	if (self->target == NULL)
523 		return;
524 
525 	A_FaceTarget (self);
526 	P_SpawnMissile (self, self->target, PClass::FindClass("PlasmaBall"));
527 	self->special1 = level.maptime + 20;
528 }
529 
530 //============================================================================
531 //
532 // A_M_BFGsound
533 //
534 //============================================================================
535 
DEFINE_ACTION_FUNCTION(AActor,A_M_BFGsound)536 DEFINE_ACTION_FUNCTION(AActor, A_M_BFGsound)
537 {
538 	if (self->target == NULL)
539 		return;
540 
541 	if (self->special1 != 0)
542 	{
543 		self->SetState (self->SeeState);
544 	}
545 	else
546 	{
547 		A_FaceTarget (self);
548 		S_Sound (self, CHAN_WEAPON, "weapons/bfgf", 1, ATTN_NORM);
549 		// Don't interrupt the firing sequence
550 		self->PainChance = 0;
551 	}
552 }
553 
554 //============================================================================
555 //
556 // A_M_FireBFG
557 //
558 //============================================================================
559 
DEFINE_ACTION_FUNCTION(AActor,A_M_FireBFG)560 DEFINE_ACTION_FUNCTION(AActor, A_M_FireBFG)
561 {
562 	if (self->target == NULL)
563 		return;
564 
565 	A_FaceTarget (self);
566 	P_SpawnMissile (self, self->target, PClass::FindClass("BFGBall"));
567 	self->special1 = level.maptime + 30;
568 	self->PainChance = MARINE_PAIN_CHANCE;
569 }
570 
571 //---------------------------------------------------------------------------
572 
SetWeapon(EMarineWeapon type)573 void AScriptedMarine::SetWeapon (EMarineWeapon type)
574 {
575 	if (GetWeaponStates(type, MeleeState, MissileState))
576 	{
577 		static const char *classes[] = {
578 			"ScriptedMarine",
579 			"MarineFist",
580 			"MarineBerserk",
581 			"MarineChainsaw",
582 			"MarinePistol",
583 			"MarineShotgun",
584 			"MarineSSG",
585 			"MarineChaingun",
586 			"MarineRocket",
587 			"MarinePlasma",
588 			"MarineRailgun",
589 			"MarineBFG"
590 		};
591 
592 		const PClass *cls = PClass::FindClass(classes[type]);
593 		if (cls != NULL)
594 			DecalGenerator = GetDefaultByType(cls)->DecalGenerator;
595 		else
596 			DecalGenerator = NULL;
597 	}
598 }
599 
SetSprite(const PClass * source)600 void AScriptedMarine::SetSprite (const PClass *source)
601 {
602 	if (source == NULL || source->ActorInfo == NULL)
603 	{ // A valid actor class wasn't passed, so use the standard sprite
604 		SpriteOverride = sprite = GetClass()->ActorInfo->OwnedStates[0].sprite;
605 		// Copy the standard scaling
606 		scaleX = GetDefault()->scaleX;
607 		scaleY = GetDefault()->scaleY;
608 	}
609 	else
610 	{ // Use the same sprite and scaling the passed class spawns with
611 		SpriteOverride = sprite = GetDefaultByType (source)->SpawnState->sprite;
612 		scaleX = GetDefaultByType(source)->scaleX;
613 		scaleY = GetDefaultByType(source)->scaleY;
614 	}
615 }
616