//************************************************************************** //** //** ## ## ## ## ## #### #### ### ### //** ## ## ## ## ## ## ## ## ## ## #### #### //** ## ## ## ## ## ## ## ## ## ## ## ## ## ## //** ## ## ######## ## ## ## ## ## ## ## ### ## //** ### ## ## ### ## ## ## ## ## ## //** # ## ## # #### #### ## ## //** //** $Id: ScriptedEntity.vc 2681 2007-08-27 22:26:06Z dj_jl $ //** //** Copyright (C) 1999-2006 Jānis Legzdiņš //** //** This program is free software; you can redistribute it and/or //** modify it under the terms of the GNU General Public License //** as published by the Free Software Foundation; either version 2 //** of the License, or (at your option) any later version. //** //** This program is distributed in the hope that it will be useful, //** but WITHOUT ANY WARRANTY; without even the implied warranty of //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //** GNU General Public License for more details. //** //************************************************************************** //** //** Class for Actor state action methods. This will eventually be //** renamed to Actor. //** //************************************************************************** class ScriptedEntity : EntityEx abstract; //========================================================================== // // A_Look // // Stay in state until a player is sighted // //========================================================================== final void A_Look() { EntityEx targ; Threshold = 0; // any shot will wake up targ = EntityEx(Sector->SoundTarget); if (targ && targ.bShootable) { Target = targ; if (bAmbush) { if (!CanSee(Target)) { if (!LookForPlayers(false)) return; } } } else { if (!LookForPlayers(bLookAllAround)) return; } // go into chase state if (SightSound) { if (bBoss) { // Full volume PlaySound(SightSound, CHAN_VOICE, 1.0, ATTN_NONE); } else { PlaySound(SightSound, CHAN_VOICE); } } SetState(SeeState); } //========================================================================== // // A_Stand // //========================================================================== final void A_Stand() { EntityEx targ; Threshold = 0; // any shot will wake up targ = EntityEx(Sector->SoundTarget); if (!bNeutral && targ && targ.bShootable) { if (bFriendly != targ.bFriendly || Level.bNoAllies) { Target = targ; if (!bStandMustSeeTarget || CanSee(targ)) { Threshold = 10; SetState(SeeState); return; } } else { if (LookForPlayers(bLookAllAround)) { SetState(SeeState); bInCombat = true; return; } } } if (P_Random() < 30) { if (!(P_Random() & 1)) SetState(FindState('LookRight')); else SetState(FindState('LookLeft')); } if (!bStanding && P_Random() < 40) { SetState(FindState('Walk')); } } //========================================================================== // // A_Walk // //========================================================================== final void A_Walk() { float delta; // Remove muzzle flash if (bMuzzleFlash) { bMuzzleFlash = false; } bInCombat = false; if (bStanding) return; if (Threshold != 0) { Threshold--; return; } // turn towards movement direction if not there yet if (MoveDir < DI_NODIR) { Angles.yaw = itof(ftoi(Angles.yaw / 45.0)) * 45.0; delta = AngleMod180(Angles.yaw - itof(MoveDir) * 45.0); if (delta > 0.0) { Angles.yaw = AngleMod360(Angles.yaw - 45.0); } else if (delta < 0.0) { Angles.yaw = AngleMod360(Angles.yaw + 45.0); } } if (--MoveCount < 0 || !StepMove()) { RandomChaseDir(); MoveCount += 5; } } //========================================================================== // // A_Chase // // Actor has a melee attack, so it tries to close as fast as possible // //========================================================================== final void A_Chase() { float delta; if (ReactionCount) { ReactionCount--; } // Remove muzzle flash if (bMuzzleFlash) { bMuzzleFlash = false; } bInCombat = true; // modify target threshold if (Threshold) { if (!Target || Target.Health <= 0) { Threshold = 0; } else { Threshold--; } } if (LineSpecialGameInfo(Level.Game).bNightmareFastChase && Level.Game.fastparm) { // Monsters move faster in nightmare mode StateTime *= 0.5; if (StateTime < 0.1) { StateTime = 0.1; } } // turn towards movement direction if not there yet if (MoveDir < 8) { Angles.yaw = itof(ftoi(Angles.yaw / 45.0)) * 45.0; delta = AngleMod180(Angles.yaw - itof(MoveDir) * 45.0); if (delta > 0.0) { Angles.yaw = AngleMod360(Angles.yaw - 45.0); } else if (delta < 0.0) { Angles.yaw = AngleMod360(Angles.yaw + 45.0); } } if (!Target || !Target.bShootable) { // look for a new target if (LookForPlayers(true)) { return; // got a new target } SetState(IdleState); bInCombat = false; return; } // do not attack twice in a row if (bJustAttacked) { bJustAttacked = false; if (!Level.Game.fastparm) { NewChaseDir(); } return; } // check for melee attack if (MeleeState && CheckMeleeRange()) { if (AttackSound) { PlaySound(AttackSound, CHAN_WEAPON); } SetState(MeleeState); return; } // check for missile attack if (MissileState) { if (Level.Game.fastparm || !MoveCount) { if (CheckMissileRange()) { SetState(MissileState); bJustAttacked = true; return; } } } // possibly choose another target if (Level.Game.netgame && !Threshold) { if (!CanSee(Target)) { if (LookForPlayers(true)) return; // got a new target } } // chase towards player if (--MoveCount < 0 || !StepMove()) { NewChaseDir(); } // make active sound if (P_Random() < 3) { PlayActiveSound(); } } //========================================================================== // // A_FaceTarget // //========================================================================== final void A_FaceTarget() { if (!Target) return; bAmbush = false; Angles.yaw = atan2(Target.Origin.y - Origin.y, Target.Origin.x - Origin.x); if (Target.Alpha < 1.0) { Angles.yaw = AngleMod360(Angles.yaw + (Random() - Random()) * 45.0); } } //========================================================================== // // A_Pain // //========================================================================== final void A_Pain() { // Remove muzzle flash if (bMuzzleFlash) { bMuzzleFlash = false; } if (bIsPlayer && !PlayerIsMorphed()) { if (Health < 25) { PlaySound('*pain25', CHAN_VOICE); } else if (Health < 50) { PlaySound('*pain50', CHAN_VOICE); } else if (Health < 75) { PlaySound('*pain75', CHAN_VOICE); } else { PlaySound('*pain100', CHAN_VOICE); } } else if (PainSound) { PlaySound(PainSound, CHAN_VOICE); } } //========================================================================== // // A_Scream // // Death sound. // //========================================================================== final void A_Scream() { // Remove muzzle flash & other light effects if (bDynamicLight || bMuzzleFlash) { bDynamicLight = false; bMuzzleFlash = false; } if (DeathSound) { // Make boss death sounds full volume PlaySound(DeathSound, CHAN_VOICE, 1.0, bBoss ? ATTN_NONE : ATTN_NORMAL); } } //========================================================================== // // A_XScream // // Explode death sound. // //========================================================================== final void A_XScream() { // Remove muzzle flash if (bMuzzleFlash) { bMuzzleFlash = false; } if (bIsPlayer) { PlaySound('*gibbed', CHAN_BODY); } else { PlaySound('misc/gibbed', CHAN_BODY); } } //========================================================================== // // A_XXScream // // Strife version of A_XScream. // //========================================================================== final void A_XXScream() { // Remove muzzle flash if (bMuzzleFlash) { bMuzzleFlash = false; } if (!bNoBlood || !DeathSound) { A_XScream(); } else { PlaySound(DeathSound, CHAN_VOICE); } } //========================================================================== // // A_NoBlocking // // Unset blocking. // //========================================================================== final void A_NoBlocking() { int SpeechNum; RogueConSpeech* Speech; class DropItemType; // Remove muzzle flash if (bMuzzleFlash) { bMuzzleFlash = false; } // actor is on ground, it can be walked over bSolid = false; SpeechNum = GetSpeech(); if (SpeechNum) { // Drop stuff as determined by conversation. if (SpeechNum < 0) { Speech = &XLevel.GenericSpeeches[-SpeechNum - 1]; } else { Speech = &XLevel.LevelSpeeches[SpeechNum - 1]; } DropItemType = LineSpecialLevelInfo(Level).GetClassFromID(Speech->DropItem); if (DropItemType) { DropItem(DropItemType, 0, 1.0); return; } } // Check for monsters dropping things NoBlockingSet(); } //========================================================================== // // A_Fall // // For compatibility // //========================================================================== final void A_Fall() { A_NoBlocking(); } //========================================================================== // // A_RemoveForceField // //========================================================================== final void A_RemoveForceField() { int i; line_t* line; bSpecial = false; for (i = 0; i < Sector->linecount; i++) { line = Sector->lines[i]; if (line->backsector && line->special == LNSPEC_ForceField) { line->flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING); line->special = 0; XLevel.Sides[line->sidenum[0]].midtexture = 0; XLevel.Sides[line->sidenum[1]].midtexture = 0; } } } //========================================================================== // // A_SetShadow // //========================================================================== final void A_SetShadow() { bUnknown2 = true; Alpha = 0.333; } //========================================================================== // // A_ClearShadow // //========================================================================== final void A_ClearShadow() { bUnknown2 = false; Alpha = 1.0; } //========================================================================== // // A_BeShadowyFoe // //========================================================================== final void A_BeShadowyFoe() { Alpha = 0.333; bFriendly = false; } //========================================================================== // // A_Explode // // Handles a bunch of exploding things. // //========================================================================== final void A_Explode() { int damage; float distance; byte damageSelf; int i; damage = 128; distance = 128.0; damageSelf = true; PreExplode(); GetExplodeParms(damage, distance, damageSelf); RadiusAttack(Target, damage, distance, damageSelf); if (Origin.z <= FloorZ + distance) { HitFloorType(); } if (bExplodeParticles) { // Spawn explosion effects - dynamic light and particles for (i = 0; i < MAXPLAYERS; i++) { if (!Level.Game.Players[i]) continue; if (!Level.Game.Players[i].bSpawned) continue; PlayerEx(Level.Game.Players[i]).ClientParticleExplosion( DLightColour, Origin + vector(0.0, 0.0, Height * 0.5)); } } else if (ExplodeEffect) { SendExplosion(ExplodeEffect, Origin); } // Clear old dynamic light effect bDynamicLight = false; bLeaveTrail = false; } //========================================================================== // // A_ExplodeAndAlert // //========================================================================== final void A_ExplodeAndAlert() { A_Explode(); if (Target && Target.bIsPlayer) { LineSpecialLevelInfo(Level).NoiseAlert(Target, self); } } //========================================================================== // // A_BossDeath // // Possibly trigger special effects if on first boss level // //========================================================================== final void A_BossDeath() { name Cls = GetClassName(Class); if (!((Cls == 'BaronOfHell' && Level.bBaronSpecial) || (Cls == 'Cyberdemon' && Level.bCyberDemonSpecial) || (Cls == 'SpiderMastermind' && Level.bSpiderMastermindSpecial) || (Cls == 'Arachnotron' && Level.bMap07Special) || (Cls == 'Fatso' && Level.bMap07Special) || (Cls == 'Ironlich' && Level.bIronLichSpecial) || (Cls == 'Minotaur' && Level.bMinotaurSpecial) || (Cls == 'Sorcerer2' && Level.bDSparilSpecial))) { return; } if (!CheckBossDeath()) { return; } // victory! if (Level.bSpecialActionKillMonsters) { // Kill any remaining monsters LineSpecialLevelInfo(Level).P_Massacre(); } if (Level.bMap07Special) { if (GetClassName(Class) == 'Fatso') { Level.ExecuteActionSpecial(LNSPEC_FloorLowerToLowest, 666, 8, 0, 0, 0, NULL, 0, none); return; } if (GetClassName(Class) == 'Arachnotron') { Level.ExecuteActionSpecial(LNSPEC_FloorRaiseByTexture, 667, 8, 0, 0, 0, NULL, 0, none); return; } } else if (Level.bSpecialActionOpenDoor) { Level.ExecuteActionSpecial(LNSPEC_DoorOpen, 666, 64, 150, 0, 0, NULL, 0, none); return; } else if (Level.bSpecialActionLowerFloor) { Level.ExecuteActionSpecial(LNSPEC_FloorLowerToLowest, 666, 8, 0, 0, 0, NULL, 0, none); return; } Level.ExitLevel(0); } //========================================================================== // // A_FreeTargMobj // //========================================================================== final void A_FreeTargMobj() { Velocity = vector(0.0, 0.0, 0.0); Origin.z = CeilingZ + 4.0; Gravity = 1.0; bSolid = false; bShootable = false; bFloat = false; bCountKill = false; bMonster = false; bSkullFly = false; bNoGravity = true; bDropOff = true; bCorpse = true; bNoPassMobj = true; Alpha = 0.0; Player = none; bIsPlayer = false; Health = -1000; // Don't resurrect } //========================================================================== // // A_Metal // // Cyberdemon move with metal sound. // //========================================================================== final void A_Metal() { PlaySound('spider/walk', CHAN_BODY); A_Chase(); } //========================================================================== // // A_SpidRefire // // Spider mastermind refire. // //========================================================================== final void A_SpidRefire() { // keep firing unless target got out of sight A_FaceTarget(); if (P_Random() < 10) return; if (!Target || Target.Health <= 0 || !CanSee(Target)) { SetState(SeeState); } } //=========================================================================== // // A_NoGravity // //=========================================================================== final void A_NoGravity() { bNoGravity = true; } //========================================================================== // // A_SetInvulnerable // //========================================================================== final void A_SetInvulnerable() { bInvulnerable = true; } //========================================================================== // // A_UnSetInvulnerable // //========================================================================== final void A_UnSetInvulnerable() { bInvulnerable = false; } //========================================================================== // // A_HideThing // //========================================================================== final void A_HideThing() { bHidden = true; } //========================================================================== // // A_UnHideThing // //========================================================================== final void A_UnHideThing() { bHidden = false; } states { FreeTargMobj: TNT1 A 1050 A_FreeTargMobj Stop } defaultproperties { }