#include "actor.h" #include "gi.h" #include "m_random.h" #include "s_sound.h" #include "d_player.h" #include "a_action.h" #include "p_local.h" #include "a_doomglobal.h" #include "a_strifeglobal.h" #include "p_enemy.h" #include "p_lnspec.h" #include "c_console.h" #include "thingdef/thingdef.h" // Notes so I don't forget them: // Strife does some extra stuff in A_Explode if a player caused the explosion. (probably NoiseAlert) // See the instructions @ 21249. // // Strife's FLOATSPEED is 5 and not 4. // // In P_CheckMissileRange, mobjtypes 53,54,55,56,57,58 shift the distance right 4 bits (some, but not all the acolytes) // mobjtypes 61,63,91 shift it right 1 bit // // When shooting missiles at something, if MF_SHADOW is set, the angle is adjusted with the formula: // angle += pr_spawnmissile.Random2() << 21 // When MF_STRIFEx4000000 is set, the angle is adjusted similarly: // angle += pr_spawnmissile.Random2() << 22 // Note that these numbers are different from those used by all the other Doom engine games. /* These mobjinfos have been converted: 0 ForceFieldGuard 1 StrifePlayer 2 WeaponSmith 3 BarKeep 4 Armorer 5 Medic 6 Peasant1 7 Peasant2 8 Peasant3 9 Peasant4 10 Peasant5 11 Peasant6 12 Peasant7 13 Peasant8 14 Peasant9 15 Peasant10 16 Peasant11 17 Peasant12 18 Peasant13 19 Peasant14 20 Peasant15 21 Peasant16 22 Peasant17 23 Peasant18 24 Peasant19 25 Peasant20 26 Peasant21 27 Peasant22 28 Zombie 29 AcolyteToBe 30 ZombieSpawner 31 Tank1 32 Tank2 33 Tank3 34 Tank4 35 Tank5 36 Tank6 37 KneelingGuy 38 Beggar1 39 Beggar2 40 Beggar3 41 Beggar4 42 Beggar5 43 Rebel1 44 Rebel2 45 Rebel3 46 Rebel4 47 Rebel5 48 Rebel6 49 Macil1 50 Macil2 51 RocketTrail 52 Reaver 53 AcolyteTan 54 AcolyteRed 55 AcolyteRust 56 AcolyteGray 57 AcolyteDGreen 58 AcolyteGold 59 AcolyteLGreen 60 AcolyteBlue 61 AcolyteShadow 62 Templar 63 Crusader 64 StrifeBishop 65 Oracle 66 Loremaster (aka Priest) 67 AlienSpectre1 68 AlienChunkSmall 69 AlienChunkLarge 70 AlienSpectre2 71 AlienSpectre3 72 AlienSpectre4 73 AlienSpectre5 74 EntityBoss 75 EntitySecond 76 EntityNest 77 EntityPod 78 SpectralLightningH1 79 SpectralLightningH2 80 SpectralLightningBall1 81 SpectralLightningBall2 82 SpectralLightningH3 83 SpectralLightningHTail 84 SpectralLightningBigBall1 85 SpectralLightningBigBall2 86 SpectralLightningV1 87 SpectralLightningV2 88 SpectralLightningSpot 89 SpectralLightningBigV1 90 SpectralLightningBigV2 91 Sentinel 92 Stalker 93 Inquisitor 94 InquisitorArm 95 Programmer 96 ProgrammerBase 97 LoreShot 98 LoreShot2 99 MiniMissile 100 CrusaderMissile 101 BishopMissile 102 ElectricBolt 103 PoisonBolt 104 SentinelFX1 105 SentinelFX2 106 HEGrenade 107 PhosphorousGrenade 108 InquisitorShot 109 PhosphorousFire 110 MaulerTorpedo 111 MaulerTorpedoWave 112 FlameMissile 113 FastFlameMissile 114 MaulerPuff 115 StrifePuff 116 StrifeSpark 117 Blood 118 TeleportFog 119 ItemFog 120 --- Doomednum is 14, which makes it a teleport destination. Don't know why it's in the mobjinfo table. 121 KlaxonWarningLight 122 CeilingTurret 123 Piston 124 Computer 125 MedPatch 126 MedicalKit 127 SurgeryKit 128 DegninOre 129 MetalArmor 130 LeatherArmor 131 WaterBottle 132 Mug 133 BaseKey 134 GovsKey 135 Passcard 136 IDBadge 137 PrisonKey 138 SeveredHand 139 Power1Key 140 Power2Key 141 Power3Key 142 GoldKey 143 IDCard 144 SilverKey 145 OracleKey 146 MilitaryID 147 OrderKey 148 WarehouseKey 149 BrassKey 150 RedCrystalKey 151 BlueCrystalKey 152 ChapelKey 153 CatacombKey 154 SecurityKey 155 CoreKey 156 MaulerKey 157 FactoryKey 158 MineKey 159 NewKey5 160 ShadowArmor 161 EnvironmentalSuit 162 GuardUniform 163 OfficersUniform 164 StrifeMap 165 Scanner 166 167 Targeter 168 Coin 169 Gold10 170 Gold25 171 Gold50 172 Gold300 173 BeldinsRing 174 OfferingChalice 175 Ear 176 Communicator 177 HEGrenadeRounds 178 PhosphorusGrenadeRounds 179 ClipOfBullets 180 BoxOfBullets 181 MiniMissiles 182 CrateOfMissiles 183 EnergyPod 184 EnergyPack 185 PoisonBolts 186 ElectricBolts 187 AmmoSatchel 188 AssaultGun 189 AssaultGunStanding 190 FlameThrower 191 FlameThrowerParts 192 MiniMissileLauncher 193 Mauler 194 StrifeCrossbow 195 StrifeGrenadeLauncher 196 Sigil1 197 Sigil2 198 Sigil3 199 Sigil4 200 Sigil5 201 PowerCrystal 202 RatBuddy 203 WoodenBarrel 204 ExplosiveBarrel2 205 TargetPractice 206 LightSilverFluorescent 207 LightBrownFluorescent 208 LightGoldFluorescent 209 LightGlobe 210 PillarTechno 211 PillarAztec 212 PillarAztecDamaged 213 PillarAztecRuined 214 PillarHugeTech 215 PillarAlienPower 216 SStalactiteBig 217 SStalactiteSmall 218 SStalagmiteBig 219 CavePillarTop 220 CavePillarBottom 221 SStalagmiteSmall 222 Candle 223 StrifeCandelabra 224 WaterDropOnFloor 225 WaterfallSplash 226 WaterDrip 227 WaterFountain 228 HeartsInTank 229 TeleportSwirl 230 DeadCrusader 231 DeadStrifePlayer 232 DeadPeasant 233 DeadAcolyte 234 DeadReaver 235 DeadRebel 236 SacrificedGuy 237 PileOfGuts 238 StrifeBurningBarrel 239 BurningBowl 240 BurningBrazier 241 SmallTorchLit 242 SmallTorchUnlit 243 CeilingChain 244 CageLight 245 Statue 246 StatueRuined 247 MediumTorch 248 OutsideLamp 249 PoleLantern 250 SRock1 251 SRock2 252 SRock3 253 SRock4 254 StickInWater 255 Rubble1 256 Rubble2 257 Rubble3 258 Rubble4 259 Rubble5 260 Rubble6 261 Rubble7 262 Rubble8 263 SurgeryCrab 264 LargeTorch 265 HugeTorch 266 PalmTree 267 BigTree2 268 PottedTree 269 TreeStub 270 ShortBush 271 TallBush 272 ChimneyStack 273 BarricadeColumn 274 Pot 275 Pitcher 276 Stool 277 MetalPot 278 Tub 279 Anvil 280 TechLampSilver 281 TechLampBrass 282 Tray 283 AmmoFiller 284 SigilBanner 285 RebelBoots 286 RebelHelmet 287 RebelShirt 288 PowerCoupling 289 BrokenPowerCoupling 290 AlienBubbleColumn 291 AlienFloorBubble 292 AlienCeilingBubble 293 AlienAspClimber 294 AlienSpiderLight 295 Meat 296 Junk 297 FireDroplet 298 AmmoFillup 299 HealthFillup 300 Info 301 RaiseAlarm 302 OpenDoor222 303 CloseDoor222 304 PrisonPass 305 OpenDoor224 306 UpgradeStamina 307 UpgradeAccuracy 308 InterrogatorReport (seems to be unused) 309 HealthTraining 310 GunTraining 311 OraclePass 312 QuestItem1 313 QuestItem2 314 QuestItem3 315 QuestItem4 316 QuestItem5 317 QuestItem6 318 QuestItem7 319 QuestItem8 320 QuestItem9 321 QuestItem10 322 QuestItem11 323 QuestItem12 324 QuestItem13 325 QuestItem14 326 QuestItem15 327 QuestItem16 328 QuestItem17 329 QuestItem18 330 QuestItem19 331 QuestItem20 332 QuestItem21 333 QuestItem22 334 QuestItem23 335 QuestItem24 336 QuestItem25 337 QuestItem26 338 QuestItem27 339 QuestItem28 340 QuestItem29 341 QuestItem30 342 QuestItem31 343 SlideshowStarter */ static FRandom pr_gibtosser ("GibTosser"); void A_TossGib (AActor *); void A_LoopActiveSound (AActor *); void A_FLoopActiveSound (AActor *); void A_Countdown (AActor *); void A_XXScream (AActor *); void A_SentinelRefire (AActor *); // Force Field Guard -------------------------------------------------------- void A_RemoveForceField (AActor *); class AForceFieldGuard : public AActor { DECLARE_CLASS (AForceFieldGuard, AActor) public: int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); }; IMPLEMENT_CLASS (AForceFieldGuard) int AForceFieldGuard::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype) { if (inflictor == NULL || !inflictor->IsKindOf (RUNTIME_CLASS(ADegninOre))) { return -1; } return health; } // Kneeling Guy ------------------------------------------------------------- DEFINE_ACTION_FUNCTION(AActor, A_SetShadow) { self->flags |= MF_STRIFEx8000000|MF_SHADOW; self->RenderStyle = STYLE_Translucent; self->alpha = HR_SHADOW; } DEFINE_ACTION_FUNCTION(AActor, A_ClearShadow) { self->flags &= ~(MF_STRIFEx8000000|MF_SHADOW); self->RenderStyle = STYLE_Normal; self->alpha = OPAQUE; } static FRandom pr_gethurt ("HurtMe!"); DEFINE_ACTION_FUNCTION(AActor, A_GetHurt) { self->flags4 |= MF4_INCOMBAT; if ((pr_gethurt() % 5) == 0) { S_Sound (self, CHAN_VOICE, self->PainSound, 1, ATTN_NORM); self->health--; } if (self->health <= 0) { self->Die (self->target, self->target); } } // Klaxon Warning Light ----------------------------------------------------- DEFINE_ACTION_FUNCTION(AActor, A_TurretLook) { AActor *target; self->threshold = 0; target = self->LastHeard; if (target != NULL && target->health > 0 && target->flags & MF_SHOOTABLE && (self->flags & MF_FRIENDLY) != (target->flags & MF_FRIENDLY)) { self->target = target; if ((self->flags & MF_AMBUSH) && !P_CheckSight (self, target)) { return; } if (self->SeeSound != 0) { S_Sound (self, CHAN_VOICE, self->SeeSound, 1, ATTN_NORM); } self->LastHeard = NULL; self->threshold = 10; self->SetState (self->SeeState); } } DEFINE_ACTION_FUNCTION(AActor, A_KlaxonBlare) { if (--self->reactiontime < 0) { self->target = NULL; self->reactiontime = self->GetDefault()->reactiontime; A_TurretLook (self); if (self->target == NULL) { self->SetIdle(); } else { self->reactiontime = 50; } } if (self->reactiontime == 2) { // [RH] Unalert monsters near the alarm and not just those in the same sector as it. P_NoiseAlert (NULL, self, false); } else if (self->reactiontime > 50) { S_Sound (self, CHAN_VOICE, "misc/alarm", 1, ATTN_NORM); } } // Power Coupling ----------------------------------------------------------- class APowerCoupling : public AActor { DECLARE_CLASS (APowerCoupling, AActor) public: void Die (AActor *source, AActor *inflictor); }; IMPLEMENT_CLASS (APowerCoupling) void APowerCoupling::Die (AActor *source, AActor *inflictor) { Super::Die (source, inflictor); int i; for (i = 0; i < MAXPLAYERS; ++i) if (playeringame[i] && players[i].health > 0) break; if (i == MAXPLAYERS) return; // [RH] In case the player broke it with the dagger, alert the guards now. if (LastHeard != source) { P_NoiseAlert (source, this); } EV_DoDoor (DDoor::doorClose, NULL, players[i].mo, 225, 2*FRACUNIT, 0, 0, 0); EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, 0, 0, false); players[i].mo->GiveInventoryType (QuestItemClasses[5]); S_Sound (CHAN_VOICE, "svox/voc13", 1, ATTN_NORM); players[i].SetLogNumber (13); P_DropItem (this, "BrokenPowerCoupling", -1, 256); Destroy (); } // Gibs for things that bleed ----------------------------------------------- class AMeat : public AActor { DECLARE_CLASS (AMeat, AActor) public: void BeginPlay () { // Strife used mod 19, but there are 20 states. Hmm. SetState (SpawnState + pr_gibtosser() % 20); } }; IMPLEMENT_CLASS (AMeat) //========================================================================== // // A_TossGib // //========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_TossGib) { const char *gibtype = (self->flags & MF_NOBLOOD) ? "Junk" : "Meat"; AActor *gib = Spawn (gibtype, self->x, self->y, self->z + 24*FRACUNIT, ALLOW_REPLACE); angle_t an; int speed; if (gib == NULL) { return; } an = pr_gibtosser() << 24; gib->angle = an; speed = pr_gibtosser() & 15; gib->momx = speed * finecosine[an >> ANGLETOFINESHIFT]; gib->momy = speed * finesine[an >> ANGLETOFINESHIFT]; gib->momz = (pr_gibtosser() & 15) << FRACBITS; } //============================================================================ DEFINE_ACTION_FUNCTION(AActor, A_FLoopActiveSound) { if (self->ActiveSound != 0 && !(level.time & 7)) { S_Sound (self, CHAN_VOICE, self->ActiveSound, 1, ATTN_NORM); } } DEFINE_ACTION_FUNCTION(AActor, A_Countdown) { if (--self->reactiontime <= 0) { P_ExplodeMissile (self, NULL, NULL); self->flags &= ~MF_SKULLFLY; } } DEFINE_ACTION_FUNCTION(AActor, A_LoopActiveSound) { if (self->ActiveSound != 0 && !S_IsActorPlayingSomething (self, CHAN_VOICE, -1)) { S_Sound (self, CHAN_VOICE|CHAN_LOOP, self->ActiveSound, 1, ATTN_NORM); } } DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain) { sector_t *sec = self->Sector; if (self->z == sec->floorplane.ZatPoint (self->x, self->y)) { if ((sec->special & 0xFF) == Damage_InstantDeath) { P_DamageMobj (self, NULL, NULL, 999, NAME_None); } else if ((sec->special & 0xFF) == Scroll_StrifeCurrent) { int anglespeed = sec->tag - 100; fixed_t speed = (anglespeed % 10) << (FRACBITS - 4); angle_t finean = (anglespeed / 10) << (32-3); finean >>= ANGLETOFINESHIFT; self->momx += FixedMul (speed, finecosine[finean]); self->momy += FixedMul (speed, finesine[finean]); } } } //============================================================================ // // A_ClearSoundTarget // //============================================================================ DEFINE_ACTION_FUNCTION(AActor, A_ClearSoundTarget) { AActor *actor; self->Sector->SoundTarget = NULL; for (actor = self->Sector->thinglist; actor != NULL; actor = actor->snext) { actor->LastHeard = NULL; } } DEFINE_ACTION_FUNCTION(AActor, A_ItBurnsItBurns) { S_Sound (self, CHAN_VOICE, "human/imonfire", 1, ATTN_NORM); if (self->player != NULL && self->player->mo == self) { P_SetPsprite (self->player, ps_weapon, self->FindState("FireHands")); P_SetPsprite (self->player, ps_flash, NULL); self->player->ReadyWeapon = NULL; self->player->PendingWeapon = WP_NOCHANGE; self->player->playerstate = PST_LIVE; } } DEFINE_ACTION_FUNCTION(AActor, A_DropFire) { AActor *drop = Spawn("FireDroplet", self->x, self->y, self->z + 24*FRACUNIT, ALLOW_REPLACE); drop->momz = -FRACUNIT; P_RadiusAttack (self, self, 64, 64, NAME_Fire, false); } DEFINE_ACTION_FUNCTION(AActor, A_CrispyPlayer) { if (self->player != NULL && self->player->mo == self) { self->player->playerstate = PST_DEAD; P_SetPsprite (self->player, ps_weapon, self->player->psprites[ps_weapon].state + (self->FindState("FireHandsLower") - self->FindState("FireHands"))); } } DEFINE_ACTION_FUNCTION(AActor, A_HandLower) { if (self->player != NULL) { pspdef_t *psp = &self->player->psprites[ps_weapon]; psp->sy += FRACUNIT*9; if (psp->sy > WEAPONBOTTOM*2) { P_SetPsprite (self->player, ps_weapon, NULL); } } }