DEFINE_ACTION_FUNCTION(AActor, A_Summon) { AMinotaurFriend *mo; mo = Spawn<AMinotaurFriend> (self->Pos(), ALLOW_REPLACE); if (mo) { if (P_TestMobjLocation(mo) == false || !self->tracer) { // Didn't fit - change back to artifact mo->Destroy (); AActor *arti = Spawn<AArtiDarkServant> (self->Pos(), ALLOW_REPLACE); if (arti) arti->flags |= MF_DROPPED; return; } mo->StartTime = level.maptime; if (self->tracer->flags & MF_CORPSE) { // Master dead mo->tracer = NULL; // No master } else { mo->tracer = self->tracer; // Pointer to master AInventory *power = Spawn<APowerMinotaur> (0, 0, 0, NO_REPLACE); power->CallTryPickup (self->tracer); mo->SetFriendPlayer(self->tracer->player); } // Make smoke puff Spawn ("MinotaurSmoke", self->Pos(), ALLOW_REPLACE); S_Sound (self, CHAN_VOICE, mo->ActiveSound, 1, ATTN_NORM); } }
void A_BarrelRespawn (AActor *actor) { fixed_t x = actor->SpawnPoint[0] << FRACBITS; fixed_t y = actor->SpawnPoint[1] << FRACBITS; sector_t *sec; // [BC] AActor *pFog; actor->flags |= MF_SOLID; sec = R_PointInSubsector (x, y)->sector; actor->SetOrigin (x, y, sec->floorplane.ZatPoint (x, y)); if (P_TestMobjLocation (actor)) { AActor *defs = actor->GetDefault(); actor->health = defs->health; actor->flags = defs->flags; actor->flags2 = defs->flags2; actor->SetState (actor->SpawnState); actor->renderflags &= ~RF_INVISIBLE; // [BC] pFog = pFog = Spawn<ATeleportFog> (x, y, actor->z + TELEFOGHEIGHT, ALLOW_REPLACE); // [BC] If we're the server, set the barrel's flags2, and spawn the fog. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { SERVERCOMMANDS_SpawnThing( actor ); SERVERCOMMANDS_SpawnThing( pFog ); } } else { actor->flags &= ~MF_SOLID; } }
DEFINE_ACTION_FUNCTION(AActor, A_GenWizard) { PARAM_ACTION_PROLOGUE; AActor *mo; mo = Spawn("Wizard", self->Pos(), ALLOW_REPLACE); if (mo != NULL) { mo->AddZ(-mo->GetDefault()->Height / 2, false); if (!P_TestMobjLocation (mo)) { // Didn't fit mo->ClearCounters(); mo->Destroy (); } else { // [RH] Make the new wizards inherit D'Sparil's target mo->CopyFriendliness (self->target, true); self->Vel.Zero(); self->SetState (self->FindState(NAME_Death)); self->flags &= ~MF_MISSILE; mo->master = self->target; P_SpawnTeleportFog(self, self->Pos(), false, true); } } return 0; }
/** * NOTE: See p_enemy for variable descriptions. */ void C_DECL A_Summon(mobj_t* actor) { mobj_t* mo; if((mo = P_SpawnMobj(MT_MINOTAUR, actor->origin, actor->angle, 0))) { mobj_t* master; if(P_TestMobjLocation(mo) == false || !actor->tracer) { // Didn't fit - change back to item. P_MobjChangeState(mo, S_NULL); if((mo = P_SpawnMobj(MT_SUMMONMAULATOR, actor->origin, actor->angle, 0))) mo->flags2 |= MF2_DROPPED; return; } memcpy((void *) mo->args, &mapTime, sizeof(mapTime)); master = actor->tracer; if(master->flags & MF_CORPSE) { // Master dead. mo->tracer = NULL; // No master. } else { mo->tracer = actor->tracer; // Pointer to master (mobj_t *) P_GivePower(master->player, PT_MINOTAUR); } // Make smoke puff. P_SpawnMobj(MT_MNTRSMOKE, actor->origin, P_Random() << 24, 0); S_StartSound(SFX_MAULATOR_ACTIVE, actor); } }
void A_GenWizard (AActor *actor) { AActor *mo; mo = Spawn<AWizard> (actor->x, actor->y, actor->z - GetDefault<AWizard>()->height/2, ALLOW_REPLACE); if (mo != NULL) { if (!P_TestMobjLocation (mo)) { // Didn't fit mo->Destroy (); level.total_monsters--; } else { // [RH] Make the new wizards inherit D'Sparil's target mo->CopyFriendliness (actor->target, true); actor->momx = actor->momy = actor->momz = 0; actor->SetState (actor->FindState(NAME_Death)); actor->flags &= ~MF_MISSILE; mo->master = actor->target; // Heretic did not offset it by TELEFOGHEIGHT, so I won't either. Spawn<ATeleportFog> (actor->x, actor->y, actor->z, ALLOW_REPLACE); } } }
bool P_MoveThing(AActor *source, const DVector3 &pos, bool fog) { DVector3 old = source->Pos(); source->SetOrigin (pos, true); if (P_TestMobjLocation (source)) { if (fog) { P_SpawnTeleportFog(source, pos, false, true); P_SpawnTeleportFog(source, old, true, true); } source->ClearInterpolation(); if (source == players[consoleplayer].camera) { R_ResetViewInterpolation(); } return true; } else { source->SetOrigin (old, true); return false; } }
DEFINE_ACTION_FUNCTION(AActor, A_GenWizard) { AActor *mo; mo = Spawn("Wizard", self->Pos(), ALLOW_REPLACE); if (mo != NULL) { mo->AddZ(-mo->GetDefault()->height / 2, false); if (!P_TestMobjLocation (mo)) { // Didn't fit mo->ClearCounters(); mo->Destroy (); } else { // [RH] Make the new wizards inherit D'Sparil's target mo->CopyFriendliness (self->target, true); self->velx = self->vely = self->velz = 0; self->SetState (self->FindState(NAME_Death)); self->flags &= ~MF_MISSILE; mo->master = self->target; // Heretic did not offset it by TELEFOGHEIGHT, so I won't either. Spawn<ATeleportFog> (self->Pos(), ALLOW_REPLACE); } } }
bool P_MoveThing(AActor *source, fixed_t x, fixed_t y, fixed_t z, bool fog) { fixed_t oldx, oldy, oldz; oldx = source->x; oldy = source->y; oldz = source->z; source->SetOrigin (x, y, z); if (P_TestMobjLocation (source)) { if (fog) { Spawn<ATeleportFog> (x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE); Spawn<ATeleportFog> (oldx, oldy, oldz + TELEFOGHEIGHT, ALLOW_REPLACE); } source->PrevX = x; source->PrevY = y; source->PrevZ = z; return true; } else { source->SetOrigin (oldx, oldy, oldz); return false; } }
boolean EV_ThingSpawn(byte *args, boolean fog) { int tid; angle_t angle; mobj_t *mobj; mobj_t *newMobj; mobj_t *fogMobj; mobjtype_t moType; int searcher; boolean success; fixed_t z; success = false; searcher = -1; tid = args[0]; moType = TranslateThingType[args[1]]; if(nomonsters && (mobjinfo[moType].flags & MF_COUNTKILL)) { // Don't spawn monsters if -nomonsters return false; } angle = (int) args[2] << 24; while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if(mobjinfo[moType].flags2 & MF2_FLOATBOB) { z = mobj->z - mobj->floorz; } else { z = mobj->z; } newMobj = P_SpawnMobj(mobj->x, mobj->y, z, moType); if(P_TestMobjLocation(newMobj) == false) { // Didn't fit P_RemoveMobj(newMobj); } else { newMobj->angle = angle; if(fog == true) { fogMobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(SFX_TELEPORT, fogMobj); } newMobj->flags2 |= MF2_DROPPED; // Don't respawn if(newMobj->flags2 & MF2_FLOATBOB) { newMobj->special1 = newMobj->z - newMobj->floorz; } success = true; } } return success; }
bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force) { AActor *actor; if (beast->UnmorphTime == 0 || beast->UnmorphedMe == NULL || beast->flags3 & MF3_STAYMORPHED || beast->UnmorphedMe->flags3 & MF3_STAYMORPHED) { return false; } actor = beast->UnmorphedMe; actor->SetOrigin (beast->Pos(), false); actor->flags |= MF_SOLID; beast->flags &= ~MF_SOLID; ActorFlags6 beastflags6 = beast->flags6; beast->flags6 &= ~MF6_TOUCHY; if (!force && !P_TestMobjLocation (actor)) { // Didn't fit actor->flags &= ~MF_SOLID; beast->flags |= MF_SOLID; beast->flags6 = beastflags6; beast->UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds return false; } actor->angle = beast->angle; actor->target = beast->target; actor->FriendPlayer = beast->FriendPlayer; actor->flags = beast->FlagsSave & ~MF_JUSTHIT; actor->flags = (actor->flags & ~(MF_FRIENDLY|MF_SHADOW)) | (beast->flags & (MF_FRIENDLY|MF_SHADOW)); actor->flags3 = (actor->flags3 & ~(MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST)) | (beast->flags3 & (MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST)); actor->flags4 = (actor->flags4 & ~MF4_NOHATEPLAYERS) | (beast->flags4 & MF4_NOHATEPLAYERS); if (!(beast->FlagsSave & MF_JUSTHIT)) actor->renderflags &= ~RF_INVISIBLE; actor->health = actor->SpawnHealth(); actor->velx = beast->velx; actor->vely = beast->vely; actor->velz = beast->velz; actor->tid = beast->tid; actor->special = beast->special; actor->Score = beast->Score; memcpy (actor->args, beast->args, sizeof(actor->args)); actor->AddToHash (); beast->UnmorphedMe = NULL; DObject::StaticPointerSubstitution (beast, actor); PClassActor *exit_flash = beast->MorphExitFlash; beast->Destroy (); AActor *eflash = Spawn(exit_flash, beast->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); if (eflash) eflash->target = actor; return true; }
dd_bool EV_ThingSpawn(byte *args, dd_bool fog) { int tid, searcher; angle_t angle; mobj_t *mobj, *newMobj, *fogMobj; mobjtype_t moType; dd_bool success; //coord_t z; success = false; searcher = -1; tid = args[0]; moType = TranslateThingType[args[1]]; if(G_Ruleset_NoMonsters() && (MOBJINFO[moType].flags & MF_COUNTKILL)) { // Don't spawn monsters if -nomonsters return false; } angle = (int) args[2] << 24; while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { //z = mobj->origin[VZ]; if((newMobj = P_SpawnMobj(moType, mobj->origin, angle, 0))) { if(P_TestMobjLocation(newMobj) == false) { // Didn't fit P_MobjRemove(newMobj, true); } else { if(fog) { if((fogMobj = P_SpawnMobjXYZ(MT_TFOG, mobj->origin[VX], mobj->origin[VY], mobj->origin[VZ] + TELEFOGHEIGHT, angle + ANG180, 0))) S_StartSound(SFX_TELEPORT, fogMobj); } newMobj->flags2 |= MF2_DROPPED; // Don't respawn if(newMobj->flags2 & MF2_FLOATBOB) { newMobj->special1 = FLT2FIX(newMobj->origin[VZ] - newMobj->floorZ); } success = true; } } } return success; }
BOOL P_Thing_Projectile (int tid, int type, angle_t angle, fixed_t speed, fixed_t vspeed, BOOL gravity) { int rtn = 0; int kind; AActor *spot = NULL, *mobj; if (type >= NumSpawnableThings) return false; if ( (kind = SpawnableThings[type]) == 0) return false; if ((mobjinfo[kind].flags & MF_COUNTKILL) && sv_nomonsters) return false; while ( (spot = AActor::FindByTID (spot, tid)) ) { if (spot->type != MT_MAPSPOT && spot->type != MT_MAPSPOTGRAVITY) continue; mobj = new AActor (spot->x, spot->y, spot->z, (mobjtype_t)kind); if (mobj) { if (mobj->info->seesound) S_Sound (mobj, CHAN_VOICE, mobj->info->seesound, 1, ATTN_NORM); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags & MF_COUNTKILL)) mobj->flags2 |= MF2_LOGRAV; } else mobj->flags |= MF_NOGRAVITY; mobj->target = spot->ptr(); mobj->angle = angle; mobj->momx = FixedMul (speed, finecosine[angle>>ANGLETOFINESHIFT]); mobj->momy = FixedMul (speed, finesine[angle>>ANGLETOFINESHIFT]); mobj->momz = vspeed; mobj->flags |= MF_DROPPED; if (mobj->flags & MF_MISSILE) rtn = P_CheckMissileSpawn (mobj); else if (!P_TestMobjLocation (mobj)) mobj->Destroy (); } } return rtn; }
BOOL P_Thing_Spawn (int tid, int type, angle_t angle, BOOL fog) { fixed_t z; int rtn = 0; int kind; AActor *spot = NULL, *mobj; if (type >= NumSpawnableThings) return false; if ( (kind = SpawnableThings[type]) == 0) return false; if ((mobjinfo[kind].flags & MF_COUNTKILL) && sv_nomonsters == 1) return false; while ( (spot = AActor::FindByTID (spot, tid)) ) { if (mobjinfo[kind].flags2 & MF2_FLOATBOB) z = spot->z - spot->floorz; else z = spot->z; mobj = new AActor (spot->x, spot->y, z, (mobjtype_t)kind); if (mobj) { if (P_TestMobjLocation (mobj)) { rtn++; mobj->angle = angle; if (fog) S_Sound (new AActor (spot->x, spot->y, spot->z, MT_TFOG), CHAN_VOICE, "misc/teleport", 1, ATTN_NORM); mobj->flags |= MF_DROPPED; // Don't respawn if (mobj->flags2 & MF2_FLOATBOB) { mobj->special1 = mobj->z - mobj->floorz; } } else { mobj->Destroy (); rtn = false; } } } return rtn != 0; }
bool P_MoveThing(AActor *source, const DVector3 &pos, bool fog) { DVector3 old = source->Pos(); source->SetOrigin (pos, true); if (P_TestMobjLocation (source)) { if (fog) { P_SpawnTeleportFog(source, pos, false, true); P_SpawnTeleportFog(source, old, true, true); } source->ClearInterpolation(); source->renderflags |= RF_NOINTERPOLATEVIEW; return true; } else { source->SetOrigin (old, true); return false; } }
int P_Thing_Warp(AActor *caller, AActor *reference, double xofs, double yofs, double zofs, DAngle angle, int flags, double heightoffset, double radiusoffset, DAngle pitch) { if (flags & WARPF_MOVEPTR) { AActor *temp = reference; reference = caller; caller = temp; } DVector3 old = caller->Pos(); int oldpgroup = caller->Sector->PortalGroup; zofs += reference->Height * heightoffset; if (!(flags & WARPF_ABSOLUTEANGLE)) { angle += (flags & WARPF_USECALLERANGLE) ? caller->Angles.Yaw: reference->Angles.Yaw; } const double rad = radiusoffset * reference->radius; const double s = angle.Sin(); const double c = angle.Cos(); if (!(flags & WARPF_ABSOLUTEPOSITION)) { if (!(flags & WARPF_ABSOLUTEOFFSET)) { double xofs1 = xofs; // (borrowed from A_SpawnItemEx, assumed workable) // in relative mode negative y values mean 'left' and positive ones mean 'right' // This is the inverse orientation of the absolute mode! xofs = xofs1 * c + yofs * s; yofs = xofs1 * s - yofs * c; } if (flags & WARPF_TOFLOOR) { // set correct xy // now the caller's floorz should be appropriate for the assigned xy-position // assigning position again with. // extra unlink, link and environment calculation caller->SetOrigin(reference->Vec3Offset(xofs + rad * c, yofs + rad * s, 0.), true); // The two-step process is important. caller->SetZ(caller->floorz + zofs); } else { caller->SetOrigin(reference->Vec3Offset(xofs + rad * c, yofs + rad * s, zofs), true); } } else // [MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. { caller->SetOrigin(xofs + rad * c, yofs + rad * s, zofs, true); if (flags & WARPF_TOFLOOR) { caller->SetZ(caller->floorz + zofs); } } if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) { if (flags & WARPF_TESTONLY) { caller->SetOrigin(old, true); } else { caller->Angles.Yaw = angle; if (flags & WARPF_COPYPITCH) caller->SetPitch(reference->Angles.Pitch, false); if (pitch != 0) caller->SetPitch(caller->Angles.Pitch + pitch, false); if (flags & WARPF_COPYVELOCITY) { caller->Vel = reference->Vel; } if (flags & WARPF_STOP) { caller->Vel.Zero(); } // this is no fun with line portals if (flags & WARPF_WARPINTERPOLATION) { // This just translates the movement but doesn't change the vector DVector3 displacedold = old + Displacements.getOffset(oldpgroup, caller->Sector->PortalGroup); caller->Prev += caller->Pos() - displacedold; caller->PrevPortalGroup = caller->Sector->PortalGroup; } else if (flags & WARPF_COPYINTERPOLATION) { // Map both positions of the reference actor to the current portal group DVector3 displacedold = old + Displacements.getOffset(reference->PrevPortalGroup, caller->Sector->PortalGroup); DVector3 displacedref = old + Displacements.getOffset(reference->Sector->PortalGroup, caller->Sector->PortalGroup); caller->Prev = caller->Pos() + displacedold - displacedref; caller->PrevPortalGroup = caller->Sector->PortalGroup; } else if (!(flags & WARPF_INTERPOLATE)) { caller->ClearInterpolation(); } if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) { caller->AddZ(reference->GetBobOffset()); } } return true; } caller->SetOrigin(old, true); return false; }
bool P_Thing_Spawn (int tid, AActor *source, int type, DAngle angle, bool fog, int newtid) { int rtn = 0; PClassActor *kind; AActor *spot, *mobj; FActorIterator iterator (tid); kind = P_GetSpawnableType(type); if (kind == NULL) return false; // Handle decorate replacements. kind = kind->GetReplacement(); if ((GetDefaultByType(kind)->flags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { mobj = Spawn (kind, spot->Pos(), ALLOW_REPLACE); if (mobj != NULL) { ActorFlags2 oldFlags2 = mobj->flags2; mobj->flags2 |= MF2_PASSMOBJ; if (P_TestMobjLocation (mobj)) { rtn++; mobj->Angles.Yaw = (angle != 1000000. ? angle : spot->Angles.Yaw); if (fog) { P_SpawnTeleportFog(mobj, spot->PosPlusZ(TELEFOGHEIGHT), false, true); } if (mobj->flags & MF_SPECIAL) mobj->flags |= MF_DROPPED; // Don't respawn mobj->tid = newtid; mobj->AddToHash (); mobj->flags2 = oldFlags2; } else { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. mobj->ClearCounters(); mobj->Destroy (); } } spot = iterator.Next(); } return rtn != 0; }
bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_name, DAngle angle, double speed, double vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; PClassActor *kind; AActor *spot, *mobj, *targ = forcedest; FActorIterator iterator (tid); int defflags3; if (type_name == NULL) { kind = P_GetSpawnableType(type); } else { kind = PClass::FindActor(type_name); } if (kind == NULL) { return false; } // Handle decorate replacements. kind = kind->GetReplacement(); defflags3 = GetDefaultByType(kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { FActorIterator tit (dest); if (dest == 0 || (targ = tit.Next())) { do { double z = spot->Z(); if (defflags3 & MF3_FLOORHUGGER) { z = ONFLOORZ; } else if (defflags3 & MF3_CEILINGHUGGER) { z = ONCEILINGZ; } else if (z != ONFLOORZ) { z -= spot->Floorclip; } mobj = Spawn (kind, spot->PosAtZ(z), ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->Gravity = 1./8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; if (targ != NULL) { DVector3 aim = mobj->Vec3To(targ); aim.Z += targ->Height / 2; if (leadTarget && speed > 0 && !targ->Vel.isZero()) { // Aiming at the target's position some time in the future // is basically just an application of the law of sines: // a/sin(A) = b/sin(B) // Thanks to all those on the notgod phorum for helping me // with the math. I don't think I would have thought of using // trig alone had I been left to solve it by myself. DVector3 tvel = targ->Vel; if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3) { // If the target is subject to gravity and not underwater, // assume that it isn't moving vertically. Thanks to gravity, // even if we did consider the vertical component of the target's // velocity, we would still miss more often than not. tvel.Z = 0.0; if (targ->Vel.X == 0 && targ->Vel.Y == 0) { goto nolead; } } double dist = aim.Length(); double targspeed = tvel.Length(); double ydotx = -aim | tvel; double a = g_acos (clamp (ydotx / targspeed / dist, -1.0, 1.0)); double multiplier = double(pr_leadtarget.Random2())*0.1/255+1.1; double sinb = -clamp (targspeed*multiplier * g_sin(a) / speed, -1.0, 1.0); // Use the cross product of two of the triangle's sides to get a // rotation vector. DVector3 rv(tvel ^ aim); // The vector must be normalized. rv.MakeUnit(); // Now combine the rotation vector with angle b to get a rotation matrix. DMatrix3x3 rm(rv, g_cos(g_asin(sinb)), sinb); // And multiply the original aim vector with the matrix to get a // new aim vector that leads the target. DVector3 aimvec = rm * aim; // And make the projectile follow that vector at the desired speed. mobj->Vel = aimvec * (speed / dist); mobj->AngleFromVel(); } else { nolead: mobj->Angles.Yaw = mobj->AngleTo(targ); mobj->Vel = aim.Resized (speed); } if (mobj->flags2 & MF2_SEEKERMISSILE) { mobj->tracer = targ; } } else { mobj->Angles.Yaw = angle; mobj->VelFromAngle(speed); mobj->Vel.Z = vspeed; } // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = mobj->VelToSpeed(); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->Vel.Z = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj, spot->radius)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. mobj->ClearCounters(); mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
bool FLevelLocals::EV_Thing_Projectile (int tid, AActor *source, int type, const char *type_name, DAngle angle, double speed, double vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; PClassActor *kind; AActor *spot, *mobj, *targ = forcedest; auto iterator = GetActorIterator(tid); int defflags3; if (type_name == NULL) { kind = P_GetSpawnableType(type); } else { kind = PClass::FindActor(type_name); } if (kind == NULL) { return false; } // Handle decorate replacements. kind = kind->GetReplacement(this); defflags3 = GetDefaultByType(kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { auto tit = GetActorIterator(dest); if (dest == 0 || (targ = tit.Next())) { do { double z = spot->Z(); if (defflags3 & MF3_FLOORHUGGER) { z = ONFLOORZ; } else if (defflags3 & MF3_CEILINGHUGGER) { z = ONCEILINGZ; } else if (z != ONFLOORZ) { z -= spot->Floorclip; } mobj = Spawn (spot->Level, kind, spot->PosAtZ(z), ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->Gravity = 1./8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; if (targ != nullptr) { VelIntercept(targ, mobj, speed, false, false, leadTarget); if (mobj->flags2 & MF2_SEEKERMISSILE) { mobj->tracer = targ; } } else { mobj->Angles.Yaw = angle; mobj->VelFromAngle(speed); mobj->Vel.Z = vspeed; } // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = mobj->VelToSpeed(); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->Vel.Z = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj, spot->radius)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. mobj->ClearCounters(); mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
bool P_Thing_ProjectileEx (int tid, AActor *source, int type, const char *type_name, angle_t angle, fixed_t spawnx, fixed_t spawny, fixed_t spawnz, fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; const PClass *kind; AActor *spot, *mobj, *targ = forcedest; FActorIterator iterator (tid); double fspeed = speed; int defflags3; fixed_t x = 0; fixed_t y = 0; fixed_t z = 0; angle_t pitch; if (type_name == NULL) { kind = P_GetSpawnableType(type); } else { kind = PClass::FindClass(type_name); } if (kind == NULL || kind->ActorInfo == NULL) { return false; } /* // [RC] I'm sure there was reason for this code here, but I'm not sure why anymore. Possibly // deprecated ThingProjectile code. if (type_name == NULL) { if (type >= MAX_SPAWNABLES) return false; if ((kind = SpawnableThings[type]) == NULL) return false; } else { if ((kind = PClass::FindClass(type_name)) == NULL || kind->ActorInfo == NULL) return false; } */ // Handle decorate replacements. kind = kind->GetReplacement(); defflags3 = GetDefaultByType (kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { FActorIterator tit (dest); if (dest == 0 || (targ = tit.Next())) { do { //fixed_t z = spot->z; z += spot->player->mo->z + (spot->player->mo->AttackZOffset+spawnz);//spot->player->mo->AttackZOffset; x += spot->player->mo->x + spawnx; y += spot->player->mo->y + spawny; pitch = spot->player->mo->pitch; if (defflags3 & MF3_FLOORHUGGER) z = ONFLOORZ; else if (defflags3 & MF3_CEILINGHUGGER) z = ONCEILINGZ; else if (z != ONFLOORZ)z -= spot->floorclip; if (z < spot->floorz) z = spot->floorz; mobj = Spawn (kind, x, y, z, ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->gravity = FRACUNIT/8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; mobj->master = spot; //Ideally, give the projectile an owner. That being the player. mobj->angle = angle; mobj->velx = FixedMul(speed, finecosine[angle>>ANGLETOFINESHIFT]); mobj->vely = FixedMul(speed, finesine[angle>>ANGLETOFINESHIFT]); mobj->velz = vspeed; // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = fixed_t (sqrt (double(mobj->velx)*mobj->velx + double(mobj->vely)*mobj->vely + double(mobj->velz)*mobj->velz)); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->velz = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. mobj->ClearCounters(); mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, int newtid) { int rtn = 0; const PClass *kind; AActor *spot, *mobj; FActorIterator iterator (tid); if (type >= MAX_SPAWNABLES) return false; if ( (kind = SpawnableThings[type]) == NULL) return false; // Handle decorate replacements. kind = kind->ActorInfo->GetReplacement()->Class; if ((GetDefaultByType (kind)->flags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { mobj = Spawn (kind, spot->x, spot->y, spot->z, ALLOW_REPLACE); if (mobj != NULL) { DWORD oldFlags2 = mobj->flags2; mobj->flags2 |= MF2_PASSMOBJ; if (P_TestMobjLocation (mobj)) { rtn++; mobj->angle = (angle != ANGLE_MAX ? angle : spot->angle); if (fog) { Spawn<ATeleportFog> (spot->x, spot->y, spot->z + TELEFOGHEIGHT, ALLOW_REPLACE); } if (mobj->flags & MF_SPECIAL) mobj->flags |= MF_DROPPED; // Don't respawn mobj->tid = newtid; mobj->AddToHash (); mobj->flags2 = oldFlags2; } else { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. if (mobj->CountsAsKill()) { level.total_monsters--; } // Same, for items if (mobj->flags & MF_COUNTITEM) { level.total_items--; } mobj->Destroy (); } } spot = iterator.Next(); } return rtn != 0; }
bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_name, angle_t angle, fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; const PClass *kind; AActor *spot, *mobj, *targ = forcedest; FActorIterator iterator (tid); double fspeed = speed; int defflags3; if (type_name == NULL) { if (type >= MAX_SPAWNABLES) return false; if ((kind = SpawnableThings[type]) == NULL) return false; } else { if ((kind = PClass::FindClass(type_name)) == NULL || kind->ActorInfo == NULL) return false; } // Handle decorate replacements. kind = kind->ActorInfo->GetReplacement()->Class; defflags3 = GetDefaultByType (kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { FActorIterator tit (dest); if (dest == 0 || (targ = tit.Next())) { do { fixed_t z = spot->z; if (defflags3 & MF3_FLOORHUGGER) { z = ONFLOORZ; } else if (defflags3 & MF3_CEILINGHUGGER) { z = ONCEILINGZ; } else if (z != ONFLOORZ) { z -= spot->floorclip; } mobj = Spawn (kind, spot->x, spot->y, z, ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->gravity = FRACUNIT/8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; if (targ != NULL) { fixed_t spot[3] = { targ->x, targ->y, targ->z+targ->height/2 }; FVector3 aim(float(spot[0] - mobj->x), float(spot[1] - mobj->y), float(spot[2] - mobj->z)); if (leadTarget && speed > 0 && (targ->velx | targ->vely | targ->velz)) { // Aiming at the target's position some time in the future // is basically just an application of the law of sines: // a/sin(A) = b/sin(B) // Thanks to all those on the notgod phorum for helping me // with the math. I don't think I would have thought of using // trig alone had I been left to solve it by myself. FVector3 tvel(targ->velx, targ->vely, targ->velz); if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3) { // If the target is subject to gravity and not underwater, // assume that it isn't moving vertically. Thanks to gravity, // even if we did consider the vertical component of the target's // velocity, we would still miss more often than not. tvel.Z = 0.0; if ((targ->velx | targ->vely) == 0) { goto nolead; } } double dist = aim.Length(); double targspeed = tvel.Length(); double ydotx = -aim | tvel; double a = acos (clamp (ydotx / targspeed / dist, -1.0, 1.0)); double multiplier = double(pr_leadtarget.Random2())*0.1/255+1.1; double sinb = -clamp (targspeed*multiplier * sin(a) / fspeed, -1.0, 1.0); // Use the cross product of two of the triangle's sides to get a // rotation vector. FVector3 rv(tvel ^ aim); // The vector must be normalized. rv.MakeUnit(); // Now combine the rotation vector with angle b to get a rotation matrix. FMatrix3x3 rm(rv, cos(asin(sinb)), sinb); // And multiply the original aim vector with the matrix to get a // new aim vector that leads the target. FVector3 aimvec = rm * aim; // And make the projectile follow that vector at the desired speed. double aimscale = fspeed / dist; mobj->velx = fixed_t (aimvec[0] * aimscale); mobj->vely = fixed_t (aimvec[1] * aimscale); mobj->velz = fixed_t (aimvec[2] * aimscale); mobj->angle = R_PointToAngle2 (0, 0, mobj->velx, mobj->vely); } else { nolead: mobj->angle = R_PointToAngle2 (mobj->x, mobj->y, targ->x, targ->y); aim.Resize (fspeed); mobj->velx = fixed_t(aim[0]); mobj->vely = fixed_t(aim[1]); mobj->velz = fixed_t(aim[2]); } if (mobj->flags2 & MF2_SEEKERMISSILE) { mobj->tracer = targ; } } else { mobj->angle = angle; mobj->velx = FixedMul (speed, finecosine[angle>>ANGLETOFINESHIFT]); mobj->vely = FixedMul (speed, finesine[angle>>ANGLETOFINESHIFT]); mobj->velz = vspeed; } // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = fixed_t (sqrt (double(mobj->velx)*mobj->velx + double(mobj->vely)*mobj->vely + double(mobj->velz)*mobj->velz)); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->velz = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. if (mobj->CountsAsKill()) { level.total_monsters--; } // Same, for items if (mobj->flags & MF_COUNTITEM) { level.total_items--; } mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, bool force) { AWeapon *beastweap; APlayerPawn *mo; APlayerPawn *pmo; angle_t angle; pmo = player->mo; // [MH] // Checks pmo as well; the PowerMorph destroyer will // try to unmorph the player; if the destroyer runs // because the level or game is ended while morphed, // by the time it gets executed the morphed player // pawn instance may have already been destroyed. if (pmo == NULL || pmo->tracer == NULL) { return false; } bool DeliberateUnmorphIsOkay = !!(MORPH_STANDARDUNDOING & unmorphflag); if ((pmo->flags2 & MF2_INVULNERABLE) // If the player is invulnerable && ((player != activator) // and either did not decide to unmorph, || (!((player->MorphStyle & MORPH_WHENINVULNERABLE) // or the morph style does not allow it || (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it), { // Then the player is immune to the unmorph. return false; } mo = barrier_cast<APlayerPawn *>(pmo->tracer); mo->SetOrigin (pmo->Pos(), false); mo->flags |= MF_SOLID; pmo->flags &= ~MF_SOLID; if (!force && !P_TestMobjLocation (mo)) { // Didn't fit mo->flags &= ~MF_SOLID; pmo->flags |= MF_SOLID; player->morphTics = 2*TICRATE; return false; } pmo->player = NULL; // Remove the morph power if the morph is being undone prematurely. for (AInventory *item = pmo->Inventory, *next = NULL; item != NULL; item = next) { next = item->Inventory; if (item->IsKindOf(RUNTIME_CLASS(APowerMorph))) { static_cast<APowerMorph *>(item)->SetNoCallUndoMorph(); item->Destroy(); } } EndAllPowerupEffects(pmo->Inventory); mo->ObtainInventory (pmo); DObject::StaticPointerSubstitution (pmo, mo); if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR)) { mo->tid = pmo->tid; mo->AddToHash (); } mo->angle = pmo->angle; mo->player = player; mo->reactiontime = 18; mo->flags = ActorFlags::FromInt (pmo->special2) & ~MF_JUSTHIT; mo->velx = 0; mo->vely = 0; player->velx = 0; player->vely = 0; mo->velz = pmo->velz; if (!(pmo->special2 & MF_JUSTHIT)) { mo->renderflags &= ~RF_INVISIBLE; } mo->flags = (mo->flags & ~(MF_SHADOW|MF_NOGRAVITY)) | (pmo->flags & (MF_SHADOW|MF_NOGRAVITY)); mo->flags2 = (mo->flags2 & ~MF2_FLY) | (pmo->flags2 & MF2_FLY); mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST); mo->Score = pmo->Score; InitAllPowerupEffects(mo->Inventory); PClassActor *exit_flash = player->MorphExitFlash; bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON); bool undobydeathsaves = !!(player->MorphStyle & MORPH_UNDOBYDEATHSAVES); player->morphTics = 0; player->MorphedPlayerClass = 0; player->MorphStyle = 0; player->MorphExitFlash = NULL; player->viewheight = mo->ViewHeight; AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true); if (level2 != NULL) { level2->Destroy (); } if ((player->health > 0) || undobydeathsaves) { player->health = mo->health = mo->SpawnHealth(); } else // killed when morphed so stay dead { mo->health = player->health; } player->mo = mo; if (player->camera == pmo) { player->camera = mo; } // [MH] // If the player that was morphed is the one // taking events, reset up the face, if any; // this is only needed for old-skool skins // and for the original DOOM status bar. if (player == &players[consoleplayer]) { FString face = pmo->GetClass()->Face; if (face.IsNotEmpty() && strcmp(face, "None") != 0) { // Assume root-level base skin to begin with size_t skinindex = 0; // If a custom skin was in use, then reload it // or else the base skin for the player class. if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () && (size_t)player->userinfo.GetSkin() < numskins) { skinindex = player->userinfo.GetSkin(); } else if (PlayerClasses.Size () > 1) { const PClass *whatami = player->mo->GetClass(); for (unsigned int i = 0; i < PlayerClasses.Size (); ++i) { if (PlayerClasses[i].Type == whatami) { skinindex = i; break; } } } } } angle = mo->angle >> ANGLETOFINESHIFT; AActor *eflash = NULL; if (exit_flash != NULL) { eflash = Spawn(exit_flash, pmo->Vec3Offset(20*finecosine[angle], 20*finesine[angle], TELEFOGHEIGHT), ALLOW_REPLACE); if (eflash) eflash->target = mo; } mo->SetupWeaponSlots(); // Use original class's weapon slots. beastweap = player->ReadyWeapon; if (player->PremorphWeapon != NULL) { player->PremorphWeapon->PostMorphWeapon (); } else { player->ReadyWeapon = player->PendingWeapon = NULL; } if (correctweapon) { // Better "lose morphed weapon" semantics PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon); if (morphweapon != NULL && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon))) { AWeapon *OriginalMorphWeapon = static_cast<AWeapon *>(mo->FindInventory (morphweapon)); if ((OriginalMorphWeapon != NULL) && (OriginalMorphWeapon->GivenAsMorphWeapon)) { // You don't get to keep your morphed weapon. if (OriginalMorphWeapon->SisterWeapon != NULL) { OriginalMorphWeapon->SisterWeapon->Destroy (); } OriginalMorphWeapon->Destroy (); } } } else // old behaviour (not really useful now) { // Assumptions made here are no longer valid if (beastweap != NULL) { // You don't get to keep your morphed weapon. if (beastweap->SisterWeapon != NULL) { beastweap->SisterWeapon->Destroy (); } beastweap->Destroy (); } } pmo->tracer = NULL; pmo->Destroy (); // Restore playerclass armor to its normal amount. AHexenArmor *hxarmor = mo->FindInventory<AHexenArmor>(); if (hxarmor != NULL) { hxarmor->Slots[4] = mo->GetClass()->HexenArmor[0]; } return true; }