//************************************************************************** //** //** p_user.c : Heretic 2 : Raven Software, Corp. //** //** $RCSfile: p_user.c,v $ //** $Revision: 1.123 $ //** $Date: 96/01/05 14:21:01 $ //** $Author: bgokey $ //** //************************************************************************** #include "h2def.h" #include "p_local.h" #include "soundst.h" void P_PlayerNextArtifact(player_t *player); // Macros #define MAXBOB 0x100000 // 16 pixels of bob // Data boolean onground; int newtorch; // used in the torch flicker effect. int newtorchdelta; int PStateNormal[NUMCLASSES] = { S_FPLAY, S_CPLAY, S_MPLAY, S_PIGPLAY }; int PStateRun[NUMCLASSES] = { S_FPLAY_RUN1, S_CPLAY_RUN1, S_MPLAY_RUN1, S_PIGPLAY_RUN1 }; int PStateAttack[NUMCLASSES] = { S_FPLAY_ATK1, S_CPLAY_ATK1, S_MPLAY_ATK1, S_PIGPLAY_ATK1 }; int PStateAttackEnd[NUMCLASSES] = { S_FPLAY_ATK2, S_CPLAY_ATK3, S_MPLAY_ATK2, S_PIGPLAY_ATK1 }; int ArmorMax[NUMCLASSES] = { 20, 18, 16, 1 }; /* ================== = = P_Thrust = = moves the given origin along a given angle = ================== */ void P_Thrust(player_t *player, angle_t angle, fixed_t move) { angle >>= ANGLETOFINESHIFT; if(player->powers[pw_flight] && !(player->mo->z <= player->mo->floorz)) { player->mo->momx += FixedMul(move, finecosine[angle]); player->mo->momy += FixedMul(move, finesine[angle]); } else if(P_GetThingFloorType(player->mo) == FLOOR_ICE) // Friction_Low { player->mo->momx += FixedMul(move>>1, finecosine[angle]); player->mo->momy += FixedMul(move>>1, finesine[angle]); } else { player->mo->momx += FixedMul(move, finecosine[angle]); player->mo->momy += FixedMul(move, finesine[angle]); } } /* ================== = = P_CalcHeight = =Calculate the walking / running height adjustment = ================== */ void P_CalcHeight (player_t *player) { int angle; fixed_t bob; // // regular movement bobbing (needs to be calculated for gun swing even // if not on ground) // OPTIMIZE: tablify angle player->bob = FixedMul (player->mo->momx, player->mo->momx)+ FixedMul (player->mo->momy,player->mo->momy); player->bob >>= 2; if (player->bob>MAXBOB) player->bob = MAXBOB; if(player->mo->flags2&MF2_FLY && !onground) { player->bob = FRACUNIT/2; } if ((player->cheats & CF_NOMOMENTUM)) { player->viewz = player->mo->z + VIEWHEIGHT; if (player->viewz > player->mo->ceilingz-4*FRACUNIT) player->viewz = player->mo->ceilingz-4*FRACUNIT; player->viewz = player->mo->z + player->viewheight; return; } angle = (FINEANGLES/20*leveltime)&FINEMASK; bob = FixedMul ( player->bob/2, finesine[angle]); // // move viewheight // if (player->playerstate == PST_LIVE) { player->viewheight += player->deltaviewheight; if (player->viewheight > VIEWHEIGHT) { player->viewheight = VIEWHEIGHT; player->deltaviewheight = 0; } if (player->viewheight < VIEWHEIGHT/2) { player->viewheight = VIEWHEIGHT/2; if (player->deltaviewheight <= 0) player->deltaviewheight = 1; } if (player->deltaviewheight) { player->deltaviewheight += FRACUNIT/4; if (!player->deltaviewheight) player->deltaviewheight = 1; } } if(player->morphTics) { player->viewz = player->mo->z+player->viewheight-(20*FRACUNIT); } else { player->viewz = player->mo->z+player->viewheight+bob; } if(player->mo->floorclip && player->playerstate != PST_DEAD && player->mo->z <= player->mo->floorz) { player->viewz -= player->mo->floorclip; } if(player->viewz > player->mo->ceilingz-4*FRACUNIT) { player->viewz = player->mo->ceilingz-4*FRACUNIT; } if(player->viewz < player->mo->floorz+4*FRACUNIT) { player->viewz = player->mo->floorz+4*FRACUNIT; } } /* ================= = = P_MovePlayer = ================= */ void P_MovePlayer(player_t *player) { int look; int fly; ticcmd_t *cmd; cmd = &player->cmd; player->mo->angle += (cmd->angleturn<<16); onground = (player->mo->z <= player->mo->floorz || (player->mo->flags2&MF2_ONMOBJ)); if(cmd->forwardmove) { if(onground || player->mo->flags2&MF2_FLY) { P_Thrust(player, player->mo->angle, cmd->forwardmove*2048); } else { P_Thrust(player, player->mo->angle, FRACUNIT>>8); } } if(cmd->sidemove) { if(onground || player->mo->flags2&MF2_FLY) { P_Thrust(player, player->mo->angle-ANG90, cmd->sidemove*2048); } else { P_Thrust(player, player->mo->angle, FRACUNIT>>8); } } if(cmd->forwardmove || cmd->sidemove) { if(player->mo->state == &states[PStateNormal[player->class]]) { P_SetMobjState(player->mo, PStateRun[player->class]); } } look = cmd->lookfly&15; if(look > 7) { look -= 16; } if(look) { if(look == TOCENTER) { player->centering = true; } else { player->lookdir += 5*look; if(player->lookdir > 90 || player->lookdir < -110) { player->lookdir -= 5*look; } } } if(player->centering) { if(player->lookdir > 0) { player->lookdir -= 8; } else if(player->lookdir < 0) { player->lookdir += 8; } if(abs(player->lookdir) < 8) { player->lookdir = 0; player->centering = false; } } fly = cmd->lookfly>>4; if(fly > 7) { fly -= 16; } if(fly && player->powers[pw_flight]) { if(fly != TOCENTER) { player->flyheight = fly*2; if(!(player->mo->flags2&MF2_FLY)) { player->mo->flags2 |= MF2_FLY; player->mo->flags |= MF_NOGRAVITY; if(player->mo->momz <= -39*FRACUNIT) { // stop falling scream S_StopSound(player->mo); } } } else { player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; } } else if(fly > 0) { P_PlayerUseArtifact(player, arti_fly); } if(player->mo->flags2&MF2_FLY) { player->mo->momz = player->flyheight*FRACUNIT; if(player->flyheight) { player->flyheight /= 2; } } } //========================================================================== // // P_DeathThink // //========================================================================== void P_DeathThink(player_t *player) { int dir; angle_t delta; int lookDelta; extern int inv_ptr; extern int curpos; P_MovePsprites(player); onground = (player->mo->z <= player->mo->floorz); if(player->mo->type == MT_BLOODYSKULL || player->mo->type == MT_ICECHUNK) { // Flying bloody skull or flying ice chunk player->viewheight = 6*FRACUNIT; player->deltaviewheight = 0; //player->damagecount = 20; if(onground) { if(player->lookdir < 60) { lookDelta = (60-player->lookdir)/8; if(lookDelta < 1 && (leveltime&1)) { lookDelta = 1; } else if(lookDelta > 6) { lookDelta = 6; } player->lookdir += lookDelta; } } } else if(!(player->mo->flags2&MF2_ICEDAMAGE)) { // Fall to ground (if not frozen) player->deltaviewheight = 0; if(player->viewheight > 6*FRACUNIT) { player->viewheight -= FRACUNIT; } if(player->viewheight < 6*FRACUNIT) { player->viewheight = 6*FRACUNIT; } if(player->lookdir > 0) { player->lookdir -= 6; } else if(player->lookdir < 0) { player->lookdir += 6; } if(abs(player->lookdir) < 6) { player->lookdir = 0; } } P_CalcHeight(player); if(player->attacker && player->attacker != player->mo) { // Watch killer dir = P_FaceMobj(player->mo, player->attacker, &delta); if(delta < ANGLE_1*10) { // Looking at killer, so fade damage and poison counters if(player->damagecount) { player->damagecount--; } if(player->poisoncount) { player->poisoncount--; } } delta = delta/8; if(delta > ANGLE_1*5) { delta = ANGLE_1*5; } if(dir) { // Turn clockwise player->mo->angle += delta; } else { // Turn counter clockwise player->mo->angle -= delta; } } else if(player->damagecount || player->poisoncount) { if(player->damagecount) { player->damagecount--; } else { player->poisoncount--; } } if(player->cmd.buttons&BT_USE) { if(player == &players[consoleplayer]) { I_SetPalette((byte *)W_CacheLumpName("PLAYPAL", PU_CACHE)); inv_ptr = 0; curpos = 0; newtorch = 0; newtorchdelta = 0; } player->playerstate = PST_REBORN; player->mo->special1 = player->class; if(player->mo->special1 > 2) { player->mo->special1 = 0; } // Let the mobj know the player has entered the reborn state. Some // mobjs need to know when it's ok to remove themselves. player->mo->special2 = 666; } } //---------------------------------------------------------------------------- // // PROC P_MorphPlayerThink // //---------------------------------------------------------------------------- void P_MorphPlayerThink(player_t *player) { mobj_t *pmo; if(player->morphTics&15) { return; } pmo = player->mo; if(!(pmo->momx+pmo->momy) && P_Random() < 64) { // Snout sniff P_SetPspriteNF(player, ps_weapon, S_SNOUTATK2); S_StartSound(pmo, SFX_PIG_ACTIVE1); // snort return; } if(P_Random() < 48) { if(P_Random() < 128) { S_StartSound(pmo, SFX_PIG_ACTIVE1); } else { S_StartSound(pmo, SFX_PIG_ACTIVE2); } } } //---------------------------------------------------------------------------- // // FUNC P_GetPlayerNum // //---------------------------------------------------------------------------- int P_GetPlayerNum(player_t *player) { int i; for(i = 0; i < MAXPLAYERS; i++) { if(player == &players[i]) { return(i); } } return(0); } //---------------------------------------------------------------------------- // // FUNC P_UndoPlayerMorph // //---------------------------------------------------------------------------- boolean P_UndoPlayerMorph(player_t *player) { mobj_t *fog; mobj_t *mo; mobj_t *pmo; fixed_t x; fixed_t y; fixed_t z; angle_t angle; int playerNum; weapontype_t weapon; int oldFlags; int oldFlags2; int oldBeast; if (player->cheats & CF_FUNNYPIGGY) { player->morphTics = 10000; return(false); } pmo = player->mo; x = pmo->x; y = pmo->y; z = pmo->z; angle = pmo->angle; weapon = pmo->special1; oldFlags = pmo->flags; oldFlags2 = pmo->flags2; oldBeast = pmo->type; P_SetMobjState(pmo, S_FREETARGMOBJ); playerNum = P_GetPlayerNum(player); switch(PlayerClass[playerNum]) { case PCLASS_FIGHTER: mo = P_SpawnMobj(x, y, z, MT_PLAYER_FIGHTER); break; case PCLASS_CLERIC: mo = P_SpawnMobj(x, y, z, MT_PLAYER_CLERIC); break; case PCLASS_MAGE: mo = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE); break; default: I_Error("P_UndoPlayerMorph: Unknown player class %d\n", player->class); } if(P_TestMobjLocation(mo) == false) { // Didn't fit P_RemoveMobj(mo); mo = P_SpawnMobj(x, y, z, oldBeast); mo->angle = angle; mo->health = player->health; mo->special1 = weapon; mo->player = player; mo->flags = oldFlags; mo->flags2 = oldFlags2; player->mo = mo; player->morphTics = 2*35; return(false); } if(player->class == PCLASS_FIGHTER) { // The first type should be blue, and the third should be the // Fighter's original gold color if(playerNum == 0) { mo->flags |= 2<flags |= playerNum<flags |= playerNum<angle = angle; mo->player = player; mo->reactiontime = 18; if(oldFlags2&MF2_FLY) { mo->flags2 |= MF2_FLY; mo->flags |= MF_NOGRAVITY; } player->morphTics = 0; player->health = mo->health = MAXHEALTH; player->mo = mo; player->class = PlayerClass[playerNum]; angle >>= ANGLETOFINESHIFT; fog = P_SpawnMobj(x+20*finecosine[angle], y+20*finesine[angle], z+TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); P_PostMorphWeapon(player, weapon); return(true); } //---------------------------------------------------------------------------- // // PROC P_PlayerThink // //---------------------------------------------------------------------------- void P_PlayerThink(player_t *player) { ticcmd_t *cmd; weapontype_t newweapon; int floorType; mobj_t *pmo; if (!player) I_Error("P_PlayerThink: no player!"); if (!player->mo) I_Error("P_PlayerThink: no player->mo!"); // No-clip cheat if(player->cheats&CF_NOCLIP) { player->mo->flags |= MF_NOCLIP; } else { player->mo->flags &= ~MF_NOCLIP; } cmd = &player->cmd; if(player->mo->flags&MF_JUSTATTACKED) { // Gauntlets attack auto forward motion cmd->angleturn = 0; cmd->forwardmove = 0xc800/512; cmd->sidemove = 0; player->mo->flags &= ~MF_JUSTATTACKED; } // messageTics is above the rest of the counters so that messages will // go away, even in death. player->messageTics--; // Can go negative if(!player->messageTics || player->messageTics == -1) { // Refresh the screen when a message goes away player->ultimateMessage = false; // clear out any chat messages. player->yellowMessage = false; if(player == &players[consoleplayer]) { BorderTopRefresh = true; } } player->worldTimer++; if(player->playerstate == PST_DEAD) { P_DeathThink(player); return; } if(player->jumpTics) { player->jumpTics--; } if(player->morphTics) { P_MorphPlayerThink(player); } // Handle movement if(player->mo->reactiontime) { // Player is frozen player->mo->reactiontime--; } else { P_MovePlayer(player); pmo = player->mo; if(player->powers[pw_speed] && !(leveltime&1) && P_AproxDistance(pmo->momx, pmo->momy) > 12*FRACUNIT) { mobj_t *speedMo; int playerNum; speedMo = P_SpawnMobj(pmo->x, pmo->y, pmo->z, MT_PLAYER_SPEED); if(speedMo) { speedMo->angle = pmo->angle; playerNum = P_GetPlayerNum(player); if(player->class == PCLASS_FIGHTER) { // The first type should be blue, and the // third should be the Fighter's original gold color if(playerNum == 0) { speedMo->flags |= 2<flags |= playerNum<flags |= playerNum<target = pmo; speedMo->special1 = player->class; if(speedMo->special1 > 2) { speedMo->special1 = 0; } speedMo->sprite = pmo->sprite; speedMo->floorclip = pmo->floorclip; if(player == &players[consoleplayer]) { speedMo->flags2 |= MF2_DONTDRAW; } } } } P_CalcHeight(player); if(player->mo->subsector->sector->special) { P_PlayerInSpecialSector(player); } if((floorType = P_GetThingFloorType(player->mo)) != FLOOR_SOLID) { P_PlayerOnSpecialFlat(player, floorType); } switch(player->class) { case PCLASS_FIGHTER: if(player->mo->momz <= -35*FRACUNIT && player->mo->momz >= -40*FRACUNIT && !player->morphTics && !S_GetSoundPlayingInfo(player->mo, SFX_PLAYER_FIGHTER_FALLING_SCREAM)) { S_StartSound(player->mo, SFX_PLAYER_FIGHTER_FALLING_SCREAM); } break; case PCLASS_CLERIC: if(player->mo->momz <= -35*FRACUNIT && player->mo->momz >= -40*FRACUNIT && !player->morphTics && !S_GetSoundPlayingInfo(player->mo, SFX_PLAYER_CLERIC_FALLING_SCREAM)) { S_StartSound(player->mo, SFX_PLAYER_CLERIC_FALLING_SCREAM); } break; case PCLASS_MAGE: if(player->mo->momz <= -35*FRACUNIT && player->mo->momz >= -40*FRACUNIT && !player->morphTics && !S_GetSoundPlayingInfo(player->mo, SFX_PLAYER_MAGE_FALLING_SCREAM)) { S_StartSound(player->mo, SFX_PLAYER_MAGE_FALLING_SCREAM); } break; default: break; } if(cmd->arti) { // Use an artifact if((cmd->arti&AFLAG_JUMP) && onground && !player->jumpTics) { if(player->morphTics) { player->mo->momz = 6*FRACUNIT; } else { player->mo->momz = 9*FRACUNIT; } player->mo->flags2 &= ~MF2_ONMOBJ; player->jumpTics = 18; } else if(cmd->arti&AFLAG_SUICIDE) { P_DamageMobj(player->mo, NULL, NULL, 10000); } if(cmd->arti == NUMARTIFACTS) { // use one of each artifact (except puzzle artifacts) int i; for(i = 1; i < arti_firstpuzzitem; i++) { P_PlayerUseArtifact(player, i); } } else { P_PlayerUseArtifact(player, cmd->arti&AFLAG_MASK); } } // Check for weapon change if(cmd->buttons&BT_SPECIAL) { // A special event has no other buttons cmd->buttons = 0; } if(cmd->buttons&BT_CHANGE && !player->morphTics) { // The actual changing of the weapon is done when the weapon // psprite can do it (A_WeaponReady), so it doesn't happen in // the middle of an attack. newweapon = (cmd->buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT; if(player->weaponowned[newweapon] && newweapon != player->readyweapon) { player->pendingweapon = newweapon; } } // Check for use if(cmd->buttons&BT_USE) { if(!player->usedown) { P_UseLines(player); player->usedown = true; } } else { player->usedown = false; } // Morph counter if(player->morphTics) { if(!--player->morphTics) { // Attempt to undo the pig P_UndoPlayerMorph(player); } } // Cycle psprites P_MovePsprites(player); // Other Counters if(player->powers[pw_invulnerability]) { if(player->class == PCLASS_CLERIC) { if(!(leveltime&7) && player->mo->flags&MF_SHADOW && !(player->mo->flags2&MF2_DONTDRAW)) { player->mo->flags &= ~MF_SHADOW; if(!(player->mo->flags&MF_ALTSHADOW)) { player->mo->flags2 |= MF2_DONTDRAW|MF2_NONSHOOTABLE; } } if(!(leveltime&31)) { if(player->mo->flags2&MF2_DONTDRAW) { if(!(player->mo->flags&MF_SHADOW)) { player->mo->flags |= MF_SHADOW|MF_ALTSHADOW; } else { player->mo->flags2 &= ~(MF2_DONTDRAW|MF2_NONSHOOTABLE); } } else { player->mo->flags |= MF_SHADOW; player->mo->flags &= ~MF_ALTSHADOW; } } } if(!(--player->powers[pw_invulnerability])) { player->mo->flags2 &= ~(MF2_INVULNERABLE|MF2_REFLECTIVE); if(player->class == PCLASS_CLERIC) { player->mo->flags2 &= ~(MF2_DONTDRAW|MF2_NONSHOOTABLE); player->mo->flags &= ~(MF_SHADOW|MF_ALTSHADOW); } } } if(player->powers[pw_minotaur]) { player->powers[pw_minotaur]--; } if(player->powers[pw_infrared]) { player->powers[pw_infrared]--; } if(player->powers[pw_flight] && netgame) { if(!--player->powers[pw_flight]) { if(player->mo->z != player->mo->floorz) { #ifdef __WATCOMC__ if(!useexterndriver) { player->centering = true; } #else player->centering = true; #endif } player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; BorderTopRefresh = true; //make sure the sprite's cleared out } } if(player->powers[pw_speed]) { player->powers[pw_speed]--; } if(player->damagecount) { player->damagecount--; } if(player->bonuscount) { player->bonuscount--; } if(player->poisoncount && !(leveltime&15)) { player->poisoncount -= 5; if(player->poisoncount < 0) { player->poisoncount = 0; } P_PoisonDamage(player, player->poisoner, 1, true); } // Colormaps // if(player->powers[pw_invulnerability]) // { // if(player->powers[pw_invulnerability] > BLINKTHRESHOLD // || (player->powers[pw_invulnerability]&8)) // { // player->fixedcolormap = INVERSECOLORMAP; // } // else // { // player->fixedcolormap = 0; // } // } // else if(player->powers[pw_infrared]) { if (player->powers[pw_infrared] <= BLINKTHRESHOLD) { if(player->powers[pw_infrared]&8) { player->fixedcolormap = 0; } else { player->fixedcolormap = 1; } } else if(!(leveltime&16) && player == &players[consoleplayer]) { if(newtorch) { if(player->fixedcolormap+newtorchdelta > 7 || player->fixedcolormap+newtorchdelta < 1 || newtorch == player->fixedcolormap) { newtorch = 0; } else { player->fixedcolormap += newtorchdelta; } } else { newtorch = (M_Random()&7)+1; newtorchdelta = (newtorch == player->fixedcolormap) ? 0 : ((newtorch > player->fixedcolormap) ? 1 : -1); } } } else { player->fixedcolormap = 0; } } //---------------------------------------------------------------------------- // // PROC P_ArtiTele // //---------------------------------------------------------------------------- void P_ArtiTele(player_t *player) { int i; int selections; fixed_t destX; fixed_t destY; angle_t destAngle; if(deathmatch) { selections = deathmatch_p-deathmatchstarts; i = P_Random()%selections; destX = deathmatchstarts[i].x<mo, destX, destY, destAngle, true); if(player->morphTics) { // Teleporting away will undo any morph effects (pig) P_UndoPlayerMorph(player); } //S_StartSound(NULL, sfx_wpnup); // Full volume laugh } //---------------------------------------------------------------------------- // // PROC P_ArtiTeleportOther // //---------------------------------------------------------------------------- void P_ArtiTeleportOther(player_t *player) { mobj_t *mo; mo=P_SpawnPlayerMissile(player->mo, MT_TELOTHER_FX1); if (mo) { mo->target = player->mo; } } void P_TeleportToPlayerStarts(mobj_t *victim) { int i,selections=0; fixed_t destX,destY; angle_t destAngle; for (i=0;iplayer) { if (deathmatch) P_TeleportToDeathmatchStarts(victim); else P_TeleportToPlayerStarts(victim); } else { // If death action, run it upon teleport if (victim->flags&MF_COUNTKILL && victim->special) { P_RemoveMobjFromTIDList(victim); P_ExecuteLineSpecial(victim->special, victim->args, NULL, 0, victim); victim->special = 0; } // Send all monsters to deathmatch spots P_TeleportToDeathmatchStarts(victim); } } #define BLAST_RADIUS_DIST 255*FRACUNIT #define BLAST_SPEED 20*FRACUNIT #define BLAST_FULLSTRENGTH 255 void ResetBlasted(mobj_t *mo) { mo->flags2 &= ~MF2_BLASTED; if (!(mo->flags&MF_ICECORPSE)) { mo->flags2 &= ~MF2_SLIDE; } } void P_BlastMobj(mobj_t *source, mobj_t *victim, fixed_t strength) { angle_t angle,ang; mobj_t *mo; fixed_t x,y,z; angle = R_PointToAngle2(source->x, source->y, victim->x, victim->y); angle >>= ANGLETOFINESHIFT; if (strength < BLAST_FULLSTRENGTH) { victim->momx = FixedMul(strength, finecosine[angle]); victim->momy = FixedMul(strength, finesine[angle]); if (victim->player) { // Players handled automatically } else { victim->flags2 |= MF2_SLIDE; victim->flags2 |= MF2_BLASTED; } } else // full strength blast from artifact { if (victim->flags&MF_MISSILE) { switch(victim->type) { case MT_SORCBALL1: // don't blast sorcerer balls case MT_SORCBALL2: case MT_SORCBALL3: return; break; case MT_MSTAFF_FX2: // Reflect to originator victim->special1 = (int)victim->target; victim->target = source; break; default: break; } } if (victim->type == MT_HOLY_FX) { if ((mobj_t *)(victim->special1) == source) { victim->special1 = (int)victim->target; victim->target = source; } } victim->momx = FixedMul(BLAST_SPEED, finecosine[angle]); victim->momy = FixedMul(BLAST_SPEED, finesine[angle]); // Spawn blast puff ang = R_PointToAngle2(victim->x, victim->y, source->x, source->y); ang >>= ANGLETOFINESHIFT; x = victim->x + FixedMul(victim->radius+FRACUNIT, finecosine[ang]); y = victim->y + FixedMul(victim->radius+FRACUNIT, finesine[ang]); z = victim->z - victim->floorclip + (victim->height>>1); mo=P_SpawnMobj(x, y, z, MT_BLASTEFFECT); if (mo) { mo->momx = victim->momx; mo->momy = victim->momy; } if (victim->flags&MF_MISSILE) { victim->momz = 8*FRACUNIT; mo->momz = victim->momz; } else { victim->momz = (1000/victim->info->mass)<player) { // Players handled automatically } else { victim->flags2 |= MF2_SLIDE; victim->flags2 |= MF2_BLASTED; } } } // Blast all mobj things away void P_BlastRadius(player_t *player) { mobj_t *mo; mobj_t *pmo=player->mo; thinker_t *think; fixed_t dist; S_StartSound(pmo, SFX_ARTIFACT_BLAST); P_NoiseAlert(player->mo, player->mo); for(think = thinkercap.next; think != &thinkercap; think = think->next) { if(think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *)think; if((mo == pmo) || (mo->flags2&MF2_BOSS)) { // Not a valid monster continue; } if ((mo->type == MT_POISONCLOUD) || // poison cloud (mo->type == MT_HOLY_FX) || // holy fx (mo->flags&MF_ICECORPSE)) // frozen corpse { // Let these special cases go } else if ((mo->flags&MF_COUNTKILL) && (mo->health <= 0)) { continue; } else if (!(mo->flags&MF_COUNTKILL) && !(mo->player) && !(mo->flags&MF_MISSILE)) { // Must be monster, player, or missile continue; } if (mo->flags2&MF2_DORMANT) { continue; // no dormant creatures } if ((mo->type == MT_WRAITHB) && (mo->flags2&MF2_DONTDRAW)) { continue; // no underground wraiths } if ((mo->type == MT_SPLASHBASE) || (mo->type == MT_SPLASH)) { continue; } if(mo->type == MT_SERPENT || mo->type == MT_SERPENTLEADER) { continue; } dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y); if(dist > BLAST_RADIUS_DIST) { // Out of range continue; } P_BlastMobj(pmo, mo, BLAST_FULLSTRENGTH); } } #define HEAL_RADIUS_DIST 255*FRACUNIT // Do class specific effect for everyone in radius boolean P_HealRadius(player_t *player) { mobj_t *mo; mobj_t *pmo=player->mo; thinker_t *think; fixed_t dist; int effective=false; int amount; for(think = thinkercap.next; think != &thinkercap; think = think->next) { if(think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *)think; if (!mo->player) continue; if (mo->health <= 0) continue; dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y); if(dist > HEAL_RADIUS_DIST) { // Out of range continue; } switch(player->class) { case PCLASS_FIGHTER: // Radius armor boost if ((P_GiveArmor(mo->player, ARMOR_ARMOR, 1)) || (P_GiveArmor(mo->player, ARMOR_SHIELD, 1)) || (P_GiveArmor(mo->player, ARMOR_HELMET, 1)) || (P_GiveArmor(mo->player, ARMOR_AMULET, 1))) { effective=true; S_StartSound(mo, SFX_MYSTICINCANT); } break; case PCLASS_CLERIC: // Radius heal amount = 50 + (P_Random()%50); if (P_GiveBody(mo->player, amount)) { effective=true; S_StartSound(mo, SFX_MYSTICINCANT); } break; case PCLASS_MAGE: // Radius mana boost amount = 50 + (P_Random()%50); if ((P_GiveMana(mo->player, MANA_1, amount)) || (P_GiveMana(mo->player, MANA_2, amount))) { effective=true; S_StartSound(mo, SFX_MYSTICINCANT); } break; case PCLASS_PIG: default: break; } } return(effective); } //---------------------------------------------------------------------------- // // PROC P_PlayerNextArtifact // //---------------------------------------------------------------------------- void P_PlayerNextArtifact(player_t *player) { extern int inv_ptr; extern int curpos; if(player == &players[consoleplayer]) { inv_ptr--; if(inv_ptr < 6) { curpos--; if(curpos < 0) { curpos = 0; } } if(inv_ptr < 0) { inv_ptr = player->inventorySlotNum-1; if(inv_ptr < 6) { curpos = inv_ptr; } else { curpos = 6; } } player->readyArtifact = player->inventory[inv_ptr].type; } } //---------------------------------------------------------------------------- // // PROC P_PlayerRemoveArtifact // //---------------------------------------------------------------------------- void P_PlayerRemoveArtifact(player_t *player, int slot) { int i; extern int inv_ptr; extern int curpos; player->artifactCount--; if(!(--player->inventory[slot].count)) { // Used last of a type - compact the artifact list player->readyArtifact = arti_none; player->inventory[slot].type = arti_none; for(i = slot+1; i < player->inventorySlotNum; i++) { player->inventory[i-1] = player->inventory[i]; } player->inventorySlotNum--; if(player == &players[consoleplayer]) { // Set position markers and get next readyArtifact inv_ptr--; if(inv_ptr < 6) { curpos--; if(curpos < 0) { curpos = 0; } } if(inv_ptr >= player->inventorySlotNum) { inv_ptr = player->inventorySlotNum-1; } if(inv_ptr < 0) { inv_ptr = 0; } player->readyArtifact = player->inventory[inv_ptr].type; } } } //---------------------------------------------------------------------------- // // PROC P_PlayerUseArtifact // //---------------------------------------------------------------------------- void P_PlayerUseArtifact(player_t *player, artitype_t arti) { int i; for(i = 0; i < player->inventorySlotNum; i++) { if(player->inventory[i].type == arti) { // Found match - try to use if(P_UseArtifact(player, arti)) { // Artifact was used - remove it from inventory P_PlayerRemoveArtifact(player, i); if(player == &players[consoleplayer]) { if(arti < arti_firstpuzzitem) { S_StartSound(NULL, SFX_ARTIFACT_USE); } else { S_StartSound(NULL, SFX_PUZZLE_SUCCESS); } ArtifactFlash = 4; } } else if(arti < arti_firstpuzzitem) { // Unable to use artifact, advance pointer P_PlayerNextArtifact(player); } break; } } } //========================================================================== // // P_UseArtifact // // Returns true if the artifact was used. // //========================================================================== boolean P_UseArtifact(player_t *player, artitype_t arti) { mobj_t *mo; angle_t angle; int i; int count; switch(arti) { case arti_invulnerability: if(!P_GivePower(player, pw_invulnerability)) { return(false); } break; case arti_health: if(!P_GiveBody(player, 25)) { return(false); } break; case arti_superhealth: if(!P_GiveBody(player, 100)) { return(false); } break; case arti_healingradius: if (!P_HealRadius(player)) { return(false); } break; case arti_torch: if(!P_GivePower(player, pw_infrared)) { return(false); } break; case arti_egg: mo = player->mo; P_SpawnPlayerMissile(mo, MT_EGGFX); P_SPMAngle(mo, MT_EGGFX, mo->angle-(ANG45/6)); P_SPMAngle(mo, MT_EGGFX, mo->angle+(ANG45/6)); P_SPMAngle(mo, MT_EGGFX, mo->angle-(ANG45/3)); P_SPMAngle(mo, MT_EGGFX, mo->angle+(ANG45/3)); break; case arti_fly: if(!P_GivePower(player, pw_flight)) { return(false); } if(player->mo->momz <= -35*FRACUNIT) { // stop falling scream S_StopSound(player->mo); } break; case arti_summon: mo = P_SpawnPlayerMissile(player->mo, MT_SUMMON_FX); if (mo) { mo->target = player->mo; mo->special1 = (int)(player->mo); mo->momz = 5*FRACUNIT; } break; case arti_teleport: P_ArtiTele(player); break; case arti_teleportother: P_ArtiTeleportOther(player); break; case arti_poisonbag: angle = player->mo->angle>>ANGLETOFINESHIFT; if(player->class == PCLASS_CLERIC) { mo = P_SpawnMobj(player->mo->x+16*finecosine[angle], player->mo->y+24*finesine[angle], player->mo->z- player->mo->floorclip+8*FRACUNIT, MT_POISONBAG); if(mo) { mo->target = player->mo; } } else if(player->class == PCLASS_MAGE) { mo = P_SpawnMobj(player->mo->x+16*finecosine[angle], player->mo->y+24*finesine[angle], player->mo->z- player->mo->floorclip+8*FRACUNIT, MT_FIREBOMB); if(mo) { mo->target = player->mo; } } else // PCLASS_FIGHTER, obviously (also pig, not so obviously) { 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); } } break; case arti_speed: if(!P_GivePower(player, pw_speed)) { return(false); } break; case arti_boostmana: if(!P_GiveMana(player, MANA_1, MAX_MANA)) { if(!P_GiveMana(player, MANA_2, MAX_MANA)) { return false; } } else { P_GiveMana(player, MANA_2, MAX_MANA); } break; case arti_boostarmor: count = 0; for(i = 0; i < NUMARMOR; i++) { count += P_GiveArmor(player, i, 1); // 1 point per armor type } if(!count) { return false; } break; case arti_blastradius: P_BlastRadius(player); break; case arti_puzzskull: case arti_puzzgembig: case arti_puzzgemred: case arti_puzzgemgreen1: case arti_puzzgemgreen2: case arti_puzzgemblue1: case arti_puzzgemblue2: case arti_puzzbook1: case arti_puzzbook2: case arti_puzzskull2: case arti_puzzfweapon: case arti_puzzcweapon: case arti_puzzmweapon: case arti_puzzgear1: case arti_puzzgear2: case arti_puzzgear3: case arti_puzzgear4: if(P_UsePuzzleItem(player, arti-arti_firstpuzzitem)) { return true; } else { P_SetYellowMessage(player, TXT_USEPUZZLEFAILED, false); return false; } break; default: return false; } return true; } //============================================================================ // // A_SpeedFade // //============================================================================ void A_SpeedFade(mobj_t *actor) { actor->flags |= MF_SHADOW; actor->flags &= ~MF_ALTSHADOW; actor->sprite = actor->target->sprite; }