extend class PlayerPawn { //=========================================================================== // // EndAllPowerupEffects // // Calls EndEffect() on every Powerup in the inventory list. // //=========================================================================== void InitAllPowerupEffects() { let item = Inv; while (item != null) { let power = Powerup(item); if (power != null) { power.InitEffect(); } item = item.Inv; } } //=========================================================================== // // EndAllPowerupEffects // // Calls EndEffect() on every Powerup in the inventory list. // //=========================================================================== void EndAllPowerupEffects() { let item = Inv; while (item != null) { let power = Powerup(item); if (power != null) { power.EndEffect(); } item = item.Inv; } } //=========================================================================== // // // //=========================================================================== virtual void ActivateMorphWeapon () { class morphweaponcls = MorphWeapon; player.PendingWeapon = WP_NOCHANGE; if (player.ReadyWeapon != null) { player.GetPSprite(PSP_WEAPON).y = WEAPONTOP; } if (morphweaponcls == null || !(morphweaponcls is 'Weapon')) { // No weapon at all while morphed! player.ReadyWeapon = null; } else { player.ReadyWeapon = Weapon(FindInventory (morphweaponcls)); if (player.ReadyWeapon == null) { player.ReadyWeapon = Weapon(GiveInventoryType (morphweaponcls)); if (player.ReadyWeapon != null) { player.ReadyWeapon.GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph } } if (player.ReadyWeapon != null) { player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState()); } } if (player.ReadyWeapon != null) { player.SetPsprite(PSP_FLASH, null); } player.PendingWeapon = WP_NOCHANGE; } //--------------------------------------------------------------------------- // // FUNC P_MorphPlayer // // Returns true if the player gets turned into a chicken/pig. // // TODO: Allow morphed players to receive weapon sets (not just one weapon), // since they have their own weapon slots now. // //--------------------------------------------------------------------------- virtual bool MorphPlayer(playerinfo activator, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) { if (bDontMorph) { return false; } if (bInvulnerable && (player != activator || !(style & MRF_WHENINVULNERABLE))) { // Immune when invulnerable unless this is a power we activated return false; } if (player.morphTics) { // Player is already a beast if ((GetClass() == spawntype) && bCanSuperMorph && (player.morphTics < (((duration) ? duration : DEFMORPHTICS) - TICRATE)) && FindInventory('PowerWeaponLevel2', true) == null) { // Make a super chicken GiveInventoryType ('PowerWeaponLevel2'); } return false; } if (health <= 0) { // Dead players cannot morph return false; } if (spawntype == null) { return false; } if (!(spawntype is 'PlayerPawn')) { return false; } if (spawntype == GetClass()) { return false; } let morphed = PlayerPawn(Spawn (spawntype, Pos, NO_REPLACE)); EndAllPowerupEffects(); Substitute(morphed); if ((style & MRF_TRANSFERTRANSLATION) && !morphed.bDontTranslate) { morphed.Translation = Translation; } if (tid != 0 && (style & MRF_NEWTIDBEHAVIOUR)) { morphed.ChangeTid(tid); ChangeTid(0); } morphed.Angle = Angle; morphed.target = target; morphed.tracer = tracer; morphed.alternative = self; morphed.FriendPlayer = FriendPlayer; morphed.DesignatedTeam = DesignatedTeam; morphed.Score = Score; player.PremorphWeapon = player.ReadyWeapon; morphed.special2 = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility morphed.player = player; if (morphed.ViewHeight > player.viewheight && player.deltaviewheight == 0) { // If the new view height is higher than the old one, start moving toward it. player.deltaviewheight = player.GetDeltaViewHeight(); } morphed.bShadow |= bShadow; morphed.bNoGravity |= bNoGravity; morphed.bFly |= bFly; morphed.bGhost |= bGhost; if (enter_flash == null) enter_flash = 'TeleportFog'; let eflash = Spawn(enter_flash, Pos + (0, 0, gameinfo.telefogheight), ALLOW_REPLACE); let p = player; player = null; alternative = morphed; bSolid = false; bShootable = false; bUnmorphed = true; bInvisible = true; p.morphTics = (duration) ? duration : DEFMORPHTICS; // [MH] Used by SBARINFO to speed up face drawing p.MorphedPlayerClass = spawntype; p.MorphStyle = style; if (exit_flash == null) exit_flash = 'TeleportFog'; p.MorphExitFlash = exit_flash; p.health = morphed.health; p.mo = morphed; p.vel = (0, 0); morphed.ObtainInventory (self); // Remove all armor for (Inventory item = morphed.Inv; item != null; ) { let next = item.Inv; if (item is 'Armor') { item.DepleteOrDestroy(); } item = next; } morphed.InitAllPowerupEffects(); morphed.ActivateMorphWeapon (); if (p.camera == self) // can this happen? { p.camera = morphed; } morphed.ScoreIcon = ScoreIcon; // [GRB] if (eflash) eflash.target = morphed; return true; } //---------------------------------------------------------------------------- // // FUNC P_UndoPlayerMorph // //---------------------------------------------------------------------------- virtual bool UndoPlayerMorph(playerinfo activator, int unmorphflag = 0, bool force = false) { if (alternative == null) { return false; } let player = self.player; bool DeliberateUnmorphIsOkay = !!(MRF_STANDARDUNDOING & unmorphflag); if ((bInvulnerable) // If the player is invulnerable && ((player != activator) // and either did not decide to unmorph, || (!((player.MorphStyle & MRF_WHENINVULNERABLE) // or the morph style does not allow it || (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it), { // Then the player is immune to the unmorph. return false; } let altmo = PlayerPawn(alternative); altmo.SetOrigin (Pos, false); altmo.bSolid = true; bSolid = false; if (!force && !altmo.TestMobjLocation()) { // Didn't fit altmo.bSolid = false; bSolid = true; player.morphTics = 2*TICRATE; return false; } // No longer using tracer as morph storage. That is what 'alternative' is for. // If the tracer has changed on the morph, change the original too. altmo.target = target; altmo.tracer = tracer; self.player = null; altmo.alternative = alternative = null; // Remove the morph power if the morph is being undone prematurely. for (Inventory item = Inv; item != null;) { let next = item.Inv; if (item is "PowerMorph") { item.Destroy(); } item = next; } EndAllPowerupEffects(); altmo.ObtainInventory (self); Substitute(altmo); if ((tid != 0) && (player.MorphStyle & MRF_NEWTIDBEHAVIOUR)) { altmo.ChangeTid(tid); } altmo.Angle = Angle; altmo.player = player; altmo.reactiontime = 18; altmo.bSolid = !!(special2 & 2); altmo.bShootable = !!(special2 & 4); altmo.bInvisible = !!(special2 & 0x40); altmo.Vel.XY = (0, 0); player.Vel = (0, 0); altmo.Vel.Z = Vel.Z; altmo.floorz = floorz; altmo.bShadow = bShadow; altmo.bNoGravity = bNoGravity; altmo.bGhost = bGhost; altmo.Score = Score; altmo.InitAllPowerupEffects(); let exit_flash = player.MorphExitFlash; bool correctweapon = !!(player.MorphStyle & MRF_LOSEACTUALWEAPON); bool undobydeathsaves = !!(player.MorphStyle & MRF_UNDOBYDEATHSAVES); player.morphTics = 0; player.MorphedPlayerClass = null; player.MorphStyle = 0; player.MorphExitFlash = null; player.viewheight = altmo.ViewHeight; Inventory level2 = altmo.FindInventory("PowerWeaponLevel2", true); if (level2 != null) { level2.Destroy (); } if ((player.health > 0) || undobydeathsaves) { player.health = altmo.health = altmo.SpawnHealth(); } else // killed when morphed so stay dead { altmo.health = player.health; } player.mo = altmo; if (player.camera == self) { player.camera = altmo; } // [MH] // If the player that was morphed is the one // taking events, reset up the face, if any; // this is only needed for old-skool skins // and for the original DOOM status bar. if (player == players[consoleplayer]) { if (face != 'None') { // Assume root-level base skin to begin with let skinindex = 0; let skin = player.GetSkin(); // If a custom skin was in use, then reload it // or else the base skin for the player class. if (skin >= PlayerClasses.Size () && skin < PlayerSkins.Size()) { skinindex = skin; } else if (PlayerClasses.Size () > 1) { let whatami = altmo.GetClass(); for (int i = 0; i < PlayerClasses.Size (); ++i) { if (PlayerClasses[i].Type == whatami) { skinindex = i; break; } } } } } Actor eflash = null; if (exit_flash != null) { eflash = Spawn(exit_flash, Vec3Angle(20., altmo.Angle, gameinfo.telefogheight), ALLOW_REPLACE); if (eflash) eflash.target = altmo; } WeaponSlots.SetupWeaponSlots(altmo); // Use original class's weapon slots. let beastweap = player.ReadyWeapon; if (player.PremorphWeapon != null) { player.PremorphWeapon.PostMorphWeapon (); } else { player.ReadyWeapon = player.PendingWeapon = null; } if (correctweapon) { // Better "lose morphed weapon" semantics class morphweaponcls = MorphWeapon; if (morphweaponcls != null && morphweaponcls is 'Weapon') { let OriginalMorphWeapon = Weapon(altmo.FindInventory (morphweapon)); if ((OriginalMorphWeapon != null) && (OriginalMorphWeapon.GivenAsMorphWeapon)) { // You don't get to keep your morphed weapon. if (OriginalMorphWeapon.SisterWeapon != null) { OriginalMorphWeapon.SisterWeapon.Destroy (); } OriginalMorphWeapon.Destroy (); } } } else // old behaviour (not really useful now) { // Assumptions made here are no longer valid if (beastweap != null) { // You don't get to keep your morphed weapon. if (beastweap.SisterWeapon != null) { beastweap.SisterWeapon.Destroy (); } beastweap.Destroy (); } } Destroy (); // Restore playerclass armor to its normal amount. let hxarmor = HexenArmor(altmo.FindInventory('HexenArmor')); if (hxarmor != null) { hxarmor.Slots[4] = altmo.HexenArmor[0]; } return true; } } class MorphProjectile : Actor { Class PlayerClass; Class MonsterClass, MorphFlash, UnMorphFlash; int Duration, MorphStyle; Default { Damage 1; Projectile; -ACTIVATEIMPACT -ACTIVATEPCROSS } override int DoSpecialDamage (Actor target, int damage, Name damagetype) { if (target.player) { target.player.MorphPlayer (NULL, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); } else { target.MorphMonster (MonsterClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); } return -1; } } class MorphedMonster : Actor native { native Actor UnmorphedMe; native int UnmorphTime, MorphStyle; native Class MorphExitFlash; Default { Monster; -COUNTKILL +FLOORCLIP } }