DEFINE_ACTION_FUNCTION(AActor, A_LightningClip) { PARAM_ACTION_PROLOGUE; AActor *cMo; AActor *target = NULL; int zigZag; if (self->flags3 & MF3_FLOORHUGGER) { if (self->lastenemy == NULL) { return 0; } self->SetZ(self->floorz); target = self->lastenemy->tracer; } else if (self->flags3 & MF3_CEILINGHUGGER) { self->SetZ(self->ceilingz - self->Height); target = self->tracer; } if (self->flags3 & MF3_FLOORHUGGER) { // floor lightning zig-zags, and forces the ceiling lightning to mimic cMo = self->lastenemy; zigZag = pr_lightningclip(); if((zigZag > 128 && self->special1 < 2) || self->special1 < -2) { self->Thrust(self->Angles.Yaw + 90, ZAGSPEED); if(cMo) { cMo->Thrust(self->Angles.Yaw + 90, ZAGSPEED); } self->special1++; } else { self->Thrust(self->Angles.Yaw - 90, ZAGSPEED); if(cMo) { cMo->Thrust(self->Angles.Yaw - 90, ZAGSPEED); } self->special1--; } } if(target) { if(target->health <= 0) { P_ExplodeMissile(self, NULL, NULL); } else { self->Angles.Yaw = self->AngleTo(target); self->VelFromAngle(self->Speed / 2); } } return 0; }
void P_XYMovement (mobj_t* mo) { fixed_t ptryx; fixed_t ptryy; player_t* player; fixed_t xmove; fixed_t ymove; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mo->info->spawnstate); } return; } player = mo->player; if (mo->momx > MAXMOVE) mo->momx = MAXMOVE; else if (mo->momx < -MAXMOVE) mo->momx = -MAXMOVE; if (mo->momy > MAXMOVE) mo->momy = MAXMOVE; else if (mo->momy < -MAXMOVE) mo->momy = -MAXMOVE; xmove = mo->momx; ymove = mo->momy; do { if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove (mo, ptryx, ptryy)) { // blocked move if (mo->player) { // try to slide along it P_SlideMove (mo); } else if (mo->flags & MF_MISSILE) { // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. P_RemoveMobj (mo); return; } P_ExplodeMissile (mo); } else mo->momx = mo->momy = 0; } } while (xmove || ymove);
void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) { // Handle possible unmorph on death bool wasgibbed = (health < GibHealth()); AActor *realthis = NULL; int realstyle = 0; int realhealth = 0; if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth)) { if (!(realstyle & MORPH_UNDOBYDEATHSAVES)) { if (wasgibbed) { int realgibhealth = realthis->GibHealth(); if (realthis->health >= realgibhealth) { realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l } } realthis->Die(source, inflictor, dmgflags); } return; } // [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) effects &= ~FX_RESPAWNINVUL; //flags &= ~MF_INVINCIBLE; if (debugfile && this->player) { static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger... int pnum = int(this->player-players); dieticks[pnum] = gametic; fprintf (debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, this->player->cheats&CF_PREDICTING?"predicting":"real"); } // [RH] Notify this actor's items. for (AInventory *item = Inventory; item != NULL; ) { AInventory *next = item->Inventory; item->OwnerDied(); item = next; } if (flags & MF_MISSILE) { // [RH] When missiles die, they just explode P_ExplodeMissile (this, NULL, NULL); return; } // [RH] Set the target to the thing that killed it. Strife apparently does this. if (source != NULL) { target = source; } flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); if (!(flags4 & MF4_DONTFALL)) flags&=~MF_NOGRAVITY; flags |= MF_DROPOFF; if ((flags3 & MF3_ISMONSTER) || FindState(NAME_Raise) != NULL || IsKindOf(RUNTIME_CLASS(APlayerPawn))) { // [RH] Only monsters get to be corpses. // Objects with a raise state should get the flag as well so they can // be revived by an Arch-Vile. Batman Doom needs this. // [RC] And disable this if DONTCORPSE is set, of course. if(!(flags6 & MF6_DONTCORPSE)) flags |= MF_CORPSE; } flags6 |= MF6_KILLED; // [RH] Allow the death height to be overridden using metadata. fixed_t metaheight = 0; if (DamageType == NAME_Fire) { metaheight = GetClass()->Meta.GetMetaFixed (AMETA_BurnHeight); } if (metaheight == 0) { metaheight = GetClass()->Meta.GetMetaFixed (AMETA_DeathHeight); } if (metaheight != 0) { height = MAX<fixed_t> (metaheight, 0); } else { height >>= 2; } // [RH] If the thing has a special, execute and remove it // Note that the thing that killed it is considered // the activator of the script. // New: In Hexen, the thing that died is the activator, // so now a level flag selects who the activator gets to be. // Everything is now moved to P_ActivateThingSpecial(). if (special && (!(flags & MF_SPECIAL) || (flags3 & MF3_ISMONSTER)) && !(activationtype & THINGSPEC_NoDeathSpecial)) { P_ActivateThingSpecial(this, source, true); } if (CountsAsKill()) level.killed_monsters++; if (source && source->player) { if (CountsAsKill()) { // count for intermission source->player->killcount++; } // Don't count any frags at level start, because they're just telefrags // resulting from insufficient deathmatch starts, and it wouldn't be // fair to count them toward a player's score. if (player && level.maptime) { source->player->frags[player - players]++; if (player == source->player) // [RH] Cumulative frag count { char buff[256]; player->fragcount--; if (deathmatch && player->spreecount >= 5 && cl_showsprees) { SexMessage (GStrings("SPREEKILLSELF"), buff, player->userinfo.GetGender(), player->userinfo.GetName(), player->userinfo.GetName()); StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, buff, 1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R')); } } else { if ((dmflags2 & DF2_YES_LOSEFRAG) && deathmatch) player->fragcount--; ++source->player->fragcount; ++source->player->spreecount; if (source->player->morphTics) { // Make a super chicken source->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); } if (deathmatch && cl_showsprees) { const char *spreemsg; char buff[256]; switch (source->player->spreecount) { case 5: spreemsg = GStrings("SPREE5"); break; case 10: spreemsg = GStrings("SPREE10"); break; case 15: spreemsg = GStrings("SPREE15"); break; case 20: spreemsg = GStrings("SPREE20"); break; case 25: spreemsg = GStrings("SPREE25"); break; default: spreemsg = NULL; break; } if (spreemsg == NULL && player->spreecount >= 5) { if (!AnnounceSpreeLoss (this)) { SexMessage (GStrings("SPREEOVER"), buff, player->userinfo.GetGender(), player->userinfo.GetName(), source->player->userinfo.GetName()); StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, buff, 1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R')); } } else if (spreemsg != NULL) { if (!AnnounceSpree (source)) { SexMessage (spreemsg, buff, player->userinfo.GetGender(), player->userinfo.GetName(), source->player->userinfo.GetName()); StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, buff, 1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R')); } } } } // [RH] Multikills if (player != source->player) { source->player->multicount++; if (source->player->lastkilltime > 0) { if (source->player->lastkilltime < level.time - 3*TICRATE) { source->player->multicount = 1; } if (deathmatch && source->CheckLocalView (consoleplayer) && cl_showmultikills) { const char *multimsg; switch (source->player->multicount) { case 1: multimsg = NULL; break; case 2: multimsg = GStrings("MULTI2"); break; case 3: multimsg = GStrings("MULTI3"); break; case 4: multimsg = GStrings("MULTI4"); break; default: multimsg = GStrings("MULTI5"); break; } if (multimsg != NULL) { char buff[256]; if (!AnnounceMultikill (source)) { SexMessage (multimsg, buff, player->userinfo.GetGender(), player->userinfo.GetName(), source->player->userinfo.GetName()); StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, buff, 1.5f, 0.8f, 0, 0, CR_RED, 3.f, 0.5f), MAKE_ID('M','K','I','L')); } } } } source->player->lastkilltime = level.time; } // [RH] Implement fraglimit if (deathmatch && fraglimit && fraglimit <= D_GetFragCount (source->player)) { Printf ("%s\n", GStrings("TXT_FRAGLIMIT")); G_ExitLevel (0, false); } } } else if (!multiplayer && CountsAsKill()) { // count all monster deaths, // even those caused by other monsters players[0].killcount++; } if (player) { // [RH] Death messages ClientObituary (this, inflictor, source, dmgflags); // Death script execution, care of Skull Tag FBehavior::StaticStartTypedScripts (SCRIPT_Death, this, true); // [RH] Force a delay between death and respawn player->respawn_time = level.time + TICRATE; //Added by MC: Respawn bots if (bglobal.botnum && !demoplayback) { if (player->Bot != NULL) player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; //Added by MC: Discard enemies. for (int i = 0; i < MAXPLAYERS; i++) { if (players[i].Bot != NULL && this == players[i].Bot->enemy) { if (players[i].Bot->dest == players[i].Bot->enemy) players[i].Bot->dest = NULL; players[i].Bot->enemy = NULL; } } player->spreecount = 0; player->multicount = 0; } // count environment kills against you if (!source) { player->frags[player - players]++; player->fragcount--; // [RH] Cumulative frag count } flags &= ~MF_SOLID; player->playerstate = PST_DEAD; P_DropWeapon (player); if (this == players[consoleplayer].camera && automapactive) { // don't die in auto map, switch view prior to dying AM_Stop (); } // [GRB] Clear extralight. When you killed yourself with weapon that // called A_Light1/2 before it called A_Light0, extraligh remained. player->extralight = 0; } // [RH] If this is the unmorphed version of another monster, destroy this // actor, because the morphed version is the one that will stick around in // the level. if (flags & MF_UNMORPHED) { Destroy (); return; } FState *diestate = NULL; int gibhealth = GibHealth(); ActorFlags4 iflags4 = inflictor == NULL ? ActorFlags4::FromInt(0) : inflictor->flags4; bool extremelydead = ((health < gibhealth || iflags4 & MF4_EXTREMEDEATH) && !(iflags4 & MF4_NOEXTREMEDEATH)); // Special check for 'extreme' damage type to ensure that it gets recorded properly as an extreme death for subsequent checks. if (DamageType == NAME_Extreme) { extremelydead = true; DamageType = NAME_None; } // find the appropriate death state. The order is: // // 1. If damagetype is not 'none' and death is extreme, try a damage type specific extreme death state // 2. If no such state is found or death is not extreme try a damage type specific normal death state // 3. If damagetype is 'ice' and actor is a monster or player, try the generic freeze death (unless prohibited) // 4. If no state has been found and death is extreme, try the extreme death state // 5. If no such state is found or death is not extreme try the regular death state. // 6. If still no state has been found, destroy the actor immediately. if (DamageType != NAME_None) { if (extremelydead) { FName labels[] = { NAME_Death, NAME_Extreme, DamageType }; diestate = FindState(3, labels, true); } if (diestate == NULL) { diestate = FindState (NAME_Death, DamageType, true); if (diestate != NULL) extremelydead = false; } if (diestate == NULL) { if (DamageType == NAME_Ice) { // If an actor doesn't have an ice death, we can still give them a generic one. if (!deh.NoAutofreeze && !(flags4 & MF4_NOICEDEATH) && (player || (flags3 & MF3_ISMONSTER))) { diestate = FindState(NAME_GenericFreezeDeath); extremelydead = false; } } } } if (diestate == NULL) { // Don't pass on a damage type this actor cannot handle. // (most importantly, prevent barrels from passing on ice damage.) // Massacre must be preserved though. if (DamageType != NAME_Massacre) { DamageType = NAME_None; } if (extremelydead) { // Extreme death diestate = FindState (NAME_Death, NAME_Extreme, true); } if (diestate == NULL) { // Normal death extremelydead = false; diestate = FindState (NAME_Death); } } if (extremelydead) { // We'll only get here if an actual extreme death state was used. // For players, mark the appropriate flag. if (player != NULL) { player->cheats |= CF_EXTREMELYDEAD; } // If a non-player, mark as extremely dead for the crash state. else if (health >= gibhealth) { health = gibhealth - 1; } } if (diestate != NULL) { SetState (diestate); if (tics > 1) { tics -= pr_killmobj() & 3; if (tics < 1) tics = 1; } } else { Destroy(); } }
// // P_XYMovement // void P_XYMovement (AActor *mo) { // angle_t angle; fixed_t ptryx, ptryy; player_t *player = NULL; fixed_t xmove, ymove; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mo->info->spawnstate); } return; } player = mo->player; if(!player || !player->mo) player = NULL; int maxmove = (mo->waterlevel < 2) || (mo->flags & MF_MISSILE) ? MAXMOVE : MAXMOVE/4; if (mo->momx > maxmove) mo->momx = maxmove; else if (mo->momx < -maxmove) mo->momx = -maxmove; if (mo->momy > maxmove) mo->momy = maxmove; else if (mo->momy < -maxmove) mo->momy = -maxmove; xmove = mo->momx; ymove = mo->momy; maxmove /= 2; do { if (xmove > maxmove || ymove > maxmove ) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } // killough 3/15/98: Allow objects to drop off if (!P_TryMove (mo, ptryx, ptryy, true)) { // blocked move if (mo->player) { // try to slide along it P_SlideMove (mo); } else if (mo->flags & MF_MISSILE) { // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. mo->Destroy (); return; } P_ExplodeMissile (mo); } else { mo->momx = mo->momy = 0; } } } while (xmove || ymove);
void P_XYMovement(mobj_t *mo) { player_t *player; fixed_t xmove, ymove; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState(mo, (statenum_t)mo->info->spawnstate); } return; } player = mo->player; if (mo->type == MT_ROCKET) { if (puffcount++ > 1) P_SpawnPuff(mo->x, mo->y, mo->z, mo->angle); } if (mo->momx > MAXMOVE) mo->momx = MAXMOVE; else if (mo->momx < -MAXMOVE) mo->momx = -MAXMOVE; if (mo->momy > MAXMOVE) mo->momy = MAXMOVE; else if (mo->momy < -MAXMOVE) mo->momy = -MAXMOVE; xmove = mo->momx; ymove = mo->momy; do { fixed_t ptryx, ptryy; if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2 || xmove < -MAXMOVE / 2 || ymove < -MAXMOVE / 2) { ptryx = mo->x + xmove / 2; ptryy = mo->y + ymove / 2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove(mo, ptryx, ptryy)) { // blocked move if (mo->player) { // try to slide along it P_SlideMove(mo); } else if (mo->flags & MF_MISSILE) { // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum && mo->z > ceilingline->backsector->ceilingheight) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. shootingsky = true; P_RemoveMobj(mo); return; } P_ExplodeMissile(mo); } else { mo->momx = mo->momy = 0; } } }
// // PIT_CheckThing // boolean PIT_CheckThing (mobj_t* thing) { fixed_t newdist; fixed_t olddist; fixed_t blockdist; int flags; int damage; int tradius; flags = thing->flags; if (!(flags & (MF_SOLID|MF_SPECIAL|MF_MISSILE|MF_SHOOTABLE) )) return true; /* We've changed all of the MF_SPECIAL stuff to their */ /* actual radius rather than all being 20*FRACUNIT but */ /* we want the original size back again when picking up. */ // if (flags & MF_SPECIAL) tradius = thing->info->pickupradius; // else // tradius = thing->radius; blockdist = tradius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; if (flags & MF_MISSILE) { if (!tmthing->player) P_ExplodeMissile (thing); if (!(flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )) return (true); } // check if a mobj passed over/under another object if (tmthing->flags2 & MF2_PASSMOBJ) { if (tmthing->z >= thing->z + thing->height) return true; // over thing else if (tmthing->z + tmthing->height <= thing->z) return true; // under thing } // check for skulls slamming into things if (tmthing->flags & MF_SKULLFLY) { damage = ((P_Random()%8)+1)*tmthing->info->damage; P_DamageMobj (thing, tmthing, tmthing, damage); tmthing->flags &= ~MF_SKULLFLY; tmthing->momx = tmthing->momy = tmthing->momz = 0; P_SetMobjState (tmthing, (statenum_t) tmthing->info->spawnstate); return false; // stop moving } // missiles can hit other things if (tmthing->flags & MF_MISSILE) { // see if it went over / under if (tmthing->z > thing->z + thing->height) return true; // overhead if (tmthing->z+tmthing->height < thing->z) return true; // underneath if (tmthing->target && ( tmthing->target->type == thing->type || (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)|| (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) ) { // Don't hit same species as originator. if (thing == tmthing->target) return true; if ((thing->type != MT_PLAYER) && (Monsters_Infight1 == false) && (Monsters_Infight2 == false)) { // Explode, but do no damage. // Let players missile other players. return false; } } if (! (flags & MF_SHOOTABLE) ) { // didn't do any damage return (boolean)!(flags & MF_SOLID); } // damage / explode damage = ((P_Random()%8)+1)*tmthing->info->damage; P_DamageMobj (thing, tmthing, tmthing->target, damage); // don't traverse any more return false; } // check for special pickup if (flags & MF_SPECIAL) { if (tmflags & MF_PICKUP) { // can remove thing P_TouchSpecialThing (thing, tmthing); } return (boolean)!(flags & MF_SOLID); } // [BH] don't hit if either thing is a corpse, which may still be solid if // they are still going through their death sequence. if ((flags & MF_CORPSE) || (tmthing->flags & MF_CORPSE)) return true; // check if things are stuck and allow move if it makes them further apart if ((tmx == tmthing->x) && (tmy == tmthing->y)) return true; newdist = P_ApproxDistance(thing->x - tmx, thing->y - tmy); olddist = P_ApproxDistance(thing->x - tmthing->x, thing->y - tmthing->y); if ((newdist > olddist) && (!((tmthing->z >= thing->z + thing->height) || (tmthing->z + tmthing->height <= thing->z)))) return true; // killough 3/16/98: Allow non-solid moving objects to move through solid // ones, by allowing the moving thing (tmthing) to move if it's non-solid, // despite another solid thing being in the way. // killough 4/11/98: Treat no-clipping things as not blocking return (boolean)!((flags & MF_SOLID) && !(flags & MF_NOCLIP) && (tmthing->flags & MF_SOLID)); }
void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) { // Handle possible unmorph on death bool wasgibbed = (health < GetGibHealth()); AActor *realthis = NULL; int realstyle = 0; int realhealth = 0; if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth)) { if (!(realstyle & MORPH_UNDOBYDEATHSAVES)) { if (wasgibbed) { int realgibhealth = realthis->GetGibHealth(); if (realthis->health >= realgibhealth) { realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l } } realthis->Die(source, inflictor, dmgflags); } return; } // [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) effects &= ~FX_RESPAWNINVUL; //flags &= ~MF_INVINCIBLE; if (debugfile && this->player) { static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger... int pnum = int(this->player-players); dieticks[pnum] = gametic; fprintf (debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, this->player->cheats&CF_PREDICTING?"predicting":"real"); } // [RH] Notify this actor's items. for (AInventory *item = Inventory; item != NULL; ) { AInventory *next = item->Inventory; item->OwnerDied(); item = next; } if (flags & MF_MISSILE) { // [RH] When missiles die, they just explode P_ExplodeMissile (this, NULL, NULL); return; } // [RH] Set the target to the thing that killed it. Strife apparently does this. if (source != NULL) { target = source; } flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); if (!(flags4 & MF4_DONTFALL)) flags&=~MF_NOGRAVITY; flags |= MF_DROPOFF; if ((flags3 & MF3_ISMONSTER) || FindState(NAME_Raise) != NULL || IsKindOf(RUNTIME_CLASS(APlayerPawn))) { // [RH] Only monsters get to be corpses. // Objects with a raise state should get the flag as well so they can // be revived by an Arch-Vile. Batman Doom needs this. // [RC] And disable this if DONTCORPSE is set, of course. if(!(flags6 & MF6_DONTCORPSE)) flags |= MF_CORPSE; } flags6 |= MF6_KILLED; // [RH] Allow the death height to be overridden using metadata. fixed_t metaheight = -1; if (DamageType == NAME_Fire) { metaheight = GetClass()->BurnHeight; } if (metaheight < 0) { metaheight = GetClass()->DeathHeight; } if (metaheight < 0) { height >>= 2; }
void AFastProjectile::Tick () { int i; DVector3 frac; int changexy; ClearInterpolation(); double oldz = Z(); if (!(flags5 & MF5_NOTIMEFREEZE)) { //Added by MC: Freeze mode. if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN) { return; } } // [RH] Ripping is a little different than it was in Hexen FCheckPosition tm(!!(flags2 & MF2_RIP)); int count = 8; if (radius > 0) { while ( fabs(Vel.X) > radius * count || fabs(Vel.Y) > radius * count) { // we need to take smaller steps. count += count; } } // Handle movement if (!Vel.isZero() || (Z() != floorz)) { // force some lateral movement so that collision detection works as intended. if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage()) { Vel.X = MinVel; } frac = Vel / count; changexy = frac.X != 0 || frac.Y != 0; int ripcount = count / 8; for (i = 0; i < count; i++) { if (changexy) { if (--ripcount <= 0) { tm.LastRipped.Clear(); // [RH] Do rip damage each step, like Hexen } if (!P_TryMove (this, Pos() + frac, true, NULL, tm)) { // Blocked move if (!(flags3 & MF3_SKYEXPLODE)) { if (tm.ceilingline && tm.ceilingline->backsector && tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(PosRelative(tm.ceilingline))) { // Hack to prevent missiles exploding against the sky. // Does not handle sky floors. Destroy (); return; } // [RH] Don't explode on horizon lines. if (BlockingLine != NULL && BlockingLine->special == Line_Horizon) { Destroy (); return; } } P_ExplodeMissile (this, BlockingLine, BlockingMobj); return; } } AddZ(frac.Z); UpdateWaterLevel (); oldz = Z(); if (oldz <= floorz) { // Hit the floor if (floorpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE)) { // [RH] Just remove the missile without exploding it // if this is a sky floor. Destroy (); return; } SetZ(floorz); P_HitFloor (this); P_ExplodeMissile (this, NULL, NULL); return; } if (Top() > ceilingz) { // Hit the ceiling if (ceilingpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE)) { Destroy (); return; } SetZ(ceilingz - Height); P_ExplodeMissile (this, NULL, NULL); return; } if (!frac.isZero() && ripcount <= 0) { ripcount = count >> 3; Effect(); } } }
void AActor::Die (AActor *source, AActor *inflictor) { // [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) effects &= ~FX_RESPAWNINVUL; //flags &= ~MF_INVINCIBLE; if (debugfile && this->player) { static int dieticks[MAXPLAYERS]; int pnum = this->player-players; if (dieticks[pnum] == gametic) gametic=gametic; dieticks[pnum] = gametic; fprintf (debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, this->player->cheats&CF_PREDICTING?"predicting":"real"); } if (flags & MF_MISSILE) { // [RH] When missiles die, they just explode P_ExplodeMissile (this, NULL); return; } // [RH] Set the target to the thing that killed it. Strife apparently does this. if (source != NULL) { target = source; } flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); if (!(flags4 & MF4_DONTFALL)) flags&=~MF_NOGRAVITY; flags |= MF_DROPOFF; if (flags3 & MF3_ISMONSTER) { // [RH] Only monsters get to be corpses. flags |= MF_CORPSE; } // [RH] Allow the death height to be overridden using metadata. fixed_t metaheight = 0; if (DamageType == MOD_FIRE) { metaheight = GetClass()->Meta.GetMetaFixed (AMETA_BurnHeight); } if (metaheight == 0) { metaheight = GetClass()->Meta.GetMetaFixed (AMETA_DeathHeight); } if (metaheight != 0) { height = MAX<fixed_t> (metaheight, 0); } else { height >>= 2; } // [RH] If the thing has a special, execute and remove it // Note that the thing that killed it is considered // the activator of the script. // New: In Hexen, the thing that died is the activator, // so now a level flag selects who the activator gets to be. if (special && (!(flags & MF_SPECIAL) || (flags3 & MF3_ISMONSTER))) { LineSpecials[special] (NULL, level.flags & LEVEL_ACTOWNSPECIAL ? this : source, false, args[0], args[1], args[2], args[3], args[4]); special = 0; } if (source && source->player) { if (flags & MF_COUNTKILL) { // count for intermission source->player->killcount++; level.killed_monsters++; } // Don't count any frags at level start, because they're just telefrags // resulting from insufficient deathmatch starts, and it wouldn't be // fair to count them toward a player's score. if (player && level.time) { source->player->frags[player - players]++; if (player == source->player) // [RH] Cumulative frag count { char buff[256]; player->fragcount--; if (deathmatch && player->spreecount >= 5 && cl_showsprees) { SexMessage (GStrings("SPREEKILLSELF"), buff, player->userinfo.gender, player->userinfo.netname, player->userinfo.netname); StatusBar->AttachMessage (new DHUDMessageFadeOut (buff, 1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R')); } } else { ++source->player->fragcount; ++source->player->spreecount; if (source->player->morphTics) { // Make a super chicken source->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); } if (deathmatch && cl_showsprees) { const char *spreemsg; char buff[256]; switch (source->player->spreecount) { case 5: spreemsg = GStrings("SPREE5"); break; case 10: spreemsg = GStrings("SPREE10"); break; case 15: spreemsg = GStrings("SPREE15"); break; case 20: spreemsg = GStrings("SPREE20"); break; case 25: spreemsg = GStrings("SPREE25"); break; default: spreemsg = NULL; break; } if (spreemsg == NULL && player->spreecount >= 5) { if (!AnnounceSpreeLoss (this)) { SexMessage (GStrings("SPREEOVER"), buff, player->userinfo.gender, player->userinfo.netname, source->player->userinfo.netname); StatusBar->AttachMessage (new DHUDMessageFadeOut (buff, 1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R')); } } else if (spreemsg != NULL) { if (!AnnounceSpree (source)) { SexMessage (spreemsg, buff, player->userinfo.gender, player->userinfo.netname, source->player->userinfo.netname); StatusBar->AttachMessage (new DHUDMessageFadeOut (buff, 1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R')); } } } } // [RH] Multikills source->player->multicount++; if (source->player->lastkilltime > 0) { if (source->player->lastkilltime < level.time - 3*TICRATE) { source->player->multicount = 1; } if (deathmatch && source->CheckLocalView (consoleplayer) && cl_showmultikills) { const char *multimsg; switch (source->player->multicount) { case 1: multimsg = NULL; break; case 2: multimsg = GStrings("MULTI2"); break; case 3: multimsg = GStrings("MULTI3"); break; case 4: multimsg = GStrings("MULTI4"); break; default: multimsg = GStrings("MULTI5"); break; } if (multimsg != NULL) { char buff[256]; if (!AnnounceMultikill (source)) { SexMessage (multimsg, buff, player->userinfo.gender, player->userinfo.netname, source->player->userinfo.netname); StatusBar->AttachMessage (new DHUDMessageFadeOut (buff, 1.5f, 0.8f, 0, 0, CR_RED, 3.f, 0.5f), MAKE_ID('M','K','I','L')); } } } } source->player->lastkilltime = level.time; // [RH] Implement fraglimit if (deathmatch && fraglimit && fraglimit == D_GetFragCount (source->player)) { Printf ("%s\n", GStrings("TXT_FRAGLIMIT")); G_ExitLevel (0, false); } } } else if (!multiplayer && (flags & MF_COUNTKILL)) { // count all monster deaths, // even those caused by other monsters players[0].killcount++; level.killed_monsters++; } if (player) { // [RH] Death messages ClientObituary (this, inflictor, source); // Death script execution, care of Skull Tag FBehavior::StaticStartTypedScripts (SCRIPT_Death, this, true); // [RH] Force a delay between death and respawn player->respawn_time = level.time + TICRATE; //Added by MC: Respawn bots if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback) { if (player->isbot) player->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; //Added by MC: Discard enemies. for (int i = 0; i < MAXPLAYERS; i++) { if (players[i].isbot && this == players[i].enemy) { if (players[i].dest == players[i].enemy) players[i].dest = NULL; players[i].enemy = NULL; } } player->spreecount = 0; player->multicount = 0; } // count environment kills against you if (!source) { player->frags[player - players]++; player->fragcount--; // [RH] Cumulative frag count } flags &= ~MF_SOLID; player->playerstate = PST_DEAD; P_DropWeapon (player); if (this == players[consoleplayer].camera && automapactive) { // don't die in auto map, switch view prior to dying AM_Stop (); } } // [RH] If this is the unmorphed version of another monster, destroy this // actor, because the morphed version is the one that will stick around in // the level. if (flags & MF_UNMORPHED) { Destroy (); return; } if (DamageType == MOD_DISINTEGRATE && EDeathState) { // Electrocution death SetState (EDeathState); } else if (DamageType == MOD_FIRE && BDeathState) { // Burn death SetState (BDeathState); } else if (DamageType == MOD_ICE && (IDeathState || ( (!deh.NoAutofreeze && !(flags4 & MF4_NOICEDEATH)) && (player || (flags3 & MF3_ISMONSTER))))) { // Ice death flags |= MF_ICECORPSE; if (IDeathState) { SetState (IDeathState); } else { SetState (&AActor::States[S_GENERICFREEZEDEATH]); } } else if (XDeathState && health < (gameinfo.gametype == GAME_Doom ? -GetDefault()->health : -GetDefault()->health/2)) { // Extreme death SetState (XDeathState); } else { // Normal death DamageType = MOD_UNKNOWN; // [RH] "Frozen" barrels shouldn't do freezing damage if (DeathState != NULL) // [RH] DeathState might be NULL, so try others as needed { SetState (DeathState); } else if (EDeathState != NULL) { SetState (EDeathState); } else if (BDeathState != NULL) { SetState (BDeathState); } else if (IDeathState != NULL) { SetState (IDeathState); } } tics -= pr_killmobj() & 3; if (tics < 1) tics = 1; }
// // P_XYMovement // void P_XYMovement(AActor *mo) { // angle_t angle; fixed_t ptryx, ptryy; player_t *player = NULL; fixed_t xmove, ymove; fixed_t maxmove; static const int windTab[3] = {2048*5, 2048*10, 2048*25}; if (!mo->subsector) return; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mo->info->spawnstate); } return; } maxmove = (mo->waterlevel < 2) || (mo->flags & MF_MISSILE) ? MAXMOVE : MAXMOVE/4; if (mo->flags2 & MF2_WINDTHRUST) { int special = mo->subsector->sector->special; switch (special) { case 40: case 41: case 42: // Wind_East P_ThrustMobj (mo, 0, windTab[special-40]); break; case 43: case 44: case 45: // Wind_North P_ThrustMobj (mo, ANG90, windTab[special-43]); break; case 46: case 47: case 48: // Wind_South P_ThrustMobj (mo, ANG270, windTab[special-46]); break; case 49: case 50: case 51: // Wind_West P_ThrustMobj (mo, ANG180, windTab[special-49]); break; } } xmove = mo->momx = clamp (mo->momx, -maxmove, maxmove); ymove = mo->momy = clamp (mo->momy, -maxmove, maxmove); player = mo->player; if(!player || !player->mo) player = NULL; maxmove /= 2; do { if (xmove > maxmove || ymove > maxmove ) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } // killough 3/15/98: Allow objects to drop off if (!P_TryMove (mo, ptryx, ptryy, true)) { // blocked move if (mo->flags2 & MF2_SLIDE) { // try to slide along it if (BlockingMobj == NULL) { // slide against wall if (BlockingLine != NULL && mo->player && mo->waterlevel && mo->waterlevel < 3 && (mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove) && BlockingLine->sidenum[1] != -1) { mo->momz = WATER_JUMP_SPEED; } P_SlideMove (mo); } else { // slide against mobj if (P_TryMove (mo, mo->x, ptryy, true)) { mo->momx = 0; } else if (P_TryMove (mo, ptryx, mo->y, true)) { mo->momy = 0; } else { mo->momx = mo->momy = 0; } } } else if (mo->flags & MF_MISSILE) { // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. mo->Destroy (); return; } // [SL] 2011-06-02 - Only server should control explosions if (serverside) P_ExplodeMissile (mo); } else { mo->momx = mo->momy = 0; } } } while (xmove || ymove);