DEFINE_ACTION_FUNCTION(AActor, A_PotteryExplode) { AActor *mo = NULL; int i; for(i = (pr_pottery()&3)+3; i; i--) { mo = Spawn ("PotteryBit", self->x, self->y, self->z, ALLOW_REPLACE); if (mo) { mo->SetState (mo->SpawnState + (pr_pottery()%5)); mo->velz = ((pr_pottery()&7)+5)*(3*FRACUNIT/4); mo->velx = (pr_pottery.Random2())<<(FRACBITS-6); mo->vely = (pr_pottery.Random2())<<(FRACBITS-6); } } S_Sound (mo, CHAN_BODY, "PotteryExplode", 1, ATTN_NORM); // Spawn an item? const PClass *type = P_GetSpawnableType(self->args[0]); if (type != NULL) { if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) || !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER)) { // Only spawn monsters if not -nomonsters Spawn (type, self->x, self->y, self->z, ALLOW_REPLACE); } } }
DEFINE_ACTION_FUNCTION(AActor, A_SoAExplode) { AActor *mo; int i; for (i = 0; i < 10; i++) { mo = Spawn ("ZArmorChunk", self->x+((pr_soaexplode()-128)<<12), self->y+((pr_soaexplode()-128)<<12), self->z+(pr_soaexplode()*self->height/256), ALLOW_REPLACE); if (mo) { mo->SetState (mo->SpawnState + i); mo->velz = ((pr_soaexplode()&7)+5)*FRACUNIT; mo->velx = pr_soaexplode.Random2()<<(FRACBITS-6); mo->vely = pr_soaexplode.Random2()<<(FRACBITS-6); } } // Spawn an item? const PClass *type = P_GetSpawnableType(self->args[0]); if (type != NULL) { if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) || !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER)) { // Only spawn monsters if not -nomonsters Spawn (type, self->x, self->y, self->z, ALLOW_REPLACE); } } S_Sound (self, CHAN_BODY, self->DeathSound, 1, ATTN_NORM); self->Destroy (); }
void P_SpawnTeleportFog(fixed_t x, fixed_t y, fixed_t z, int spawnid) { const PClass *fog = P_GetSpawnableType(spawnid); if (fog == NULL) { AActor *mo = Spawn ("TeleportFog", x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE); } else { AActor *mo = Spawn (fog, x, y, z, ALLOW_REPLACE); if (mo != NULL) S_Sound(mo, CHAN_BODY, mo->SeeSound, 1.f, ATTN_NORM); } }
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 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_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) { kind = P_GetSpawnableType(type); } else { kind = PClass::FindClass(type_name); } if (kind == 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; 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. mobj->ClearCounters(); mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
DEFINE_ACTION_FUNCTION(AActor, GetSpawnableType) { PARAM_PROLOGUE; PARAM_INT(num); ACTION_RETURN_OBJECT(P_GetSpawnableType(num)); }
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; }