diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87c875d16f..19179a0c4d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/g_doom/a_doomglobal.h b/src/g_doom/a_doomglobal.h index 31d41f6c0a..73e3d3239a 100644 --- a/src/g_doom/a_doomglobal.h +++ b/src/g_doom/a_doomglobal.h @@ -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); diff --git a/src/g_doom/a_doommisc.cpp b/src/g_doom/a_doommisc.cpp index ca13ac7ee4..40c8e2f8d0 100644 --- a/src/g_doom/a_doommisc.cpp +++ b/src/g_doom/a_doommisc.cpp @@ -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" - diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp deleted file mode 100644 index e270af6b51..0000000000 --- a/src/g_doom/a_scriptedmarine.cpp +++ /dev/null @@ -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(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; - } -} diff --git a/src/info.h b/src/info.h index 5af51f34b8..4f2b4b2939 100644 --- a/src/info.h +++ b/src/info.h @@ -359,7 +359,7 @@ struct FStateLabelStorage } } - FState *GetState(int pos, PClassActor *cls); + FState *GetState(int pos, PClassActor *cls, bool exact = false); }; extern FStateLabelStorage StateLabels; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index da8f76d7f1..96e20d0227 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -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(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(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 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(activator)->SetWeapon ( - (AScriptedMarine::EMarineWeapon)STACK(1)); + SetMarineWeapon(activator, STACK(1)); } } sp -= 2; @@ -8973,19 +8999,19 @@ scriptwait: { if (STACK(2) != 0) { - AScriptedMarine *marine; - TActorIterator 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(activator)->SetSprite (type); + SetMarineSprite(activator, type); } } } diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 37a2d533ed..54485d6538 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -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(state->ActionFunc)->PrintableName.GetChars()); + owner->TypeName.GetChars(), state - owner->OwnedStates, state->ActionFunc->PrintableName.GetChars()); state->ActionFunc = nullptr; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d1a273ef25..1066d985af 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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. diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 3bc2dfbe1d..157badd879 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -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(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)) diff --git a/src/p_states.cpp b/src/p_states.cpp index a49d30af25..fde7a0276d 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -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. diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index cae95da34d..8d44e5c184 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 76770272d4..cdb896902a 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -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(); diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index ad42ee1ddc..7d56b32dbb 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -669,6 +669,7 @@ public: int VirtualIndex = -1; FName Name; TArray 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 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 &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) diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp index 04339f6eb1..74b7a34000 100644 --- a/src/scripting/vm/vmbuilder.cpp +++ b/src/scripting/vm/vmbuilder.cpp @@ -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; diff --git a/src/scripting/vm/vmdisasm.cpp b/src/scripting/vm/vmdisasm.cpp index d8fc16433a..cba67cbdc9 100644 --- a/src/scripting/vm/vmdisasm.cpp +++ b/src/scripting/vm/vmdisasm.cpp @@ -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(callfunc)->PrintableName : callfunc->Name != NAME_None ? callfunc->Name : "[anonfunc]"; - printf_wrapper(out, " [%s]\n", callname); + printf_wrapper(out, " [%s]\n", callfunc->PrintableName.GetChars()); } else { diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 3380959ec9..80057724cd 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -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; diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 27a02a9190..9682066b97 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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; diff --git a/src/virtual.h b/src/virtual.h index 01541cebab..8842eb891f 100644 --- a/src/virtual.h +++ b/src/virtual.h @@ -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] diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 53f0475135..87d2f8759b 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -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 pufftype = "BulletPuff"); - native void A_ActiveSound(); native void A_FastChase(); diff --git a/wadsrc/static/zscript/doom/scriptedmarine.txt b/wadsrc/static/zscript/doom/scriptedmarine.txt index 7a82513cb8..1096f24b50 100644 --- a/wadsrc/static/zscript/doom/scriptedmarine.txt +++ b/wadsrc/static/zscript/doom/scriptedmarine.txt @@ -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 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 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 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 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 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); + } + } } //---------------------------------------------------------------------------