mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-01-18 15:42:34 +00:00
- scriptified the scripted marines.
- fixed symbol name generation for native functions. - moved PrintableName to VMFunction so that native functions also have this information.
This commit is contained in:
parent
97763b5a2b
commit
360436c201
20 changed files with 658 additions and 756 deletions
|
@ -867,7 +867,6 @@ set( NOT_COMPILED_SOURCE_FILES
|
|||
sc_man_scanner.h
|
||||
sc_man_scanner.re
|
||||
g_doom/a_painelemental.cpp
|
||||
g_doom/a_scriptedmarine.cpp
|
||||
g_heretic/a_chicken.cpp
|
||||
g_heretic/a_dsparil.cpp
|
||||
g_heretic/a_hereticartifacts.cpp
|
||||
|
|
|
@ -7,21 +7,6 @@ class AScriptedMarine : public AActor
|
|||
{
|
||||
DECLARE_CLASS (AScriptedMarine, AActor)
|
||||
public:
|
||||
enum EMarineWeapon
|
||||
{
|
||||
WEAPON_Dummy,
|
||||
WEAPON_Fist,
|
||||
WEAPON_BerserkFist,
|
||||
WEAPON_Chainsaw,
|
||||
WEAPON_Pistol,
|
||||
WEAPON_Shotgun,
|
||||
WEAPON_SuperShotgun,
|
||||
WEAPON_Chaingun,
|
||||
WEAPON_RocketLauncher,
|
||||
WEAPON_PlasmaRifle,
|
||||
WEAPON_Railgun,
|
||||
WEAPON_BFG
|
||||
};
|
||||
|
||||
void Activate (AActor *activator);
|
||||
void Deactivate (AActor *activator);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "gstrings.h"
|
||||
#include "g_level.h"
|
||||
#include "p_enemy.h"
|
||||
#include "a_doomglobal.h"
|
||||
#include "a_specialspot.h"
|
||||
#include "templates.h"
|
||||
#include "m_bbox.h"
|
||||
|
@ -21,5 +20,3 @@
|
|||
|
||||
// Include all the other Doom stuff here to reduce compile time
|
||||
#include "a_painelemental.cpp"
|
||||
#include "a_scriptedmarine.cpp"
|
||||
|
||||
|
|
|
@ -1,654 +0,0 @@
|
|||
/*
|
||||
#include "actor.h"
|
||||
#include "p_enemy.h"
|
||||
#include "a_action.h"
|
||||
#include "m_random.h"
|
||||
#include "p_local.h"
|
||||
#include "a_doomglobal.h"
|
||||
#include "s_sound.h"
|
||||
#include "r_data/r_translate.h"
|
||||
#include "vm.h"
|
||||
#include "g_level.h"
|
||||
*/
|
||||
|
||||
#define MARINE_PAIN_CHANCE 160
|
||||
|
||||
static FRandom pr_m_refire ("SMarineRefire");
|
||||
static FRandom pr_m_punch ("SMarinePunch");
|
||||
static FRandom pr_m_gunshot ("SMarineGunshot");
|
||||
static FRandom pr_m_saw ("SMarineSaw");
|
||||
static FRandom pr_m_fireshotgun2 ("SMarineFireSSG");
|
||||
|
||||
IMPLEMENT_CLASS(AScriptedMarine, false, false, false, false)
|
||||
|
||||
void AScriptedMarine::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
|
||||
auto def = (AScriptedMarine*)GetDefault();
|
||||
|
||||
arc.Sprite("spriteoverride", SpriteOverride, &def->SpriteOverride)
|
||||
("currentweapon", CurrentWeapon, def->CurrentWeapon);
|
||||
}
|
||||
|
||||
void AScriptedMarine::Activate (AActor *activator)
|
||||
{
|
||||
if (flags2 & MF2_DORMANT)
|
||||
{
|
||||
flags2 &= ~MF2_DORMANT;
|
||||
tics = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void AScriptedMarine::Deactivate (AActor *activator)
|
||||
{
|
||||
if (!(flags2 & MF2_DORMANT))
|
||||
{
|
||||
flags2 |= MF2_DORMANT;
|
||||
tics = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool AScriptedMarine::GetWeaponStates(int weap, FState *&melee, FState *&missile)
|
||||
{
|
||||
static ENamedName WeaponNames[] =
|
||||
{
|
||||
NAME_None,
|
||||
NAME_Fist,
|
||||
NAME_Berserk,
|
||||
NAME_Chainsaw,
|
||||
NAME_Pistol,
|
||||
NAME_Shotgun,
|
||||
NAME_SSG,
|
||||
NAME_Chaingun,
|
||||
NAME_Rocket,
|
||||
NAME_Plasma,
|
||||
NAME_Railgun,
|
||||
NAME_BFG
|
||||
};
|
||||
|
||||
if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy;
|
||||
|
||||
melee = FindState(NAME_Melee, WeaponNames[weap], true);
|
||||
missile = FindState(NAME_Missile, WeaponNames[weap], true);
|
||||
|
||||
return melee != NULL || missile != NULL;
|
||||
}
|
||||
|
||||
void AScriptedMarine::BeginPlay ()
|
||||
{
|
||||
Super::BeginPlay ();
|
||||
|
||||
// Set the current weapon
|
||||
for(int i=WEAPON_Dummy; i<=WEAPON_BFG; i++)
|
||||
{
|
||||
FState *melee, *missile;
|
||||
if (GetWeaponStates(i, melee, missile))
|
||||
{
|
||||
if (melee == MeleeState && missile == MissileState)
|
||||
{
|
||||
CurrentWeapon = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AScriptedMarine::Tick ()
|
||||
{
|
||||
Super::Tick ();
|
||||
|
||||
// Override the standard sprite, if desired
|
||||
if (SpriteOverride != 0 && sprite == SpawnState->sprite)
|
||||
{
|
||||
sprite = SpriteOverride;
|
||||
}
|
||||
|
||||
if (special1 != 0)
|
||||
{
|
||||
if (CurrentWeapon == WEAPON_SuperShotgun)
|
||||
{ // Play SSG reload sounds
|
||||
int ticks = level.maptime - special1;
|
||||
if (ticks < 47)
|
||||
{
|
||||
switch (ticks)
|
||||
{
|
||||
case 14:
|
||||
S_Sound (this, CHAN_WEAPON, "weapons/sshoto", 1, ATTN_NORM);
|
||||
break;
|
||||
case 28:
|
||||
S_Sound (this, CHAN_WEAPON, "weapons/sshotl", 1, ATTN_NORM);
|
||||
break;
|
||||
case 41:
|
||||
S_Sound (this, CHAN_WEAPON, "weapons/sshotc", 1, ATTN_NORM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
special1 = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Wait for a long refire time
|
||||
if (level.maptime >= special1)
|
||||
{
|
||||
special1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags |= MF_JUSTATTACKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_Refire
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_Refire)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_BOOL_DEF(ignoremissile);
|
||||
|
||||
if (self->target == NULL || self->target->health <= 0)
|
||||
{
|
||||
if (self->MissileState && pr_m_refire() < 160)
|
||||
{ // Look for a new target most of the time
|
||||
if (P_LookForPlayers (self, true, NULL) && P_CheckMissileRange (self))
|
||||
{ // Found somebody new and in range, so don't stop shooting
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
self->SetState (self->state + 1);
|
||||
return 0;
|
||||
}
|
||||
if (((ignoremissile || self->MissileState == NULL) && !self->CheckMeleeRange ()) ||
|
||||
!P_CheckSight (self, self->target) ||
|
||||
pr_m_refire() < 4) // Small chance of stopping even when target not dead
|
||||
{
|
||||
self->SetState (self->state + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_SawRefire
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_SawRefire)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->target == NULL || self->target->health <= 0)
|
||||
{
|
||||
self->SetState (self->state + 1);
|
||||
return 0;
|
||||
}
|
||||
if (!self->CheckMeleeRange ())
|
||||
{
|
||||
self->SetState (self->state + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_MarineNoise
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_MarineNoise)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (static_cast<AScriptedMarine *>(self)->CurrentWeapon == AScriptedMarine::WEAPON_Chainsaw)
|
||||
{
|
||||
S_Sound (self, CHAN_WEAPON, "weapons/sawidle", 1, ATTN_NORM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_MarineChase
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_MarineChase)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
CALL_ACTION(A_MarineNoise, self);
|
||||
A_Chase (stack, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_MarineLook
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_MarineLook)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
CALL_ACTION(A_MarineNoise, self);
|
||||
CALL_ACTION(A_Look, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_Saw
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_Saw)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_SOUND_DEF (fullsound)
|
||||
PARAM_SOUND_DEF (hitsound)
|
||||
PARAM_INT_DEF (damage)
|
||||
PARAM_CLASS_DEF (pufftype, AActor)
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
if (pufftype == NULL)
|
||||
{
|
||||
pufftype = PClass::FindActor(NAME_BulletPuff);
|
||||
}
|
||||
if (damage == 0)
|
||||
{
|
||||
damage = 2;
|
||||
}
|
||||
|
||||
A_FaceTarget (self);
|
||||
if (self->CheckMeleeRange ())
|
||||
{
|
||||
DAngle angle;
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
damage *= (pr_m_saw()%10+1);
|
||||
angle = self->Angles.Yaw + pr_m_saw.Random2() * (5.625 / 256);
|
||||
|
||||
P_LineAttack (self, angle, SAWRANGE,
|
||||
P_AimLineAttack (self, angle, SAWRANGE), damage,
|
||||
NAME_Melee, pufftype, false, &t);
|
||||
|
||||
if (!t.linetarget)
|
||||
{
|
||||
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
|
||||
return 0;
|
||||
}
|
||||
S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
|
||||
|
||||
// turn to face target
|
||||
angle = t.angleFromSource;
|
||||
DAngle anglediff = deltaangle(self->Angles.Yaw, angle);
|
||||
|
||||
if (anglediff < 0.0)
|
||||
{
|
||||
if (anglediff < -4.5)
|
||||
self->Angles.Yaw = angle + 90.0 / 21;
|
||||
else
|
||||
self->Angles.Yaw -= 4.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anglediff > 4.5)
|
||||
self->Angles.Yaw = angle - 90.0 / 21;
|
||||
else
|
||||
self->Angles.Yaw += 4.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
|
||||
}
|
||||
//A_Chase (self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_Punch
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
static void MarinePunch(AActor *self, int damagemul)
|
||||
{
|
||||
DAngle angle;
|
||||
int damage;
|
||||
DAngle pitch;
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (self->target == NULL)
|
||||
return;
|
||||
|
||||
damage = ((pr_m_punch()%10+1) << 1) * damagemul;
|
||||
|
||||
A_FaceTarget (self);
|
||||
angle = self->Angles.Yaw + pr_m_punch.Random2() * (5.625 / 256);
|
||||
pitch = P_AimLineAttack (self, angle, MELEERANGE);
|
||||
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true, &t);
|
||||
|
||||
// turn to face target
|
||||
if (t.linetarget)
|
||||
{
|
||||
S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
|
||||
self->Angles.Yaw = t.angleFromSource;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_Punch)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(mult);
|
||||
|
||||
MarinePunch(self, mult);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// P_GunShot2
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void P_GunShot2 (AActor *mo, bool accurate, DAngle pitch, PClassActor *pufftype)
|
||||
{
|
||||
DAngle angle;
|
||||
int damage;
|
||||
|
||||
damage = 5*(pr_m_gunshot()%3+1);
|
||||
angle = mo->Angles.Yaw;
|
||||
|
||||
if (!accurate)
|
||||
{
|
||||
angle += pr_m_gunshot.Random2() * (5.625 / 256);
|
||||
}
|
||||
|
||||
P_LineAttack (mo, angle, MISSILERANGE, pitch, damage, NAME_Hitscan, pufftype);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FirePistol
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FirePistol)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_BOOL(accurate);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
|
||||
A_FaceTarget (self);
|
||||
P_GunShot2 (self, accurate, P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE),
|
||||
PClass::FindActor(NAME_BulletPuff));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireShotgun
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
DAngle pitch;
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
S_Sound (self, CHAN_WEAPON, "weapons/shotgf", 1, ATTN_NORM);
|
||||
A_FaceTarget (self);
|
||||
pitch = P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE);
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
P_GunShot2 (self, false, pitch, PClass::FindActor(NAME_BulletPuff));
|
||||
}
|
||||
self->special1 = level.maptime + 27;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_CheckAttack
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_CheckAttack)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->special1 != 0 || self->target == NULL)
|
||||
{
|
||||
self->SetState (self->FindState("SkipAttack"));
|
||||
}
|
||||
else
|
||||
{
|
||||
A_FaceTarget (self);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireShotgun2
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun2)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
DAngle pitch;
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
S_Sound (self, CHAN_WEAPON, "weapons/sshotf", 1, ATTN_NORM);
|
||||
A_FaceTarget (self);
|
||||
pitch = P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
int damage = 5*(pr_m_fireshotgun2()%3+1);
|
||||
DAngle angle = self->Angles.Yaw + pr_m_fireshotgun2.Random2() * (11.25 / 256);
|
||||
|
||||
P_LineAttack (self, angle, MISSILERANGE,
|
||||
pitch + pr_m_fireshotgun2.Random2() * (7.097 / 256), damage,
|
||||
NAME_Hitscan, NAME_BulletPuff);
|
||||
}
|
||||
self->special1 = level.maptime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireCGun
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FireCGun)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_BOOL(accurate);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
|
||||
A_FaceTarget (self);
|
||||
P_GunShot2 (self, accurate, P_AimLineAttack (self, self->Angles.Yaw, MISSILERANGE),
|
||||
PClass::FindActor(NAME_BulletPuff));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireMissile
|
||||
//
|
||||
// Giving a marine a rocket launcher is probably a bad idea unless you pump
|
||||
// up his health, because he's just as likely to kill himself as he is to
|
||||
// kill anything else with it.
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FireMissile)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
if (self->CheckMeleeRange ())
|
||||
{ // If too close, punch it
|
||||
MarinePunch(self, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_FaceTarget (self);
|
||||
P_SpawnMissile (self, self->target, PClass::FindActor("Rocket"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireRailgun
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FireRailgun)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
CALL_ACTION(A_MonsterRail, self);
|
||||
self->special1 = level.maptime + 50;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FirePlasma
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FirePlasma)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
A_FaceTarget (self);
|
||||
P_SpawnMissile (self, self->target, PClass::FindActor("PlasmaBall"));
|
||||
self->special1 = level.maptime + 20;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_BFGsound
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_BFGsound)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
if (self->special1 != 0)
|
||||
{
|
||||
self->SetState (self->SeeState);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_FaceTarget (self);
|
||||
S_Sound (self, CHAN_WEAPON, "weapons/bfgf", 1, ATTN_NORM);
|
||||
// Don't interrupt the firing sequence
|
||||
self->PainChance = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireBFG
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_M_FireBFG)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (self->target == NULL)
|
||||
return 0;
|
||||
|
||||
A_FaceTarget (self);
|
||||
P_SpawnMissile (self, self->target, PClass::FindActor("BFGBall"));
|
||||
self->special1 = level.maptime + 30;
|
||||
self->PainChance = MARINE_PAIN_CHANCE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void AScriptedMarine::SetWeapon (EMarineWeapon type)
|
||||
{
|
||||
if (GetWeaponStates(type, MeleeState, MissileState))
|
||||
{
|
||||
static const char *classes[] = {
|
||||
"ScriptedMarine",
|
||||
"MarineFist",
|
||||
"MarineBerserk",
|
||||
"MarineChainsaw",
|
||||
"MarinePistol",
|
||||
"MarineShotgun",
|
||||
"MarineSSG",
|
||||
"MarineChaingun",
|
||||
"MarineRocket",
|
||||
"MarinePlasma",
|
||||
"MarineRailgun",
|
||||
"MarineBFG"
|
||||
};
|
||||
|
||||
const PClass *cls = PClass::FindClass(classes[type]);
|
||||
if (cls != NULL)
|
||||
DecalGenerator = GetDefaultByType(cls)->DecalGenerator;
|
||||
else
|
||||
DecalGenerator = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void AScriptedMarine::SetSprite (PClassActor *source)
|
||||
{
|
||||
if (source == NULL)
|
||||
{ // A valid actor class wasn't passed, so use the standard sprite
|
||||
SpriteOverride = sprite = GetClass()->OwnedStates[0].sprite;
|
||||
// Copy the standard scaling
|
||||
Scale = GetDefault()->Scale;
|
||||
}
|
||||
else
|
||||
{ // Use the same sprite and scaling the passed class spawns with
|
||||
SpriteOverride = sprite = GetDefaultByType (source)->SpawnState->sprite;
|
||||
Scale = GetDefaultByType(source)->Scale;
|
||||
}
|
||||
}
|
|
@ -359,7 +359,7 @@ struct FStateLabelStorage
|
|||
}
|
||||
}
|
||||
|
||||
FState *GetState(int pos, PClassActor *cls);
|
||||
FState *GetState(int pos, PClassActor *cls, bool exact = false);
|
||||
};
|
||||
|
||||
extern FStateLabelStorage StateLabels;
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
#include "sbar.h"
|
||||
#include "m_swap.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "a_doomglobal.h"
|
||||
#include "a_strifeglobal.h"
|
||||
#include "v_video.h"
|
||||
#include "w_wad.h"
|
||||
|
@ -6110,6 +6109,34 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, int *Stack, int &
|
|||
return true;
|
||||
}
|
||||
|
||||
static void SetMarineWeapon(AActor *marine, int weapon)
|
||||
{
|
||||
static VMFunction *smw = nullptr;
|
||||
if (smw == nullptr)
|
||||
{
|
||||
auto cls = PClass::FindActor("ScriptedMarine");
|
||||
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("SetWeapon", true));
|
||||
smw = func->Variants[0].Implementation;
|
||||
}
|
||||
VMValue params[2] = { marine, weapon };
|
||||
VMFrameStack stack;
|
||||
stack.Call(smw, params, 2, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
static void SetMarineSprite(AActor *marine, PClassActor *source)
|
||||
{
|
||||
static VMFunction *sms = nullptr;
|
||||
if (sms == nullptr)
|
||||
{
|
||||
auto cls = PClass::FindActor("ScriptedMarine");
|
||||
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("SetSprite", true));
|
||||
sms = func->Variants[0].Implementation;
|
||||
}
|
||||
VMValue params[2] = { marine, source };
|
||||
VMFrameStack stack;
|
||||
stack.Call(sms, params, 2, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
int DLevelScript::RunScript ()
|
||||
{
|
||||
DACSThinker *controller = DACSThinker::ActiveThinker;
|
||||
|
@ -8946,20 +8973,19 @@ scriptwait:
|
|||
case PCD_SETMARINEWEAPON:
|
||||
if (STACK(2) != 0)
|
||||
{
|
||||
AScriptedMarine *marine;
|
||||
TActorIterator<AScriptedMarine> iterator (STACK(2));
|
||||
AActor *marine;
|
||||
NActorIterator iterator("ScriptedMarine", STACK(2));
|
||||
|
||||
while ((marine = iterator.Next()) != NULL)
|
||||
{
|
||||
marine->SetWeapon ((AScriptedMarine::EMarineWeapon)STACK(1));
|
||||
SetMarineWeapon(marine, STACK(1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activator != NULL && activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine)))
|
||||
if (activator != nullptr && activator->IsKindOf (PClass::FindClass("ScriptedMarine")))
|
||||
{
|
||||
barrier_cast<AScriptedMarine *>(activator)->SetWeapon (
|
||||
(AScriptedMarine::EMarineWeapon)STACK(1));
|
||||
SetMarineWeapon(activator, STACK(1));
|
||||
}
|
||||
}
|
||||
sp -= 2;
|
||||
|
@ -8973,19 +8999,19 @@ scriptwait:
|
|||
{
|
||||
if (STACK(2) != 0)
|
||||
{
|
||||
AScriptedMarine *marine;
|
||||
TActorIterator<AScriptedMarine> iterator (STACK(2));
|
||||
AActor *marine;
|
||||
NActorIterator iterator("ScriptedMarine", STACK(2));
|
||||
|
||||
while ((marine = iterator.Next()) != NULL)
|
||||
{
|
||||
marine->SetSprite (type);
|
||||
SetMarineSprite(marine, type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activator != NULL && activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine)))
|
||||
if (activator != nullptr && activator->IsKindOf(PClass::FindClass("ScriptedMarine")))
|
||||
{
|
||||
barrier_cast<AScriptedMarine *>(activator)->SetSprite (type);
|
||||
SetMarineSprite(activator, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state)
|
|||
// If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash.
|
||||
auto owner = FState::StaticFindStateOwner(state);
|
||||
Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s which accesses user variables. The action function has been removed from this state\n",
|
||||
owner->TypeName.GetChars(), state - owner->OwnedStates, static_cast<VMScriptFunction *>(state->ActionFunc)->PrintableName.GetChars());
|
||||
owner->TypeName.GetChars(), state - owner->OwnedStates, state->ActionFunc->PrintableName.GetChars());
|
||||
state->ActionFunc = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -315,10 +315,11 @@ void AActor::InitNativeFields()
|
|||
meta->AddNativeField("TelefogDestType", TypeActorClass, myoffsetof(AActor, TeleFogDestType));
|
||||
meta->AddNativeField("SpawnState", TypeState, myoffsetof(AActor, SpawnState), VARF_ReadOnly);
|
||||
meta->AddNativeField("SeeState", TypeState, myoffsetof(AActor, SeeState), VARF_ReadOnly);
|
||||
meta->AddNativeField("MeleeState", TypeState, myoffsetof(AActor, MeleeState), VARF_ReadOnly);
|
||||
meta->AddNativeField("MissileState", TypeState, myoffsetof(AActor, MissileState), VARF_ReadOnly);
|
||||
meta->AddNativeField("MeleeState", TypeState, myoffsetof(AActor, MeleeState));
|
||||
meta->AddNativeField("MissileState", TypeState, myoffsetof(AActor, MissileState));
|
||||
//int ConversationRoot; // THe root of the current dialogue
|
||||
//FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is "used."
|
||||
meta->AddNativeField("DecalGenerator", NewPointer(TypeVoid), myoffsetof(AActor, DecalGenerator));
|
||||
//FDecalBase *DecalGenerator;
|
||||
|
||||
// synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them.
|
||||
|
|
|
@ -388,7 +388,7 @@ void DPSprite::SetState(FState *newstate, bool pending)
|
|||
// If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash.
|
||||
auto owner = FState::StaticFindStateOwner(newstate);
|
||||
Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s which accesses user variables. The action function has been removed from this state\n",
|
||||
owner->TypeName.GetChars(), newstate - owner->OwnedStates, static_cast<VMScriptFunction *>(newstate->ActionFunc)->PrintableName.GetChars());
|
||||
owner->TypeName.GetChars(), newstate - owner->OwnedStates, newstate->ActionFunc->PrintableName.GetChars());
|
||||
newstate->ActionFunc = nullptr;
|
||||
}
|
||||
if (newstate->CallAction(Owner->mo, Caller, &stp, &nextstate))
|
||||
|
|
|
@ -289,7 +289,7 @@ static bool VerifyJumpTarget(PClassActor *cls, FState *CallingState, int index)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FState *FStateLabelStorage::GetState(int pos, PClassActor *cls)
|
||||
FState *FStateLabelStorage::GetState(int pos, PClassActor *cls, bool exact)
|
||||
{
|
||||
if (pos > 0x10000000)
|
||||
{
|
||||
|
@ -322,7 +322,7 @@ FState *FStateLabelStorage::GetState(int pos, PClassActor *cls)
|
|||
else if (cls != nullptr)
|
||||
{
|
||||
FName *labels = (FName*)&Storage[pos + sizeof(int)];
|
||||
return cls->FindState(val, labels, false);
|
||||
return cls->FindState(val, labels, exact);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -337,8 +337,9 @@ FState *FStateLabelStorage::GetState(int pos, PClassActor *cls)
|
|||
DEFINE_ACTION_FUNCTION(AActor, FindState)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_STATE(newstate);
|
||||
ACTION_RETURN_STATE(newstate);
|
||||
PARAM_INT(newstate);
|
||||
PARAM_BOOL_DEF(exact)
|
||||
ACTION_RETURN_STATE(StateLabels.GetState(newstate, self->GetClass(), exact));
|
||||
}
|
||||
|
||||
// same as above but context aware.
|
||||
|
|
|
@ -230,6 +230,7 @@ static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCall
|
|||
{
|
||||
PSymbolVMFunction *symfunc = new PSymbolVMFunction(funcname);
|
||||
VMNativeFunction *calldec = new VMNativeFunction(func, funcname);
|
||||
calldec->PrintableName = funcname.GetChars();
|
||||
symfunc->Function = calldec;
|
||||
sym = symfunc;
|
||||
GlobalSymbols.AddSymbol(sym);
|
||||
|
@ -1428,6 +1429,14 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
|
|||
delete this;
|
||||
return x;
|
||||
}
|
||||
else if (ValueType == TypeSpriteID && basex->IsInteger())
|
||||
{
|
||||
basex->ValueType = TypeSpriteID;
|
||||
auto x = basex;
|
||||
basex = nullptr;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
else if (ValueType == TypeStateLabel)
|
||||
{
|
||||
if (basex->ValueType == TypeNullPtr)
|
||||
|
|
|
@ -581,17 +581,21 @@ AFuncDesc *FindFunction(PStruct *cls, const char * string)
|
|||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
// Since many functions have been declared with Actor as owning class, despited being members of something else, let's hack around this until they have been fixed or exported.
|
||||
// Since many functions have been declared with Actor as owning class, despite being members of something else, let's hack around this until they have been fixed or exported.
|
||||
// Since most of these are expected to be scriptified anyway, there's no point fixing them all before they get exported.
|
||||
if (i == 1 && !cls->IsKindOf(RUNTIME_CLASS(PClassActor))) break;
|
||||
if (i == 1)
|
||||
{
|
||||
if (!cls->IsKindOf(RUNTIME_CLASS(PClassActor))) break;
|
||||
cls = RUNTIME_CLASS(AActor);
|
||||
}
|
||||
|
||||
FStringf fullname("%s_%s", i == 0 ? cls->TypeName.GetChars() : "Actor", string);
|
||||
int min = 0, max = AFTable.Size() - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int mid = (min + max) / 2;
|
||||
int lexval = stricmp(fullname, AFTable[mid].Name + 1);
|
||||
int lexval = stricmp(cls->TypeName.GetChars(), AFTable[mid].ClassName + 1);
|
||||
if (lexval == 0) lexval = stricmp(string, AFTable[mid].FuncName);
|
||||
if (lexval == 0)
|
||||
{
|
||||
return &AFTable[mid];
|
||||
|
@ -641,7 +645,10 @@ static int propcmp(const void * a, const void * b)
|
|||
|
||||
static int funccmp(const void * a, const void * b)
|
||||
{
|
||||
return stricmp(((AFuncDesc*)a)->Name + 1, ((AFuncDesc*)b)->Name + 1); // +1 to get past the prefix letter of the native class name, which gets omitted by the FName for the class.
|
||||
// +1 to get past the prefix letter of the native class name, which gets omitted by the FName for the class.
|
||||
int res = stricmp(((AFuncDesc*)a)->ClassName + 1, ((AFuncDesc*)b)->ClassName + 1);
|
||||
if (res == 0) res = stricmp(((AFuncDesc*)a)->FuncName, ((AFuncDesc*)b)->FuncName);
|
||||
return res;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -700,7 +707,8 @@ void InitThingdef()
|
|||
{
|
||||
AFuncDesc *afunc = (AFuncDesc *)*probe;
|
||||
assert(afunc->VMPointer != NULL);
|
||||
*(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->Name);
|
||||
*(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->FuncName);
|
||||
(*(afunc->VMPointer))->PrintableName.Format("%s.%s [Native]", afunc->ClassName+1, afunc->FuncName);
|
||||
AFTable.Push(*afunc);
|
||||
}
|
||||
AFTable.ShrinkToFit();
|
||||
|
|
|
@ -669,6 +669,7 @@ public:
|
|||
int VirtualIndex = -1;
|
||||
FName Name;
|
||||
TArray<VMValue> DefaultArgs;
|
||||
FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong.
|
||||
|
||||
class PPrototype *Proto;
|
||||
|
||||
|
@ -822,7 +823,6 @@ public:
|
|||
VM_UHALF NumKonstA;
|
||||
VM_UHALF MaxParam; // Maximum number of parameters this function has on the stack at once
|
||||
VM_UBYTE NumArgs; // Number of arguments this function takes
|
||||
FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong.
|
||||
TArray<FTypeAndOffset> SpecialInits; // list of all contents on the extra stack which require construction and destruction
|
||||
|
||||
void InitExtra(void *addr);
|
||||
|
@ -1015,7 +1015,8 @@ typedef int(*actionf_p)(VMFrameStack *stack, VMValue *param, TArray<VMValue> &de
|
|||
|
||||
struct AFuncDesc
|
||||
{
|
||||
const char *Name;
|
||||
const char *ClassName;
|
||||
const char *FuncName;
|
||||
actionf_p Function;
|
||||
VMNativeFunction **VMPointer;
|
||||
};
|
||||
|
@ -1037,7 +1038,7 @@ struct AFuncDesc
|
|||
#define DEFINE_ACTION_FUNCTION(cls, name) \
|
||||
static int AF_##cls##_##name(VM_ARGS); \
|
||||
VMNativeFunction *cls##_##name##_VMPtr; \
|
||||
static const AFuncDesc cls##_##name##_Hook = { #cls "_" #name, AF_##cls##_##name, &cls##_##name##_VMPtr }; \
|
||||
static const AFuncDesc cls##_##name##_Hook = { #cls, #name, AF_##cls##_##name, &cls##_##name##_VMPtr }; \
|
||||
extern AFuncDesc const *const cls##_##name##_HookPtr; \
|
||||
MSVC_ASEG AFuncDesc const *const cls##_##name##_HookPtr GCC_ASEG = &cls##_##name##_Hook; \
|
||||
static int AF_##cls##_##name(VM_ARGS)
|
||||
|
|
|
@ -887,7 +887,7 @@ void FFunctionBuildList::Build()
|
|||
catch (CRecoverableError &err)
|
||||
{
|
||||
// catch errors from the code generator and pring something meaningful.
|
||||
item.Code->ScriptPosition.Message(MSG_ERROR, "%s in %s", err.GetMessage(), item.PrintableName);
|
||||
item.Code->ScriptPosition.Message(MSG_ERROR, "%s in %s", err.GetMessage(), item.PrintableName.GetChars());
|
||||
}
|
||||
}
|
||||
delete item.Code;
|
||||
|
|
|
@ -261,7 +261,6 @@ void VMDumpConstants(FILE *out, const VMScriptFunction *func)
|
|||
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func)
|
||||
{
|
||||
VMFunction *callfunc;
|
||||
const char *callname;
|
||||
const char *name;
|
||||
int col;
|
||||
int mode;
|
||||
|
@ -497,8 +496,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
|
|||
}
|
||||
else if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K)
|
||||
{
|
||||
callname = callfunc->IsKindOf(RUNTIME_CLASS(VMScriptFunction)) ? static_cast<VMScriptFunction*>(callfunc)->PrintableName : callfunc->Name != NAME_None ? callfunc->Name : "[anonfunc]";
|
||||
printf_wrapper(out, " [%s]\n", callname);
|
||||
printf_wrapper(out, " [%s]\n", callfunc->PrintableName.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -787,11 +787,11 @@ type_list(X) ::= type_list(A) COMMA type_or_array(B). { X = A; /*X-overwrites-A*
|
|||
type_list_or_void(X) ::= VOID. { X = NULL; }
|
||||
type_list_or_void(X) ::= type_list(X).
|
||||
|
||||
array_size_expr(X) ::= LBRACKET opt_expr(A) RBRACKET.
|
||||
array_size_expr(X) ::= LBRACKET(L) opt_expr(A) RBRACKET.
|
||||
{
|
||||
if (A == NULL)
|
||||
{
|
||||
NEW_AST_NODE(Expression,nil,A);
|
||||
NEW_AST_NODE(Expression,nil,L.SourceLoc);
|
||||
nil->Operation = PEX_Nil;
|
||||
nil->Type = NULL;
|
||||
X = nil;
|
||||
|
|
|
@ -2304,14 +2304,14 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
{
|
||||
if (vindex == -1)
|
||||
{
|
||||
Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
|
||||
Error(f, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto oldfunc = clstype->Virtuals[vindex];
|
||||
if (oldfunc->Final)
|
||||
{
|
||||
Error(p, "Attempt to override final function %s", FName(f->Name).GetChars());
|
||||
Error(f, "Attempt to override final function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
clstype->Virtuals[vindex] = sym->Variants[0].Implementation;
|
||||
sym->Variants[0].Implementation->VirtualIndex = vindex;
|
||||
|
|
|
@ -55,7 +55,7 @@ inline int GetVirtualIndex(PClass *cls, const char *funcname)
|
|||
static int VIndex = -1; \
|
||||
if (VIndex < 0) { \
|
||||
VIndex = GetVirtualIndex(RUNTIME_CLASS(cls), #funcname); \
|
||||
if (VIndex < 0) I_Error("Unable to find virtual function in " #cls, #funcname); \
|
||||
if (VIndex < 0) I_Error("Unable to find virtual function %s in " #cls, #funcname); \
|
||||
}
|
||||
|
||||
#define VFUNC this->GetClass()->Virtuals[VIndex]
|
||||
|
|
|
@ -115,7 +115,7 @@ class Actor : Thinker native
|
|||
native void NewChaseDir();
|
||||
native bool CheckMissileRange();
|
||||
native bool SetState(state st, bool nofunction = false);
|
||||
native state FindState(statelabel st); // do we need exact later?
|
||||
native state FindState(statelabel st, bool exact = false); // do we need exact later?
|
||||
bool SetStateLabel(statelabel st, bool nofunction = false) { return SetState(FindState(st), nofunction); }
|
||||
native action state ResolveState(statelabel st); // this one, unlike FindState, is context aware.
|
||||
native void LinkToWorld();
|
||||
|
@ -369,8 +369,6 @@ class Actor : Thinker native
|
|||
native void A_Detonate();
|
||||
native bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0);
|
||||
|
||||
native void A_M_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff");
|
||||
|
||||
native void A_ActiveSound();
|
||||
|
||||
native void A_FastChase();
|
||||
|
|
|
@ -1,8 +1,35 @@
|
|||
|
||||
// Scriptable marine -------------------------------------------------------
|
||||
|
||||
class ScriptedMarine : Actor native
|
||||
class ScriptedMarine : Actor
|
||||
{
|
||||
const MARINE_PAIN_CHANCE = 160;
|
||||
|
||||
enum EMarineWeapon
|
||||
{
|
||||
WEAPON_Dummy,
|
||||
WEAPON_Fist,
|
||||
WEAPON_BerserkFist,
|
||||
WEAPON_Chainsaw,
|
||||
WEAPON_Pistol,
|
||||
WEAPON_Shotgun,
|
||||
WEAPON_SuperShotgun,
|
||||
WEAPON_Chaingun,
|
||||
WEAPON_RocketLauncher,
|
||||
WEAPON_PlasmaRifle,
|
||||
WEAPON_Railgun,
|
||||
WEAPON_BFG
|
||||
};
|
||||
|
||||
struct WeaponStates
|
||||
{
|
||||
state melee;
|
||||
state missile;
|
||||
}
|
||||
|
||||
int CurrentWeapon;
|
||||
SpriteID SpriteOverride;
|
||||
|
||||
Default
|
||||
{
|
||||
Health 100;
|
||||
|
@ -10,7 +37,7 @@ class ScriptedMarine : Actor native
|
|||
Height 56;
|
||||
Mass 100;
|
||||
Speed 8;
|
||||
Painchance 160;
|
||||
Painchance MARINE_PAIN_CHANCE;
|
||||
MONSTER;
|
||||
-COUNTKILL
|
||||
Translation 0;
|
||||
|
@ -19,23 +46,6 @@ class ScriptedMarine : Actor native
|
|||
PainSound "*pain50";
|
||||
}
|
||||
|
||||
native void A_M_Refire (bool ignoremissile=false);
|
||||
native void A_M_CheckAttack ();
|
||||
native void A_MarineChase ();
|
||||
native void A_MarineLook ();
|
||||
native void A_MarineNoise ();
|
||||
native void A_M_Punch (int force);
|
||||
native void A_M_SawRefire ();
|
||||
native void A_M_FirePistol (bool accurate);
|
||||
native void A_M_FireShotgun ();
|
||||
native void A_M_FireShotgun2 ();
|
||||
native void A_M_FireCGun(bool accurate);
|
||||
native void A_M_FireMissile ();
|
||||
native void A_M_FirePlasma ();
|
||||
native void A_M_FireRailgun ();
|
||||
native void A_M_BFGsound ();
|
||||
native void A_M_FireBFG ();
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
|
@ -58,25 +68,22 @@ class ScriptedMarine : Actor native
|
|||
PLAY E 4 A_FaceTarget;
|
||||
PLAY E 4 A_M_Punch(1);
|
||||
PLAY A 9;
|
||||
PLAY A 0 A_M_Refire(1);
|
||||
PLAY A 0 A_M_Refire(1, "FistEnd");
|
||||
Loop;
|
||||
FistEnd:
|
||||
PLAY A 5 A_FaceTarget;
|
||||
Goto See;
|
||||
Melee.Berserk:
|
||||
PLAY E 4 A_FaceTarget;
|
||||
PLAY E 4 A_M_Punch(10);
|
||||
PLAY A 9;
|
||||
PLAY A 0 A_M_Refire(1);
|
||||
PLAY A 0 A_M_Refire(1, "FistEnd");
|
||||
Loop;
|
||||
PLAY A 5 A_FaceTarget;
|
||||
Goto See;
|
||||
Melee.Chainsaw:
|
||||
PLAY E 4 A_MarineNoise;
|
||||
PLAY E 4 A_M_Saw;
|
||||
PLAY E 0 A_M_SawRefire;
|
||||
goto Melee.Chainsaw+1;
|
||||
PLAY A 0;
|
||||
Goto See;
|
||||
|
||||
Missile:
|
||||
Missile.None:
|
||||
|
@ -88,16 +95,15 @@ class ScriptedMarine : Actor native
|
|||
PLAY E 4 A_FaceTarget;
|
||||
PLAY F 6 BRIGHT A_M_FirePistol(1);
|
||||
PLAY A 4 A_FaceTarget;
|
||||
PLAY A 0 A_M_Refire;
|
||||
PLAY A 0 A_M_Refire(0, "ShootEnd");
|
||||
ShootEnd:
|
||||
PLAY A 5;
|
||||
Goto See;
|
||||
Fireloop.Pistol:
|
||||
PLAY F 6 BRIGHT A_M_FirePistol(0);
|
||||
PLAY A 4 A_FaceTarget;
|
||||
PLAY A 0 A_M_Refire;
|
||||
PLAY A 0 A_M_Refire(0, "ShootEnd");
|
||||
Goto Fireloop.Pistol;
|
||||
PLAY A 5;
|
||||
Goto See;
|
||||
Missile.Shotgun:
|
||||
PLAY E 3 A_M_CheckAttack;
|
||||
PLAY F 7 BRIGHT A_M_FireShotgun;
|
||||
|
@ -110,26 +116,20 @@ class ScriptedMarine : Actor native
|
|||
PLAY E 4 A_FaceTarget;
|
||||
PLAY FF 4 BRIGHT A_M_FireCGun(1);
|
||||
PLAY FF 4 BRIGHT A_M_FireCGun(0);
|
||||
PLAY A 0 A_M_Refire;
|
||||
PLAY A 0 A_M_Refire(0, "See");
|
||||
Goto Missile.Chaingun+3;
|
||||
PLAY A 0;
|
||||
Goto See;
|
||||
Missile.Rocket:
|
||||
PLAY E 8;
|
||||
PLAY F 6 BRIGHT A_M_FireMissile;
|
||||
PLAY E 6;
|
||||
PLAY A 0 A_M_Refire;
|
||||
PLAY A 0 A_M_Refire(0, "See");
|
||||
Loop;
|
||||
PLAY A 0;
|
||||
Goto See;
|
||||
Missile.Plasma:
|
||||
PLAY E 2 A_FaceTarget;
|
||||
PLAY E 0 A_FaceTarget;
|
||||
PLAY F 3 BRIGHT A_M_FirePlasma;
|
||||
PLAY A 0 A_M_Refire;
|
||||
PLAY A 0 A_M_Refire(0, "See");
|
||||
Goto Missile.Plasma+1;
|
||||
PLAY A 0;
|
||||
Goto See;
|
||||
Missile.Railgun:
|
||||
PLAY E 4 A_M_CheckAttack;
|
||||
PLAY F 6 BRIGHT A_M_FireRailgun;
|
||||
|
@ -139,10 +139,8 @@ class ScriptedMarine : Actor native
|
|||
PLAY EEEEE 5 A_FaceTarget;
|
||||
PLAY F 6 BRIGHT A_M_FireBFG;
|
||||
PLAY A 4 A_FaceTarget;
|
||||
PLAY A 0 A_M_Refire;
|
||||
PLAY A 0 A_M_Refire(0, "See");
|
||||
Loop;
|
||||
PLAY A 0;
|
||||
Goto See;
|
||||
|
||||
SkipAttack:
|
||||
PLAY A 1;
|
||||
|
@ -169,6 +167,541 @@ class ScriptedMarine : Actor native
|
|||
PLAY MLKJIH 5;
|
||||
Goto See;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
private bool GetWeaponStates(int weap, WeaponStates wstates)
|
||||
{
|
||||
static const statelabel MeleeNames[] =
|
||||
{
|
||||
"Melee.None", "Melee.Fist", "Melee.Berserk", "Melee.Chainsaw", "Melee.Pistol", "Melee.Shotgun",
|
||||
"Melee.SSG", "Melee.Chaingun", "Melee.Rocket", "Melee.Plasma", "Melee.Railgun", "Melee.BFG"
|
||||
};
|
||||
|
||||
static const statelabel MissileNames[] =
|
||||
{
|
||||
"Missile.None", "Missile.Fist", "Missile.Berserk", "Missile.Chainsaw", "Missile.Pistol", "Missile.Shotgun",
|
||||
"Missile.SSG", "Missile.Chaingun", "Missile.Rocket", "Missile.Plasma", "Missile.Railgun", "Missile.BFG"
|
||||
};
|
||||
|
||||
if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy;
|
||||
|
||||
wstates.melee = FindState(MeleeNames[weap], true);
|
||||
wstates.missile = FindState(MissileNames[weap], true);
|
||||
|
||||
return wstates.melee != null || wstates.missile != null;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
override void BeginPlay ()
|
||||
{
|
||||
Super.BeginPlay ();
|
||||
|
||||
// Set the current weapon
|
||||
for(int i = WEAPON_Dummy; i <= WEAPON_BFG; i++)
|
||||
{
|
||||
WeaponStates wstates;
|
||||
if (GetWeaponStates(i, wstates))
|
||||
{
|
||||
if (wstates.melee == MeleeState && wstates.missile == MissileState)
|
||||
{
|
||||
CurrentWeapon = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
// placeholder to make it compile for the time being.
|
||||
SpriteID GetSprite(State st)
|
||||
{
|
||||
return SpriteID(0);
|
||||
}
|
||||
|
||||
override void Tick ()
|
||||
{
|
||||
Super.Tick ();
|
||||
|
||||
// Override the standard sprite, if desired
|
||||
if (SpriteOverride != 0 && sprite == GetSprite(SpawnState))
|
||||
{
|
||||
sprite = SpriteOverride;
|
||||
}
|
||||
|
||||
if (special1 != 0)
|
||||
{
|
||||
if (CurrentWeapon == WEAPON_SuperShotgun)
|
||||
{ // Play SSG reload sounds
|
||||
int ticks = level.maptime - special1;
|
||||
if (ticks < 47)
|
||||
{
|
||||
switch (ticks)
|
||||
{
|
||||
case 14:
|
||||
A_PlaySound ("weapons/sshoto", CHAN_WEAPON);
|
||||
break;
|
||||
case 28:
|
||||
A_PlaySound ("weapons/sshotl", CHAN_WEAPON);
|
||||
break;
|
||||
case 41:
|
||||
A_PlaySound ("weapons/sshotc", CHAN_WEAPON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
special1 = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Wait for a long refire time
|
||||
if (level.maptime >= special1)
|
||||
{
|
||||
special1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bJustAttacked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_Refire
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_Refire (bool ignoremissile, statelabel jumpto)
|
||||
{
|
||||
if (target == null || target.health <= 0)
|
||||
{
|
||||
if (MissileState && random[SMarineRefire]() < 160)
|
||||
{ // Look for a new target most of the time
|
||||
if (LookForPlayers (true) && CheckMissileRange ())
|
||||
{ // Found somebody new and in range, so don't stop shooting
|
||||
return;
|
||||
}
|
||||
}
|
||||
SetStateLabel (jumpto);
|
||||
return;
|
||||
}
|
||||
if (((ignoremissile || MissileState == null) && !CheckMeleeRange ()) ||
|
||||
!CheckSight (target) || random[SMarineRefire]() < 4) // Small chance of stopping even when target not dead
|
||||
{
|
||||
SetStateLabel (jumpto);
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_SawRefire
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_SawRefire ()
|
||||
{
|
||||
if (target == null || target.health <= 0 || !CheckMeleeRange ())
|
||||
{
|
||||
SetStateLabel ("See");
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_MarineNoise
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_MarineNoise ()
|
||||
{
|
||||
if (CurrentWeapon == WEAPON_Chainsaw)
|
||||
{
|
||||
A_PlaySound ("weapons/sawidle", CHAN_WEAPON);
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_MarineChase
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_MarineChase ()
|
||||
{
|
||||
A_MarineNoise();
|
||||
A_Chase ();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_MarineLook
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_MarineLook ()
|
||||
{
|
||||
A_MarineNoise();
|
||||
A_Look();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_Punch (also used in the rocket attack.)
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_Punch(int damagemul)
|
||||
{
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
int damage = (random[SMarinePunch](1, 10) << 1) * damagemul;
|
||||
|
||||
A_FaceTarget ();
|
||||
double ang = angle + random2[SMarinePunch]() * (5.625 / 256);
|
||||
double pitch = AimLineAttack (ang, MELEERANGE);
|
||||
LineAttack (ang, MELEERANGE, pitch, damage, 'Melee', "BulletPuff", true, t);
|
||||
|
||||
// turn to face target
|
||||
if (t.linetarget)
|
||||
{
|
||||
A_PlaySound ("*fist", CHAN_WEAPON);
|
||||
angle = t.angleFromSource;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// P_GunShot2
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
private void GunShot2 (bool accurate, double pitch, class<Actor> pufftype)
|
||||
{
|
||||
int damage = 5 * random[SMarineGunshot](1,3);
|
||||
double ang = angle;
|
||||
|
||||
if (!accurate)
|
||||
{
|
||||
ang += Random2[SMarineGunshot]() * (5.625 / 256);
|
||||
}
|
||||
|
||||
LineAttack (ang, MISSILERANGE, pitch, damage, 'Hitscan', pufftype);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FirePistol
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FirePistol (bool accurate)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_PlaySound ("weapons/pistol", CHAN_WEAPON);
|
||||
A_FaceTarget ();
|
||||
GunShot2 (accurate, AimLineAttack (angle, MISSILERANGE), "BulletPuff");
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireShotgun
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FireShotgun ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_PlaySound ("weapons/shotgf", CHAN_WEAPON);
|
||||
A_FaceTarget ();
|
||||
double pitch = AimLineAttack (angle, MISSILERANGE);
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
GunShot2 (false, pitch, "BulletPuff");
|
||||
}
|
||||
special1 = level.maptime + 27;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_CheckAttack
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_CheckAttack ()
|
||||
{
|
||||
if (special1 != 0 || target == null)
|
||||
{
|
||||
SetStateLabel ("SkipAttack");
|
||||
}
|
||||
else
|
||||
{
|
||||
A_FaceTarget ();
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireShotgun2
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FireShotgun2 ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_PlaySound ("weapons/sshotf", CHAN_WEAPON);
|
||||
A_FaceTarget ();
|
||||
double pitch = AimLineAttack (angle, MISSILERANGE);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
int damage = 5*(random[SMarineFireSSG]()%3+1);
|
||||
double ang = angle + Random2[SMarineFireSSG]() * (11.25 / 256);
|
||||
|
||||
LineAttack (ang, MISSILERANGE, pitch + Random2[SMarineFireSSG]() * (7.097 / 256), damage, 'Hitscan', "BulletPuff");
|
||||
}
|
||||
special1 = level.maptime;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireCGun
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FireCGun(bool accurate)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_PlaySound ("weapons/chngun", CHAN_WEAPON);
|
||||
A_FaceTarget ();
|
||||
GunShot2 (accurate, AimLineAttack (angle, MISSILERANGE), "BulletPuff");
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireMissile
|
||||
//
|
||||
// Giving a marine a rocket launcher is probably a bad idea unless you pump
|
||||
// up his health, because he's just as likely to kill himself as he is to
|
||||
// kill anything else with it.
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FireMissile ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
if (CheckMeleeRange ())
|
||||
{ // If too close, punch it
|
||||
A_M_Punch(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_FaceTarget ();
|
||||
SpawnMissile (target, "Rocket");
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireRailgun
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FireRailgun ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_MonsterRail();
|
||||
special1 = level.maptime + 50;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FirePlasma
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FirePlasma ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_FaceTarget ();
|
||||
SpawnMissile (target, "PlasmaBall");
|
||||
special1 = level.maptime + 20;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_BFGsound
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_BFGsound ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
if (special1 != 0)
|
||||
{
|
||||
SetState (SeeState);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_FaceTarget ();
|
||||
A_PlaySound ("weapons/bfgf", CHAN_WEAPON);
|
||||
// Don't interrupt the firing sequence
|
||||
PainChance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_FireBFG
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_FireBFG ()
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
A_FaceTarget ();
|
||||
SpawnMissile (target, "BFGBall");
|
||||
special1 = level.maptime + 30;
|
||||
PainChance = MARINE_PAIN_CHANCE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
final void SetWeapon (int type)
|
||||
{
|
||||
WeaponStates wstates;
|
||||
if (GetWeaponStates(type, wstates))
|
||||
{
|
||||
static const class<Actor> classes[] = {
|
||||
"ScriptedMarine",
|
||||
"MarineFist",
|
||||
"MarineBerserk",
|
||||
"MarineChainsaw",
|
||||
"MarinePistol",
|
||||
"MarineShotgun",
|
||||
"MarineSSG",
|
||||
"MarineChaingun",
|
||||
"MarineRocket",
|
||||
"MarinePlasma",
|
||||
"MarineRailgun",
|
||||
"MarineBFG"
|
||||
};
|
||||
|
||||
MeleeState = wstates.melee;
|
||||
MissileState = wstates.missile;
|
||||
DecalGenerator = GetDefaultByType(classes[type]).DecalGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
final void SetSprite (class<Actor> source)
|
||||
{
|
||||
if (source == null)
|
||||
{ // A valid actor class wasn't passed, so use the standard sprite
|
||||
SpriteOverride = sprite = GetSprite(SpawnState);
|
||||
// Copy the standard scaling
|
||||
Scale = Default.Scale;
|
||||
}
|
||||
else
|
||||
{ // Use the same sprite and scaling the passed class spawns with
|
||||
readonly<Actor> def = GetDefaultByType (source);
|
||||
SpriteOverride = sprite = GetSprite(def.SpawnState);
|
||||
Scale = def.Scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extend class Actor
|
||||
{
|
||||
//============================================================================
|
||||
//
|
||||
// A_M_Saw (this is globally exported)
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_M_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff")
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
if (pufftype == null) pufftype = "BulletPuff";
|
||||
if (damage == 0) damage = 2;
|
||||
|
||||
A_FaceTarget ();
|
||||
if (CheckMeleeRange ())
|
||||
{
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
damage *= random[SMarineSaw](1, 10);
|
||||
double ang = angle + Random2[SMarineSaw]() * (5.625 / 256);
|
||||
|
||||
LineAttack (angle, SAWRANGE, AimLineAttack (angle, SAWRANGE), damage, 'Melee', pufftype, false, t);
|
||||
|
||||
if (!t.linetarget)
|
||||
{
|
||||
A_PlaySound (fullsound, 1, CHAN_WEAPON);
|
||||
return;
|
||||
}
|
||||
A_PlaySound (hitsound, CHAN_WEAPON);
|
||||
|
||||
// turn to face target
|
||||
ang = t.angleFromSource;
|
||||
double anglediff = deltaangle(angle, ang);
|
||||
|
||||
if (anglediff < 0.0)
|
||||
{
|
||||
if (anglediff < -4.5)
|
||||
angle = ang + 90.0 / 21;
|
||||
else
|
||||
angle -= 4.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anglediff > 4.5)
|
||||
angle = ang - 90.0 / 21;
|
||||
else
|
||||
angle += 4.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
A_PlaySound (fullsound, 1, CHAN_WEAPON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue