ref: 4802041e17330e1f6952b2ee6e11087e8171bbcb
dir: /p_pspr.c/
//************************************************************************** //** //** p_pspr.c : Heretic 2 : Raven Software, Corp. //** //** $Revision: 575 $ //** $Date: 2011-04-13 22:06:28 +0300 (Wed, 13 Apr 2011) $ //** //************************************************************************** // HEADER FILES ------------------------------------------------------------ #include "h2stdinc.h" #include "h2def.h" #include "p_local.h" #include "soundst.h" #include "v_compat.h" /* V_SetPaletteXXX() macros */ // MACROS ------------------------------------------------------------------ #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- extern void P_ExplodeMissile(mobj_t *mo); extern void A_UnHideThing(mobj_t *actor); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern fixed_t FloatBobOffsets[64]; // PUBLIC DATA DEFINITIONS ------------------------------------------------- weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES] = { { // First Weapons { // Fighter First Weapon - Punch MANA_NONE, // mana S_PUNCHUP, // upstate S_PUNCHDOWN, // downstate S_PUNCHREADY, // readystate S_PUNCHATK1_1, // atkstate S_PUNCHATK1_1, // holdatkstate S_NULL // flashstate }, { // Cleric First Weapon - Mace MANA_NONE, // mana S_CMACEUP, // upstate S_CMACEDOWN, // downstate S_CMACEREADY, // readystate S_CMACEATK_1, // atkstate S_CMACEATK_1, // holdatkstate S_NULL // flashstate }, { // Mage First Weapon - Wand MANA_NONE, S_MWANDUP, S_MWANDDOWN, S_MWANDREADY, S_MWANDATK_1, S_MWANDATK_1, S_NULL }, #ifdef ASSASSIN { // Assassin - Katar MANA_NONE, S_KATARUP, S_KATARDOWN, S_KATARREADY, S_KATARATK1_1, S_KATARATK1_1, S_NULL }, #endif { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } }, { // Second Weapons { // Fighter - Axe MANA_NONE, // mana S_FAXEUP, // upstate S_FAXEDOWN, // downstate S_FAXEREADY, // readystate S_FAXEATK_1, // atkstate S_FAXEATK_1, // holdatkstate S_NULL // flashstate }, { // Cleric - Serpent Staff MANA_1, // mana S_CSTAFFUP, // upstate S_CSTAFFDOWN, // downstate S_CSTAFFREADY, // readystate S_CSTAFFATK_1, // atkstate S_CSTAFFATK_1, // holdatkstate S_NULL // flashstate }, { // Mage - Cone of shards MANA_1, // mana S_CONEUP, // upstate S_CONEDOWN, // downstate S_CONEREADY, // readystate S_CONEATK1_1, // atkstate S_CONEATK1_3, // holdatkstate S_NULL // flashstate }, #ifdef ASSASSIN { // Assassin - Hand Crossbow MANA_1, S_ACROSSUP, S_ACROSSDOWN, S_ACROSSREADY, S_ACROSSATK_1, S_ACROSSATK_3, S_NULL }, #endif { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } }, { // Third Weapons { // Fighter - Hammer MANA_NONE, // mana S_FHAMMERUP, // upstate S_FHAMMERDOWN, // downstate S_FHAMMERREADY, // readystate S_FHAMMERATK_1, // atkstate S_FHAMMERATK_1, // holdatkstate S_NULL // flashstate }, { // Cleric - Flame Strike MANA_2, // mana S_CFLAMEUP, // upstate S_CFLAMEDOWN, // downstate S_CFLAMEREADY1, // readystate S_CFLAMEATK_1, // atkstate S_CFLAMEATK_1, // holdatkstate S_NULL // flashstate }, { // Mage - Lightning MANA_2, // mana S_MLIGHTNINGUP, // upstate S_MLIGHTNINGDOWN, // downstate S_MLIGHTNINGREADY, // readystate S_MLIGHTNINGATK_1, // atkstate S_MLIGHTNINGATK_1, // holdatkstate S_NULL // flashstate }, #ifdef ASSASSIN { // Assassin - Grenades MANA_2, S_AGRENUP, S_AGRENDOWN, S_AGRENREADY, S_AGRENATK_1, S_AGRENATK_1, S_NULL }, #endif { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } }, { // Fourth Weapons { // Fighter - Rune Sword MANA_BOTH, // mana S_FSWORDUP, // upstate S_FSWORDDOWN, // downstate S_FSWORDREADY, // readystate S_FSWORDATK_1, // atkstate S_FSWORDATK_1, // holdatkstate S_NULL // flashstate }, { // Cleric - Holy Symbol MANA_BOTH, // mana S_CHOLYUP, // upstate S_CHOLYDOWN, // downstate S_CHOLYREADY, // readystate S_CHOLYATK_1, // atkstate S_CHOLYATK_1, // holdatkstate S_NULL // flashstate }, { // Mage - Staff MANA_BOTH, // mana S_MSTAFFUP, // upstate S_MSTAFFDOWN, // downstate S_MSTAFFREADY, // readystate S_MSTAFFATK_1, // atkstate S_MSTAFFATK_1, // holdatkstate S_NULL // flashstate }, #ifdef ASSASSIN { // Assassin - Staff of Set MANA_BOTH, S_ASTAFFUP, S_ASTAFFDOWN, S_ASTAFFREADY, S_ASTAFFATK_1, S_ASTAFFATK_1, S_NULL }, #endif { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } } }; // PRIVATE DATA DEFINITIONS ------------------------------------------------ //fixed_t bulletslope; // for P_BulletSlope() static int WeaponManaUse[NUMCLASSES][NUMWEAPONS] = { { 0, 2, 3, 14 }, { 0, 1, 4, 18 }, { 0, 3, 5, 15 }, #ifdef ASSASSIN { 0, 3, 3, 1 }, // True to Hexen II #endif { 0, 0, 0, 0 } }; // CODE -------------------------------------------------------------------- //--------------------------------------------------------------------------- // // PROC P_SetPsprite // //--------------------------------------------------------------------------- void P_SetPsprite(player_t *player, int position, statenum_t stnum) { pspdef_t *psp; state_t *state; psp = &player->psprites[position]; do { if (!stnum) { // Object removed itself. psp->state = NULL; break; } state = &states[stnum]; psp->state = state; psp->tics = state->tics; // could be 0 if (state->misc1) { // Set coordinates. psp->sx = state->misc1<<FRACBITS; } if (state->misc2) { psp->sy = state->misc2<<FRACBITS; } if (state->action) { // Call action routine. state->action(player, psp); if (!psp->state) { break; } } stnum = psp->state->nextstate; } while (!psp->tics); // An initial state of 0 could cycle through. } //--------------------------------------------------------------------------- // // PROC P_SetPspriteNF // // Identical to P_SetPsprite, without calling the action function //--------------------------------------------------------------------------- void P_SetPspriteNF(player_t *player, int position, statenum_t stnum) { pspdef_t *psp; state_t *state; psp = &player->psprites[position]; do { if (!stnum) { // Object removed itself. psp->state = NULL; break; } state = &states[stnum]; psp->state = state; psp->tics = state->tics; // could be 0 if (state->misc1) { // Set coordinates. psp->sx = state->misc1<<FRACBITS; } if (state->misc2) { psp->sy = state->misc2<<FRACBITS; } stnum = psp->state->nextstate; } while (!psp->tics); // An initial state of 0 could cycle through. } /* ================= = = P_CalcSwing = ================= */ /* fixed_t swingx, swingy; void P_CalcSwing (player_t *player) { fixed_t swing; int angle; // OPTIMIZE: tablify this swing = player->bob; angle = (FINEANGLES/70*leveltime) & FINEMASK; swingx = FixedMul (swing, finesine[angle]); angle = (FINEANGLES/70*leveltime + FINEANGLES/2) & FINEMASK; swingy = -FixedMul (swingx, finesine[angle]); } */ //--------------------------------------------------------------------------- // // PROC P_ActivateMorphWeapon // //--------------------------------------------------------------------------- void P_ActivateMorphWeapon(player_t *player) { player->pendingweapon = WP_NOCHANGE; player->psprites[ps_weapon].sy = WEAPONTOP; player->readyweapon = WP_FIRST; // Snout is the first weapon P_SetPsprite(player, ps_weapon, S_SNOUTREADY); } //--------------------------------------------------------------------------- // // PROC P_PostMorphWeapon // //--------------------------------------------------------------------------- void P_PostMorphWeapon(player_t *player, weapontype_t weapon) { player->pendingweapon = WP_NOCHANGE; player->readyweapon = weapon; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite(player, ps_weapon, WeaponInfo[weapon][player->playerclass].upstate); } //--------------------------------------------------------------------------- // // PROC P_BringUpWeapon // // Starts bringing the pending weapon up from the bottom of the screen. // //--------------------------------------------------------------------------- void P_BringUpWeapon(player_t *player) { statenum_t newstate; if (player->pendingweapon == WP_NOCHANGE) { player->pendingweapon = player->readyweapon; } if (player->playerclass == PCLASS_FIGHTER && player->pendingweapon == WP_SECOND && player->mana[MANA_1]) { newstate = S_FAXEUP_G; } else { newstate = WeaponInfo[player->pendingweapon][player->playerclass].upstate; } player->pendingweapon = WP_NOCHANGE; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite(player, ps_weapon, newstate); } //--------------------------------------------------------------------------- // // FUNC P_CheckMana // // Returns true if there is enough mana to shoot. If not, selects the // next weapon to use. // //--------------------------------------------------------------------------- static boolean P_CheckMana(player_t *player) { manatype_t mana; int count; mana = WeaponInfo[player->readyweapon][player->playerclass].mana; count = WeaponManaUse[player->playerclass][player->readyweapon]; if (mana == MANA_BOTH) { if (player->mana[MANA_1] >= count && player->mana[MANA_2] >= count) { return true; } } else if (mana == MANA_NONE || player->mana[mana] >= count) { return true; } // out of mana, pick a weapon to change to do { if (player->weaponowned[WP_THIRD] && player->mana[MANA_2] >= WeaponManaUse[player->playerclass][WP_THIRD]) { player->pendingweapon = WP_THIRD; } else if (player->weaponowned[WP_SECOND] && player->mana[MANA_1] >= WeaponManaUse[player->playerclass][WP_SECOND]) { player->pendingweapon = WP_SECOND; } else if (player->weaponowned[WP_FOURTH] && player->mana[MANA_1] >= WeaponManaUse[player->playerclass][WP_FOURTH] && player->mana[MANA_2] >= WeaponManaUse[player->playerclass][WP_FOURTH]) { player->pendingweapon = WP_FOURTH; } else { player->pendingweapon = WP_FIRST; } } while (player->pendingweapon == WP_NOCHANGE); P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->playerclass].downstate); return false; } //--------------------------------------------------------------------------- // // PROC P_FireWeapon // //--------------------------------------------------------------------------- static void P_FireWeapon(player_t *player) { statenum_t attackState; if (!P_CheckMana(player)) { return; } P_SetMobjState(player->mo, PStateAttack[player->playerclass]); // S_PLAY_ATK1); if (player->playerclass == PCLASS_FIGHTER && player->readyweapon == WP_SECOND && player->mana[MANA_1] > 0) { // Glowing axe attackState = S_FAXEATK_G1; } else { attackState = player->refire ? WeaponInfo[player->readyweapon][player->playerclass].holdatkstate : WeaponInfo[player->readyweapon][player->playerclass].atkstate; } P_SetPsprite(player, ps_weapon, attackState); P_NoiseAlert(player->mo, player->mo); } //--------------------------------------------------------------------------- // // PROC P_DropWeapon // // The player died, so put the weapon away. // //--------------------------------------------------------------------------- void P_DropWeapon(player_t *player) { P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->playerclass].downstate); } //--------------------------------------------------------------------------- // // PROC A_WeaponReady // // The player can fire the weapon or change to another weapon at this time. // //--------------------------------------------------------------------------- void A_WeaponReady(player_t *player, pspdef_t *psp) { int angle; // Change player from attack state if (player->mo->state >= &states[PStateAttack[player->playerclass]] && player->mo->state <= &states[PStateAttackEnd[player->playerclass]]) { P_SetMobjState(player->mo, PStateNormal[player->playerclass]); } // Put the weapon away if the player has a pending weapon or has // died. if (player->pendingweapon != WP_NOCHANGE || !player->health) { P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->playerclass].downstate); return; } // Check for fire. if (player->cmd.buttons & BT_ATTACK) { player->attackdown = true; P_FireWeapon(player); return; } else { player->attackdown = false; } if (!player->morphTics) { // Bob the weapon based on movement speed. angle = (128*leveltime)&FINEMASK; psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); angle &= FINEANGLES/2 - 1; psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); } } //--------------------------------------------------------------------------- // // PROC A_ReFire // // The player can re fire the weapon without lowering it entirely. // //--------------------------------------------------------------------------- void A_ReFire(player_t *player, pspdef_t *psp) { if ((player->cmd.buttons&BT_ATTACK) && player->pendingweapon == WP_NOCHANGE && player->health) { player->refire++; P_FireWeapon(player); } else { player->refire = 0; P_CheckMana(player); } } //--------------------------------------------------------------------------- // // PROC A_Lower // //--------------------------------------------------------------------------- void A_Lower(player_t *player, pspdef_t *psp) { if (player->morphTics) { psp->sy = WEAPONBOTTOM; } else { psp->sy += LOWERSPEED; } if (psp->sy < WEAPONBOTTOM) { // Not lowered all the way yet return; } if (player->playerstate == PST_DEAD) { // Player is dead, so don't bring up a pending weapon psp->sy = WEAPONBOTTOM; return; } if (!player->health) { // Player is dead, so keep the weapon off screen P_SetPsprite(player, ps_weapon, S_NULL); return; } player->readyweapon = player->pendingweapon; P_BringUpWeapon(player); } //--------------------------------------------------------------------------- // // PROC A_Raise // //--------------------------------------------------------------------------- void A_Raise(player_t *player, pspdef_t *psp) { psp->sy -= RAISESPEED; if (psp->sy > WEAPONTOP) { // Not raised all the way yet return; } psp->sy = WEAPONTOP; if (player->playerclass == PCLASS_FIGHTER && player->readyweapon == WP_SECOND && player->mana[MANA_1]) { P_SetPsprite(player, ps_weapon, S_FAXEREADY_G); } else { P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->playerclass].readystate); } } /* =============== = = P_BulletSlope = = Sets a slope so a near miss is at aproximately the height of the = intended target = =============== */ /* static void P_BulletSlope (mobj_t *mo) { angle_t an; // // see which target is to be aimed at // an = mo->angle; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); } if (!linetarget) { an += 1<<26; bulletslope = (mo->player->lookdir<<FRACBITS)/173; } } } */ //**************************************************************************** // // WEAPON ATTACKS // //**************************************************************************** //============================================================================ // // AdjustPlayerAngle // //============================================================================ #define MAX_ANGLE_ADJUST (5*ANGLE_1) static void AdjustPlayerAngle(mobj_t *pmo) { angle_t angle; int difference; angle = R_PointToAngle2(pmo->x, pmo->y, linetarget->x, linetarget->y); difference = (int)angle - (int)pmo->angle; if (abs(difference) > MAX_ANGLE_ADJUST) { pmo->angle += difference > 0 ? MAX_ANGLE_ADJUST : -MAX_ANGLE_ADJUST; } else { pmo->angle = angle; } } //============================================================================ // // A_SnoutAttack // //============================================================================ void A_SnoutAttack(player_t *player, pspdef_t *psp) { angle_t angle; int damage; int slope; damage = 3 + (P_Random() & 3); angle = player->mo->angle; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); PuffType = MT_SNOUTPUFF; PuffSpawned = NULL; P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); S_StartSound(player->mo, SFX_PIG_ACTIVE1 + (P_Random() & 1)); if (linetarget) { AdjustPlayerAngle(player->mo); // player->mo->angle = R_PointToAngle2(player->mo->x, // player->mo->y, linetarget->x, linetarget->y); if (PuffSpawned) { // Bit something S_StartSound(player->mo, SFX_PIG_ATTACK); } } } //============================================================================ // // A_FHammerAttack // //============================================================================ #define HAMMER_RANGE (MELEERANGE+MELEERANGE/2) void A_FHammerAttack(player_t *player, pspdef_t *psp) { angle_t angle; mobj_t *pmo = player->mo; int damage; fixed_t power; int slope; int i; damage = 60 + (P_Random() & 63); power = 10*FRACUNIT; PuffType = MT_HAMMERPUFF; for (i = 0; i < 16; i++) { angle = pmo->angle + i*(ANG45/32); slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); if (linetarget) { P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); AdjustPlayerAngle(pmo); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } pmo->special1 = false; // Don't throw a hammer goto hammerdone; } angle = pmo->angle - i*(ANG45/32); slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); if (linetarget) { P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); AdjustPlayerAngle(pmo); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } pmo->special1 = false; // Don't throw a hammer goto hammerdone; } } // didn't find any targets in meleerange, so set to throw out a hammer PuffSpawned = NULL; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); if (PuffSpawned) { pmo->special1 = false; } else { pmo->special1 = true; } hammerdone: if (player->mana[MANA_2] < WeaponManaUse[player->playerclass][player->readyweapon]) { // Don't spawn a hammer if the player doesn't have enough mana pmo->special1 = false; } return; } //============================================================================ // // A_FHammerThrow // //============================================================================ void A_FHammerThrow(player_t *player, pspdef_t *psp) { mobj_t *mo; if (!player->mo->special1) { return; } player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; mo = P_SpawnPlayerMissile(player->mo, MT_HAMMER_MISSILE); if (mo) { mo->special1 = 0; } } //============================================================================ // // A_FSwordAttack // //============================================================================ void A_FSwordAttack(player_t *player, pspdef_t *psp) { mobj_t *pmo; player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; pmo = player->mo; P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z - 10*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle + ANG45/4); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z - 5*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle + ANG45/8); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z, MT_FSWORD_MISSILE, pmo->angle); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z + 5*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle - ANG45/8); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z + 10*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle - ANG45/4); S_StartSound(pmo, SFX_FIGHTER_SWORD_FIRE); } //============================================================================ // // A_FSwordAttack2 // //============================================================================ void A_FSwordAttack2(mobj_t *actor) { angle_t angle = actor->angle; P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle + ANG45/4, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle + ANG45/8, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle - ANG45/8, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle - ANG45/4, 0); S_StartSound(actor, SFX_FIGHTER_SWORD_FIRE); } //============================================================================ // // A_FSwordFlames // //============================================================================ void A_FSwordFlames(mobj_t *actor) { int i; for (i = 1 + (P_Random() & 3); i; i--) { P_SpawnMobj(actor->x + ((P_Random() - 128) << 12), actor->y + ((P_Random() - 128) << 12), actor->z + ((P_Random() - 128) << 11), MT_FSWORD_FLAME); } } //============================================================================ // // A_MWandAttack // //============================================================================ void A_MWandAttack(player_t *player, pspdef_t *psp) { mobj_t *mo; mo = P_SpawnPlayerMissile(player->mo, MT_MWAND_MISSILE); if (mo) { mo->thinker.function = P_BlasterMobjThinker; } S_StartSound(player->mo, SFX_MAGE_WAND_FIRE); } // ===== Mage Lightning Weapon ===== //============================================================================ // // A_LightningReady // //============================================================================ void A_LightningReady(player_t *player, pspdef_t *psp) { A_WeaponReady(player, psp); if (P_Random() < 160) { S_StartSound(player->mo, SFX_MAGE_LIGHTNING_READY); } } //============================================================================ // // A_LightningClip // //============================================================================ #define ZAGSPEED FRACUNIT void A_LightningClip(mobj_t *actor) { mobj_t *cMo; mobj_t *target = NULL; /* jim added initialiser */ int zigZag; if (actor->type == MT_LIGHTNING_FLOOR) { actor->z = actor->floorz; target = (mobj_t *)((mobj_t *)actor->special2)->special1; } else if (actor->type == MT_LIGHTNING_CEILING) { actor->z = actor->ceilingz - actor->height; target = (mobj_t *)actor->special1; } if (actor->type == MT_LIGHTNING_FLOOR) { // floor lightning zig-zags, and forces the ceiling lightning to mimic cMo = (mobj_t *)actor->special2; zigZag = P_Random(); if ((zigZag > 128 && actor->special1 < 2) || actor->special1 < -2) { P_ThrustMobj(actor, actor->angle + ANG90, ZAGSPEED); if (cMo) { P_ThrustMobj(cMo, actor->angle + ANG90, ZAGSPEED); } actor->special1++; } else { P_ThrustMobj(actor, actor->angle - ANG90, ZAGSPEED); if (cMo) { P_ThrustMobj(cMo, cMo->angle - ANG90, ZAGSPEED); } actor->special1--; } } if (target) { if (target->health <= 0) { P_ExplodeMissile(actor); } else { actor->angle = R_PointToAngle2(actor->x, actor->y, target->x, target->y); actor->momx = 0; actor->momy = 0; P_ThrustMobj(actor, actor->angle, actor->info->speed>>1); } } } //============================================================================ // // A_LightningZap // //============================================================================ void A_LightningZap(mobj_t *actor) { mobj_t *mo; fixed_t deltaZ; A_LightningClip(actor); actor->health -= 8; if (actor->health <= 0) { P_SetMobjState(actor, actor->info->deathstate); return; } if (actor->type == MT_LIGHTNING_FLOOR) { deltaZ = 10*FRACUNIT; } else { deltaZ = -10*FRACUNIT; } mo = P_SpawnMobj(actor->x + ((P_Random() - 128) * actor->radius/256), actor->y + ((P_Random() - 128) * actor->radius/256), actor->z + deltaZ, MT_LIGHTNING_ZAP); if (mo) { mo->special2 = (intptr_t)actor; mo->momx = actor->momx; mo->momy = actor->momy; mo->target = actor->target; if (actor->type == MT_LIGHTNING_FLOOR) { mo->momz = 20*FRACUNIT; } else { mo->momz = -20*FRACUNIT; } } /* mo = P_SpawnMobj(actor->x + ((P_Random() - 128) * actor->radius/256), actor->y + ((P_Random() - 128) * actor->radius/256), actor->z+deltaZ, MT_LIGHTNING_ZAP); if (mo) { mo->special2 = (intptr_t)actor; mo->momx = actor->momx; mo->momy = actor->momy; mo->target = actor->target; if (actor->type == MT_LIGHTNING_FLOOR) { mo->momz = 16*FRACUNIT; } else { mo->momz = -16*FRACUNIT; } } */ if (actor->type == MT_LIGHTNING_FLOOR && P_Random() < 160) { S_StartSound(actor, SFX_MAGE_LIGHTNING_CONTINUOUS); } } //============================================================================ // // A_MLightningAttack2 // //============================================================================ void A_MLightningAttack2(mobj_t *actor) { mobj_t *fmo, *cmo; fmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_FLOOR); cmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_CEILING); if (fmo) { fmo->special1 = 0; fmo->special2 = (intptr_t)cmo; A_LightningZap(fmo); } if (cmo) { cmo->special1 = 0; // mobj that it will track cmo->special2 = (intptr_t)fmo; A_LightningZap(cmo); } S_StartSound(actor, SFX_MAGE_LIGHTNING_FIRE); } //============================================================================ // // A_MLightningAttack // //============================================================================ void A_MLightningAttack(player_t *player, pspdef_t *psp) { A_MLightningAttack2(player->mo); player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; } //============================================================================ // // A_ZapMimic // //============================================================================ void A_ZapMimic(mobj_t *actor) { mobj_t *mo; mo = (mobj_t *)actor->special2; if (mo) { if (mo->state >= &states[mo->info->deathstate] || mo->state == &states[S_FREETARGMOBJ]) { P_ExplodeMissile(actor); } else { actor->momx = mo->momx; actor->momy = mo->momy; } } } //============================================================================ // // A_LastZap // //============================================================================ void A_LastZap(mobj_t *actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_LIGHTNING_ZAP); if (mo) { P_SetMobjState(mo, S_LIGHTNING_ZAP_X1); mo->momz = 40*FRACUNIT; } } //============================================================================ // // A_LightningRemove // //============================================================================ void A_LightningRemove(mobj_t *actor) { mobj_t *mo; mo = (mobj_t *)actor->special2; if (mo) { mo->special2 = 0; P_ExplodeMissile(mo); } } //============================================================================ // // MStaffSpawn // //============================================================================ static void MStaffSpawn(mobj_t *pmo, angle_t angle) { mobj_t *mo; mo = P_SPMAngle(pmo, MT_MSTAFF_FX2, angle); if (mo) { mo->target = pmo; mo->special1 = (intptr_t)P_RoughMonsterSearch(mo, 10); } } //============================================================================ // // A_MStaffAttack // //============================================================================ void A_MStaffAttack(player_t *player, pspdef_t *psp) { angle_t angle; mobj_t *pmo; player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; pmo = player->mo; angle = pmo->angle; MStaffSpawn(pmo, angle); MStaffSpawn(pmo, angle-ANGLE_1*5); MStaffSpawn(pmo, angle+ANGLE_1*5); S_StartSound(player->mo, SFX_MAGE_STAFF_FIRE); if (player == &players[consoleplayer]) { player->damagecount = 0; player->bonuscount = 0; V_SetPaletteShift(STARTSCOURGEPAL); } } //============================================================================ // // A_MStaffPalette // //============================================================================ void A_MStaffPalette(player_t *player, pspdef_t *psp) { int pal; if (player == &players[consoleplayer]) { pal = STARTSCOURGEPAL + psp->state - (&states[S_MSTAFFATK_2]); if (pal == STARTSCOURGEPAL + 3) { // reset back to original playpal pal = 0; } V_SetPaletteShift(pal); } } //============================================================================ // // A_MStaffWeave // //============================================================================ void A_MStaffWeave(mobj_t *actor) { fixed_t newX, newY; int weaveXY, weaveZ; int angle; weaveXY = actor->special2 >> 16; weaveZ = actor->special2 & 0xFFFF; angle = (actor->angle + ANG90)>>ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]<<2); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY]<<2); weaveXY = (weaveXY + 6) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]<<2); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]<<2); P_TryMove(actor, newX, newY); actor->z -= FloatBobOffsets[weaveZ]<<1; weaveZ = (weaveZ + 3) & 63; actor->z += FloatBobOffsets[weaveZ]<<1; if (actor->z <= actor->floorz) { actor->z = actor->floorz + FRACUNIT; } actor->special2 = weaveZ + (weaveXY<<16); } //============================================================================ // // A_MStaffTrack // //============================================================================ void A_MStaffTrack(mobj_t *actor) { if ((actor->special1 == 0) && (P_Random() < 50)) { actor->special1 = (intptr_t)P_RoughMonsterSearch(actor, 10); } P_SeekerMissile(actor, ANGLE_1*2, ANGLE_1*10); } //============================================================================ // // MStaffSpawn2 - for use by mage class boss // //============================================================================ static void MStaffSpawn2(mobj_t *actor, angle_t angle) { mobj_t *mo; mo = P_SpawnMissileAngle(actor, MT_MSTAFF_FX2, angle, 0); if (mo) { mo->target = actor; mo->special1 = (intptr_t)P_RoughMonsterSearch(mo, 10); } } //============================================================================ // // A_MStaffAttack2 - for use by mage class boss // //============================================================================ void A_MStaffAttack2(mobj_t *actor) { angle_t angle; angle = actor->angle; MStaffSpawn2(actor, angle); MStaffSpawn2(actor, angle - ANGLE_1*5); MStaffSpawn2(actor, angle + ANGLE_1*5); S_StartSound(actor, SFX_MAGE_STAFF_FIRE); } //============================================================================ // // A_FPunchAttack // //============================================================================ void A_FPunchAttack(player_t *player, pspdef_t *psp) { angle_t angle; int damage; int slope; mobj_t *pmo = player->mo; fixed_t power; int i; damage = 40 + (P_Random() & 15); power = 2*FRACUNIT; PuffType = MT_PUNCHPUFF; for (i = 0; i < 16; i++) { angle = pmo->angle + i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE); if (linetarget) { player->mo->special1++; if (pmo->special1 == 3) { damage <<= 1; power = 6*FRACUNIT; PuffType = MT_HAMMERPUFF; } P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); goto punchdone; } angle = pmo->angle - i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE); if (linetarget) { pmo->special1++; if (pmo->special1 == 3) { damage <<= 1; power = 6*FRACUNIT; PuffType = MT_HAMMERPUFF; } P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); goto punchdone; } } // didn't find any creatures, so try to strike any walls pmo->special1 = 0; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, MELEERANGE); P_LineAttack(pmo, angle, MELEERANGE, slope, damage); punchdone: if (pmo->special1 == 3) { pmo->special1 = 0; P_SetPsprite(player, ps_weapon, S_PUNCHATK2_1); S_StartSound(pmo, SFX_FIGHTER_GRUNT); } return; } //============================================================================ // // A_FAxeAttack // //============================================================================ #define AXERANGE 2.25*MELEERANGE void A_FAxeAttack(player_t *player, pspdef_t *psp) { angle_t angle; mobj_t *pmo = player->mo; fixed_t power; int damage; int slope; int i; int useMana; damage = 40 + (P_Random() & 15) + (P_Random() & 7); power = 0; if (player->mana[MANA_1] > 0) { damage <<= 1; power = 6*FRACUNIT; PuffType = MT_AXEPUFF_GLOW; useMana = 1; } else { PuffType = MT_AXEPUFF; useMana = 0; } for (i = 0; i < 16; i++) { angle = pmo->angle + i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, AXERANGE); if (linetarget) { P_LineAttack(pmo, angle, AXERANGE, slope, damage); if (linetarget->flags&MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); useMana++; goto axedone; } angle = pmo->angle - i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, AXERANGE); if (linetarget) { P_LineAttack(pmo, angle, AXERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); useMana++; goto axedone; } } // didn't find any creatures, so try to strike any walls pmo->special1 = 0; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, MELEERANGE); P_LineAttack(pmo, angle, MELEERANGE, slope, damage); axedone: if (useMana == 2) { player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; if (player->mana[MANA_1] <= 0) { P_SetPsprite(player, ps_weapon, S_FAXEATK_5); } } return; } //=========================================================================== // // A_CMaceAttack // //=========================================================================== void A_CMaceAttack(player_t *player, pspdef_t *psp) { angle_t angle; int damage; int slope; int i; damage = 25 + (P_Random() & 15); PuffType = MT_HAMMERPUFF; for (i = 0; i < 16; i++) { angle = player->mo->angle + i*(ANG45/16); slope = P_AimLineAttack(player->mo, angle, 2*MELEERANGE); if (linetarget) { P_LineAttack(player->mo, angle, 2*MELEERANGE, slope, damage); AdjustPlayerAngle(player->mo); // player->mo->angle = R_PointToAngle2(player->mo->x, // player->mo->y, linetarget->x, linetarget->y); goto macedone; } angle = player->mo->angle - i*(ANG45/16); slope = P_AimLineAttack(player->mo, angle, 2*MELEERANGE); if (linetarget) { P_LineAttack(player->mo, angle, 2*MELEERANGE, slope, damage); AdjustPlayerAngle(player->mo); // player->mo->angle = R_PointToAngle2(player->mo->x, // player->mo->y, linetarget->x, linetarget->y); goto macedone; } } // didn't find any creatures, so try to strike any walls player->mo->special1 = 0; angle = player->mo->angle; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); macedone: return; } //============================================================================ // // A_CStaffCheck // //============================================================================ void A_CStaffCheck(player_t *player, pspdef_t *psp) { mobj_t *pmo; int damage; int newLife; angle_t angle; int slope; int i; pmo = player->mo; damage = 20 + (P_Random() & 15); PuffType = MT_CSTAFFPUFF; for (i = 0; i < 3; i++) { angle = pmo->angle + i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, 1.5*MELEERANGE); if (linetarget) { P_LineAttack(pmo, angle, 1.5*MELEERANGE, slope, damage); pmo->angle = R_PointToAngle2(pmo->x, pmo->y, linetarget->x, linetarget->y); if ((linetarget->player || linetarget->flags & MF_COUNTKILL) && (!(linetarget->flags2 & (MF2_DORMANT+MF2_INVULNERABLE)))) { newLife = player->health + (damage>>3); newLife = newLife > 100 ? 100 : newLife; pmo->health = player->health = newLife; P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1); } player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; break; } angle = pmo->angle - i*(ANG45/16); slope = P_AimLineAttack(player->mo, angle, 1.5*MELEERANGE); if (linetarget) { P_LineAttack(pmo, angle, 1.5*MELEERANGE, slope, damage); pmo->angle = R_PointToAngle2(pmo->x, pmo->y, linetarget->x, linetarget->y); if (linetarget->player || linetarget->flags & MF_COUNTKILL) { newLife = player->health + (damage>>4); newLife = newLife > 100 ? 100 : newLife; pmo->health = player->health = newLife; P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1); } player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; break; } } } //============================================================================ // // A_CStaffAttack // //============================================================================ void A_CStaffAttack(player_t *player, pspdef_t *psp) { mobj_t *mo; mobj_t *pmo; player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; pmo = player->mo; mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle - (ANG45/15)); if (mo) { mo->special2 = 32; } mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle + (ANG45/15)); if (mo) { mo->special2 = 0; } S_StartSound(player->mo, SFX_CLERIC_CSTAFF_FIRE); } //============================================================================ // // A_CStaffMissileSlither // //============================================================================ void A_CStaffMissileSlither(mobj_t *actor) { fixed_t newX, newY; int weaveXY; int angle; weaveXY = actor->special2; angle = (actor->angle + ANG90)>>ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY]); weaveXY = (weaveXY + 3) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]); P_TryMove(actor, newX, newY); actor->special2 = weaveXY; } //============================================================================ // // A_CStaffInitBlink // //============================================================================ void A_CStaffInitBlink(player_t *player, pspdef_t *psp) { player->mo->special1 = (P_Random()>>1) + 20; } //============================================================================ // // A_CStaffCheckBlink // //============================================================================ void A_CStaffCheckBlink(player_t *player, pspdef_t *psp) { if (!--player->mo->special1) { P_SetPsprite(player, ps_weapon, S_CSTAFFBLINK1); player->mo->special1 = (P_Random() + 50)>>2; } } //============================================================================ // // A_CFlameAttack // //============================================================================ #define FLAMESPEED (0.45*FRACUNIT) #define CFLAMERANGE (12*64*FRACUNIT) void A_CFlameAttack(player_t *player, pspdef_t *psp) { mobj_t *mo; mo = P_SpawnPlayerMissile(player->mo, MT_CFLAME_MISSILE); if (mo) { mo->thinker.function = P_BlasterMobjThinker; mo->special1 = 2; } player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE); } //============================================================================ // // A_CFlamePuff // //============================================================================ void A_CFlamePuff(mobj_t *actor) { A_UnHideThing(actor); actor->momx = 0; actor->momy = 0; actor->momz = 0; S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE); } //============================================================================ // // A_CFlameMissile // //============================================================================ void A_CFlameMissile(mobj_t *actor) { int i; int an; // int an90; fixed_t dist; mobj_t *mo; A_UnHideThing(actor); S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE); if (BlockingMobj && BlockingMobj->flags & MF_SHOOTABLE) { // Hit something, so spawn the flame circle around the thing dist = BlockingMobj->radius + 18*FRACUNIT; for (i = 0; i < 4; i++) { an = (i*ANG45)>>ANGLETOFINESHIFT; // an90 = (i*ANG45 + ANG90)>>ANGLETOFINESHIFT; mo = P_SpawnMobj(BlockingMobj->x + FixedMul(dist, finecosine[an]), BlockingMobj->y + FixedMul(dist, finesine[an]), BlockingMobj->z + 5*FRACUNIT, MT_CIRCLEFLAME); if (mo) { mo->angle = an<<ANGLETOFINESHIFT; mo->target = actor->target; mo->momx = mo->special1 = FixedMul(finecosine[an], FLAMESPEED); mo->momy = mo->special2 = FixedMul(finesine[an], FLAMESPEED); mo->tics -= P_Random() & 3; } mo = P_SpawnMobj(BlockingMobj->x - FixedMul(dist, finecosine[an]), BlockingMobj->y - FixedMul(dist, finesine[an]), BlockingMobj->z + 5*FRACUNIT, MT_CIRCLEFLAME); if (mo) { mo->angle = ANG180 + (an<<ANGLETOFINESHIFT); mo->target = actor->target; mo->momx = mo->special1 = FixedMul(finecosine[an], -FLAMESPEED); mo->momy = mo->special2 = FixedMul(finesine[an], -FLAMESPEED); mo->tics -= P_Random() & 3; } } P_SetMobjState(actor, S_FLAMEPUFF2_1); } } /* void A_CFlameAttack(player_t *player, pspdef_t *psp) { mobj_t *pmo; angle_t angle; int damage; int i; int an; // int an90; fixed_t dist; mobj_t *mo; pmo = player->mo; P_BulletSlope(pmo); damage = 25 + HITDICE(3); angle = pmo->angle; if (player->refire) { angle += (P_Random() - P_Random()) << 17; } P_AimLineAttack(pmo, angle, CFLAMERANGE); // Correctly set linetarget if (!linetarget) { angle += ANGLE_1*2; P_AimLineAttack(pmo, angle, CFLAMERANGE); if (!linetarget) { angle -= ANGLE_1*4; P_AimLineAttack(pmo, angle, CFLAMERANGE); if (!linetarget) { angle += ANGLE_1*2; } } } if (linetarget) { PuffType = MT_FLAMEPUFF2; } else { PuffType = MT_FLAMEPUFF; } P_LineAttack(pmo, angle, CFLAMERANGE, bulletslope, damage); if (linetarget) { // Hit something, so spawn the flame circle around the thing dist = linetarget->radius + 18*FRACUNIT; for (i = 0; i < 4; i++) { an = (i*ANG45)>>ANGLETOFINESHIFT; // an90 = (i*ANG45 + ANG90)>>ANGLETOFINESHIFT; mo = P_SpawnMobj(linetarget->x + FixedMul(dist, finecosine[an]), linetarget->y + FixedMul(dist, finesine[an]), linetarget->z + 5*FRACUNIT, MT_CIRCLEFLAME); if (mo) { mo->angle = an<<ANGLETOFINESHIFT; mo->target = pmo; mo->momx = mo->special1 = FixedMul(FLAMESPEED, finecosine[an]); mo->momy = mo->special2 = FixedMul(FLAMESPEED, finesine[an]); mo->tics -= P_Random() & 3; } mo = P_SpawnMobj(linetarget->x - FixedMul(dist, finecosine[an]), linetarget->y - FixedMul(dist, finesine[an]), linetarget->z + 5*FRACUNIT, MT_CIRCLEFLAME); if (mo) { mo->angle = ANG180 + (an<<ANGLETOFINESHIFT); mo->target = pmo; mo->momx = mo->special1 = FixedMul(-FLAMESPEED, finecosine[an]); mo->momy = mo->special2 = FixedMul(-FLAMESPEED, finesine[an]); mo->tics -= P_Random() & 3; } } } // Create a line of flames from the player to the flame puff CFlameCreateFlames(player->mo); player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE); } */ //============================================================================ // // A_CFlameRotate // //============================================================================ #define FLAMEROTSPEED 2*FRACUNIT void A_CFlameRotate(mobj_t *actor) { int an; an = (actor->angle + ANG90)>>ANGLETOFINESHIFT; actor->momx = actor->special1 + FixedMul(FLAMEROTSPEED, finecosine[an]); actor->momy = actor->special2 + FixedMul(FLAMEROTSPEED, finesine[an]); actor->angle += ANG90/15; } //============================================================================ // // A_CHolyAttack3 // // Spawns the spirits //============================================================================ void A_CHolyAttack3(mobj_t *actor) { P_SpawnMissile(actor, actor->target, MT_HOLY_MISSILE); S_StartSound(actor, SFX_CHOLY_FIRE); } //============================================================================ // // A_CHolyAttack2 // // Spawns the spirits //============================================================================ void A_CHolyAttack2(mobj_t *actor) { int j; int i; mobj_t *mo; mobj_t *tail, *next; for (j = 0; j < 4; j++) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_FX); if (!mo) { continue; } switch (j) { // float bob index case 0: mo->special2 = P_Random() & 7; // upper-left break; case 1: mo->special2 = 32 + (P_Random() & 7); // upper-right break; case 2: mo->special2 = (32 + (P_Random() & 7)) << 16; // lower-left break; case 3: mo->special2 = ((32 + (P_Random() & 7)) << 16) + 32 + (P_Random() & 7); break; } mo->z = actor->z; mo->angle = actor->angle + (ANGLE_45 + ANGLE_45/2) - ANGLE_45*j; P_ThrustMobj(mo, mo->angle, mo->info->speed); mo->target = actor->target; mo->args[0] = 10; // initial turn value mo->args[1] = 0; // initial look angle if (deathmatch) { // Ghosts last slightly less longer in DeathMatch mo->health = 85; } if (linetarget) { mo->special1 = (intptr_t)linetarget; mo->flags |= MF_NOCLIP|MF_SKULLFLY; mo->flags &= ~MF_MISSILE; } tail = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL); tail->special2 = (intptr_t)mo; // parent for (i = 1; i < 3; i++) { next = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL); P_SetMobjState(next, next->info->spawnstate + 1); tail->special1 = (intptr_t)next; tail = next; } tail->special1 = 0; // last tail bit } } //============================================================================ // // A_CHolyAttack // //============================================================================ void A_CHolyAttack(player_t *player, pspdef_t *psp) { player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; P_SpawnPlayerMissile(player->mo, MT_HOLY_MISSILE); if (player == &players[consoleplayer]) { player->damagecount = 0; player->bonuscount = 0; V_SetPaletteShift(STARTHOLYPAL); } S_StartSound(player->mo, SFX_CHOLY_FIRE); } //============================================================================ // // A_CHolyPalette // //============================================================================ void A_CHolyPalette(player_t *player, pspdef_t *psp) { int pal; if (player == &players[consoleplayer]) { pal = STARTHOLYPAL+psp->state - (&states[S_CHOLYATK_6]); if (pal == STARTHOLYPAL + 3) { // reset back to original playpal pal = 0; } V_SetPaletteShift(pal); } } //============================================================================ // // CHolyFindTarget // //============================================================================ static void CHolyFindTarget(mobj_t *actor) { mobj_t *target; if ((target = P_RoughMonsterSearch(actor, 6))) { actor->special1 = (intptr_t)target; actor->flags |= MF_NOCLIP|MF_SKULLFLY; actor->flags &= ~MF_MISSILE; } } //============================================================================ // // CHolySeekerMissile // // Similar to P_SeekerMissile, but seeks to a random Z on the target //============================================================================ static void CHolySeekerMissile(mobj_t *actor, angle_t thresh, angle_t turnMax) { int dir; int dist; angle_t delta; angle_t angle; mobj_t *target; fixed_t newZ; fixed_t deltaZ; target = (mobj_t *)actor->special1; if (target == NULL) { return; } if (!(target->flags & MF_SHOOTABLE) || (!(target->flags & MF_COUNTKILL) && !target->player)) { // Target died/target isn't a player or creature actor->special1 = 0; actor->flags &= ~(MF_NOCLIP|MF_SKULLFLY); actor->flags |= MF_MISSILE; CHolyFindTarget(actor); return; } dir = P_FaceMobj(actor, target, &delta); if (delta > thresh) { delta >>= 1; if (delta > turnMax) { delta = turnMax; } } if (dir) { // Turn clockwise actor->angle += delta; } else { // Turn counter clockwise actor->angle -= delta; } angle = actor->angle>>ANGLETOFINESHIFT; actor->momx = FixedMul(actor->info->speed, finecosine[angle]); actor->momy = FixedMul(actor->info->speed, finesine[angle]); if (!(leveltime & 15) || actor->z > target->z + (target->height) || actor->z + actor->height < target->z) { newZ = target->z + ((P_Random()*target->height)>>8); deltaZ = newZ - actor->z; if (abs(deltaZ) > 15*FRACUNIT) { if (deltaZ > 0) { deltaZ = 15*FRACUNIT; } else { deltaZ = -15*FRACUNIT; } } dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) { dist = 1; } actor->momz = deltaZ / dist; } return; } //============================================================================ // // A_CHolyWeave // //============================================================================ static void CHolyWeave(mobj_t *actor) { fixed_t newX, newY; int weaveXY, weaveZ; int angle; weaveXY = actor->special2 >> 16; weaveZ = actor->special2 & 0xFFFF; angle = (actor->angle + ANG90)>>ANGLETOFINESHIFT; newX = actor->x-FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]<<2); newY = actor->y-FixedMul(finesine[angle], FloatBobOffsets[weaveXY]<<2); weaveXY = (weaveXY + (P_Random() % 5)) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]<<2); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]<<2); P_TryMove(actor, newX, newY); actor->z -= FloatBobOffsets[weaveZ]<<1; weaveZ = (weaveZ + (P_Random() % 5)) & 63; actor->z += FloatBobOffsets[weaveZ]<<1; actor->special2 = weaveZ + (weaveXY<<16); } //============================================================================ // // A_CHolySeek // //============================================================================ void A_CHolySeek(mobj_t *actor) { actor->health--; if (actor->health <= 0) { actor->momx >>= 2; actor->momy >>= 2; actor->momz = 0; P_SetMobjState(actor, actor->info->deathstate); actor->tics -= P_Random() & 3; return; } if (actor->special1) { CHolySeekerMissile(actor, actor->args[0]*ANGLE_1, actor->args[0]*ANGLE_1*2); if (!((leveltime + 7) & 15)) { actor->args[0] = 5 + (P_Random()/20); } } CHolyWeave(actor); } //============================================================================ // // CHolyTailFollow // //============================================================================ static void CHolyTailFollow(mobj_t *actor, fixed_t dist) { mobj_t *child; int an; fixed_t oldDistance, newDistance; child = (mobj_t *)actor->special1; if (child) { an = R_PointToAngle2(actor->x, actor->y, child->x, child->y)>>ANGLETOFINESHIFT; oldDistance = P_AproxDistance(child->x - actor->x, child->y - actor->y); if (P_TryMove(child, actor->x + FixedMul(dist, finecosine[an]), actor->y + FixedMul(dist, finesine[an]))) { newDistance = P_AproxDistance(child->x-actor->x, child->y-actor->y) - FRACUNIT; if (oldDistance < FRACUNIT) { if (child->z < actor->z) { child->z = actor->z - dist; } else { child->z = actor->z + dist; } } else { child->z = actor->z + FixedMul(FixedDiv(newDistance, oldDistance), child->z - actor->z); } } CHolyTailFollow(child, dist - FRACUNIT); } } //============================================================================ // // CHolyTailRemove // //============================================================================ static void CHolyTailRemove(mobj_t *actor) { mobj_t *child; child = (mobj_t *)actor->special1; if (child) { CHolyTailRemove(child); } P_RemoveMobj(actor); } //============================================================================ // // A_CHolyTail // //============================================================================ void A_CHolyTail(mobj_t *actor) { mobj_t *parent; parent = (mobj_t *)actor->special2; if (parent) { if (parent->state >= &states[parent->info->deathstate]) { // Ghost removed, so remove all tail parts CHolyTailRemove(actor); return; } else if (P_TryMove(actor, parent->x - FixedMul(14*FRACUNIT, finecosine[parent->angle>>ANGLETOFINESHIFT]), parent->y - FixedMul(14*FRACUNIT, finesine[parent->angle>>ANGLETOFINESHIFT]))) { actor->z = parent->z-5*FRACUNIT; } CHolyTailFollow(actor, 10*FRACUNIT); } } //============================================================================ // // A_CHolyCheckScream // //============================================================================ void A_CHolyCheckScream(mobj_t *actor) { A_CHolySeek(actor); if (P_Random() < 20) { S_StartSound(actor, SFX_SPIRIT_ACTIVE); } if (!actor->special1) { CHolyFindTarget(actor); } } //============================================================================ // // A_CHolySpawnPuff // //============================================================================ void A_CHolySpawnPuff(mobj_t *actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_MISSILE_PUFF); } //---------------------------------------------------------------------------- // // PROC A_FireConePL1 // //---------------------------------------------------------------------------- #define SHARDSPAWN_LEFT 1 #define SHARDSPAWN_RIGHT 2 #define SHARDSPAWN_UP 4 #define SHARDSPAWN_DOWN 8 void A_FireConePL1(player_t *player, pspdef_t *psp) { angle_t angle; int damage; int slope; int i; mobj_t *pmo, *mo; int conedone = false; pmo = player->mo; player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; S_StartSound(pmo, SFX_MAGE_SHARDS_FIRE); damage = 90 + (P_Random() & 15); for (i = 0; i < 16; i++) { angle = pmo->angle + i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, MELEERANGE); (void) slope; /* variable set but not used */ if (linetarget) { pmo->flags2 |= MF2_ICEDAMAGE; P_DamageMobj(linetarget, pmo, pmo, damage); pmo->flags2 &= ~MF2_ICEDAMAGE; conedone = true; break; } } // didn't find any creatures, so fire projectiles if (!conedone) { mo = P_SpawnPlayerMissile(pmo, MT_SHARDFX1); if (mo) { mo->special1 = SHARDSPAWN_LEFT|SHARDSPAWN_DOWN|SHARDSPAWN_UP |SHARDSPAWN_RIGHT; mo->special2 = 3; // Set sperm count (levels of reproductivity) mo->target = pmo; mo->args[0] = 3; // Mark Initial shard as super damage } } } void A_ShedShard(mobj_t *actor) { mobj_t *mo; int spawndir = actor->special1; int spermcount = actor->special2; if (spermcount <= 0) return; // No sperm left actor->special2 = 0; spermcount--; // every so many calls, spawn a new missile in it's set directions if (spawndir & SHARDSPAWN_LEFT) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle + (ANG45/9), 0, (20 + 2*spermcount)<<FRACBITS); if (mo) { mo->special1 = SHARDSPAWN_LEFT; mo->special2 = spermcount; mo->momz = actor->momz; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } if (spawndir & SHARDSPAWN_RIGHT) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle - (ANG45/9), 0, (20 + 2*spermcount)<<FRACBITS); if (mo) { mo->special1 = SHARDSPAWN_RIGHT; mo->special2 = spermcount; mo->momz = actor->momz; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } if (spawndir & SHARDSPAWN_UP) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle, 0, (15 + 2*spermcount)<<FRACBITS); if (mo) { mo->momz = actor->momz; mo->z += 8*FRACUNIT; if (spermcount & 1) // Every other reproduction mo->special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; else mo->special1 = SHARDSPAWN_UP; mo->special2 = spermcount; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } if (spawndir & SHARDSPAWN_DOWN) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle, 0, (15 + 2*spermcount)<<FRACBITS); if (mo) { mo->momz = actor->momz; mo->z -= 4*FRACUNIT; if (spermcount & 1) // Every other reproduction mo->special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; else mo->special1 = SHARDSPAWN_DOWN; mo->special2 = spermcount; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } } //---------------------------------------------------------------------------- // // PROC A_HideInCeiling // //---------------------------------------------------------------------------- /* void A_HideInCeiling(mobj_t *actor) { actor->z = actor->ceilingz + 4*FRACUNIT; } */ //---------------------------------------------------------------------------- // // PROC A_FloatPuff // //---------------------------------------------------------------------------- /* void A_FloatPuff(mobj_t *puff) { puff->momz += 1.8*FRACUNIT; } */ void A_Light0(player_t *player, pspdef_t *psp) { player->extralight = 0; } /* void A_Light1(player_t *player, pspdef_t *psp) { player->extralight = 1; } */ /* void A_Light2(player_t *player, pspdef_t *psp) { player->extralight = 2; } */ //------------------------------------------------------------------------ // // PROC P_SetupPsprites // // Called at start of level for each player // //------------------------------------------------------------------------ void P_SetupPsprites(player_t *player) { int i; // Remove all psprites for (i = 0; i < NUMPSPRITES; i++) { player->psprites[i].state = NULL; } // Spawn the ready weapon player->pendingweapon = player->readyweapon; P_BringUpWeapon(player); } //------------------------------------------------------------------------ // // PROC P_MovePsprites // // Called every tic by player thinking routine // //------------------------------------------------------------------------ void P_MovePsprites(player_t *player) { int i; pspdef_t *psp; state_t *state; psp = &player->psprites[0]; for (i = 0; i < NUMPSPRITES; i++, psp++) { if ((state = psp->state) != 0) // a null state means not active { // drop tic count and possibly change state if (psp->tics != -1) // a -1 tic count never changes { psp->tics--; if (!psp->tics) { P_SetPsprite(player, i, psp->state->nextstate); } } } } player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; } //============================================================================ // // ASSASSIN WEAPONS / ATTACKS (ADD-ON CLASS FROM HEXEN II) // //============================================================================ #if defined(ASSASSIN) //============================================================================ // // A_AKnifeAttack // // Jim Cameron did most of this one //============================================================================ void A_AKnifeAttack(player_t *player, pspdef_t *psp) { angle_t angle; int damage; int slope; mobj_t *pmo = player->mo; fixed_t power; int i; boolean oof = false; /* jim - the Katar should be a bit feebler */ damage = 20 + (P_Random() & 15); power = 2*FRACUNIT; PuffType = MT_PUNCHPUFF; for (i = 0; i < 16; i++) { angle = pmo->angle + i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE); if (linetarget) { player->mo->special1++; /* * jim - this is the Mighty Blow for the fighter and is * not useful here */ #if 0 if (pmo->special1 == 3) { damage <<= 1; power = 6*FRACUNIT; PuffType = MT_HAMMERPUFF; } #endif /* * jim - instead of that we make the Katar deal more * damage to a monster if struck from behind. Assume * so if the angle of striking is within 45 degrees * of the angle the target is facing in * OOOPS! but only if it IS a monster! Striking trees from * behind might be amusing but doesn't do much for realism 8-) */ if (linetarget->flags & MF_COUNTKILL || linetarget->player) { if ((angle - linetarget->angle < ANG45) || (linetarget->angle - angle < ANG45)) { damage *= 15; power = 6 * FRACUNIT; PuffType = MT_HAMMERPUFF; oof = true; } } P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); goto knifedone; } angle = pmo->angle - i*(ANG45/16); slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE); if (linetarget) { player->mo->special1++; /* * jim - this is the Mighty Blow for the fighter and is * not useful here */ #if 0 if (pmo->special1 == 3) { damage <<= 1; power = 6*FRACUNIT; PuffType = MT_HAMMERPUFF; } #endif /* * jim - instead of that we make the Katar deal more * damage to a monster if struck from behind. Assume * so if the angle of striking is within 45 degrees * of the angle the target is facing in * OOOPS! but only if it IS a monster! Striking trees from * behind might be amusing but doesn't do much for realism 8-) */ if (linetarget->flags & MF_COUNTKILL || linetarget->player) { if ((angle - linetarget->angle < ANG45) || (linetarget->angle - angle < ANG45)) { damage *= 15; power = 6 * FRACUNIT; PuffType = MT_HAMMERPUFF; oof = true; } } P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); goto knifedone; } } /* didn't find any creatures, so try to strike any walls*/ pmo->special1 = 0; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, MELEERANGE); P_LineAttack(pmo, angle, MELEERANGE, slope, damage); knifedone: if (oof) { pmo->special1 = 0; P_SetPsprite(player, ps_weapon, S_KATARATK2_1); /* jim - come on, she's a girl! */ S_StartSound(pmo, SFX_PLAYER_MAGE_GRUNT); } } void A_ACrossAttack(player_t *player, pspdef_t *psp) { mobj_t *mo; mobj_t *pmo = player->mo; player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; // pmo = player->mo; // P_SpawnPlayerMissile(pmo, MT_CSTAFF_MISSILE); /* * jim - special2 is used to control the serpent staff projectiles' * `slither' and is not used here * We do however want to give crossbow missiles BlasterMobjThinker()s * instead of the ordinary ones because they are FAST. */ mo = P_SPMAngle(pmo, MT_ACROSS_MISSILE, pmo->angle); if (mo) { /* mo->special2 = 16; */ mo->thinker.function = P_BlasterMobjThinker; } mo = P_SPMAngle(pmo, MT_ACROSS_MISSILE, pmo->angle - (ANG45/10)); if (mo) { /* mo->special2 = 32; */ mo->thinker.function = P_BlasterMobjThinker; } mo = P_SPMAngle(pmo, MT_ACROSS_MISSILE, pmo->angle + (ANG45/10)); if (mo) { /* mo->special2 = 0; */ mo->thinker.function = P_BlasterMobjThinker; } S_StartSound(player->mo, SFX_CLERIC_CSTAFF_FIRE); } void A_AGrenAttack(player_t *player, pspdef_t *psp) { mobj_t *mo; mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z - player->mo->floorclip + 35*FRACUNIT, MT_THROWINGBOMB); if (mo) { mo->angle = player->mo->angle + (((P_Random() & 7) - 4)<<24); mo->momz = 4*FRACUNIT + ((player->lookdir)<<(FRACBITS - 4)); mo->z += player->lookdir<<(FRACBITS - 4); P_ThrustMobj(mo, mo->angle, mo->info->speed); mo->momx += player->mo->momx>>1; mo->momy += player->mo->momy>>1; mo->target = player->mo; mo->tics -= P_Random() & 3; P_CheckMissileSpawn(mo); } } void A_AStaffAttack(player_t *player, pspdef_t *psp) { angle_t angle; mobj_t *pmo; /* THIS ISN'T FINISHED YET!! */ player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon]; pmo = player->mo; angle = pmo->angle; } #endif /* ASSASSIN */