void A_VolcBallImpact (AActor *ball) { int i; AActor *tiny; angle_t angle; if (ball->z <= ball->floorz) { ball->flags |= MF_NOGRAVITY; ball->gravity = FRACUNIT; ball->z += 28*FRACUNIT; //ball->momz = 3*FRACUNIT; } P_RadiusAttack (ball, ball->target, 25, 25, NAME_Fire, true); for (i = 0; i < 4; i++) { tiny = Spawn<AVolcanoTBlast> (ball->x, ball->y, ball->z, ALLOW_REPLACE); tiny->target = ball; angle = i*ANG90; tiny->angle = angle; angle >>= ANGLETOFINESHIFT; tiny->momx = FixedMul (FRACUNIT*7/10, finecosine[angle]); tiny->momy = FixedMul (FRACUNIT*7/10, finesine[angle]); tiny->momz = FRACUNIT + (pr_impact() << 9); P_CheckMissileSpawn (tiny); } }
DEFINE_ACTION_FUNCTION(AActor, A_VolcBallImpact) { unsigned int i; AActor *tiny; angle_t angle; if (self->z <= self->floorz) { self->flags |= MF_NOGRAVITY; self->gravity = FRACUNIT; self->z += 28*FRACUNIT; //self->velz = 3*FRACUNIT; } P_RadiusAttack (self, self->target, 25, 25, NAME_Fire, RADF_HURTSOURCE); for (i = 0; i < 4; i++) { tiny = Spawn("VolcanoTBlast", self->x, self->y, self->z, ALLOW_REPLACE); tiny->target = self; angle = i*ANG90; tiny->angle = angle; angle >>= ANGLETOFINESHIFT; tiny->velx = FixedMul (FRACUNIT*7/10, finecosine[angle]); tiny->vely = FixedMul (FRACUNIT*7/10, finesine[angle]); tiny->velz = FRACUNIT + (pr_volcimpact() << 9); P_CheckMissileSpawn (tiny, self->radius); } }
DEFINE_ACTION_FUNCTION(AActor, A_SentinelAttack) { PARAM_ACTION_PROLOGUE; AActor *missile, *trail; // [BB] Without a target the P_SpawnMissileZAimed call will crash. if (!self->target) { return 0; } missile = P_SpawnMissileZAimed (self, self->Z() + 32, self->target, PClass::FindActor("SentinelFX2")); if (missile != NULL && (missile->Vel.X != 0 || missile->Vel.Y != 0)) { for (int i = 8; i > 1; --i) { trail = Spawn("SentinelFX1", self->Vec3Angle(missile->radius*i, missile->Angles.Yaw, 32 + missile->Vel.Z / 4 * i), ALLOW_REPLACE); if (trail != NULL) { trail->target = self; trail->Vel = missile->Vel; P_CheckMissileSpawn (trail, self->radius); } } missile->AddZ(missile->Vel.Z / 4); } return 0; }
AActor *P_SpawnSubMissile (AActor *source, PClassActor *type, AActor *target) { AActor *other = Spawn (type, source->Pos(), ALLOW_REPLACE); if (other == NULL) { return NULL; } other->target = target; other->angle = source->angle; other->velx = FixedMul (other->Speed, finecosine[source->angle >> ANGLETOFINESHIFT]); other->vely = FixedMul (other->Speed, finesine[source->angle >> ANGLETOFINESHIFT]); if (other->flags4 & MF4_SPECTRAL) { if (source->flags & MF_MISSILE && source->flags4 & MF4_SPECTRAL) { other->FriendPlayer = source->FriendPlayer; } else { other->SetFriendPlayer(target->player); } } if (P_CheckMissileSpawn (other, source->radius)) { angle_t pitch = P_AimLineAttack (source, source->angle, 1024*FRACUNIT); other->velz = FixedMul (-finesine[pitch>>ANGLETOFINESHIFT], other->Speed); return other; }
AActor *P_SpawnSubMissile (AActor *source, PClassActor *type, AActor *target) { AActor *other = Spawn (type, source->Pos(), ALLOW_REPLACE); if (other == NULL) { return NULL; } other->target = target; other->Angles.Yaw = source->Angles.Yaw; other->VelFromAngle(); if (other->flags4 & MF4_SPECTRAL) { if (source->flags & MF_MISSILE && source->flags4 & MF4_SPECTRAL) { other->FriendPlayer = source->FriendPlayer; } else { other->SetFriendPlayer(target->player); } } if (P_CheckMissileSpawn (other, source->radius)) { DAngle pitch = P_AimLineAttack (source, source->Angles.Yaw, 1024.); other->Vel.Z = -other->Speed * pitch.Sin(); return other; } return NULL; }
void A_FireOldBFG(player_t *player, pspdef_t *psp) { int type = MT_PLASMA1; if (compatibility_level < mbf_compatibility) return; CHECK_WEAPON_CODEPOINTER("A_FireOldBFG", player); if (weapon_recoil && !(player->mo->flags & MF_NOCLIP)) P_Thrust(player, ANG180 + player->mo->angle, 512*recoil_values[wp_plasma]); player->ammo[weaponinfo[player->readyweapon].ammo]--; player->extralight = 2; do { mobj_t *th, *mo = player->mo; angle_t an = mo->angle; angle_t an1 = ((P_Random(pr_bfg)&127) - 64) * (ANG90/768) + an; angle_t an2 = ((P_Random(pr_bfg)&127) - 64) * (ANG90/640) + ANG90; extern int autoaim; if (autoaim/* || !beta_emulation*/) { // killough 8/2/98: make autoaiming prefer enemies uint_64_t mask = mbf_features ? MF_FRIEND : 0; fixed_t slope; do { slope = P_AimLineAttack(mo, an, 16*64*FRACUNIT, mask); if (!linetarget) slope = P_AimLineAttack(mo, an += 1<<26, 16*64*FRACUNIT, mask); if (!linetarget) slope = P_AimLineAttack(mo, an -= 2<<26, 16*64*FRACUNIT, mask); if (!linetarget) slope = 0, an = mo->angle; } while (mask && (mask=0, !linetarget)); // killough 8/2/98 an1 += an - mo->angle; an2 += tantoangle[slope >> DBITS]; } th = P_SpawnMobj(mo->x, mo->y, mo->z + 62*FRACUNIT - player->psprites[ps_weapon].sy, type); P_SetTarget(&th->target, mo); th->angle = an1; th->momx = finecosine[an1>>ANGLETOFINESHIFT] * 25; th->momy = finesine[an1>>ANGLETOFINESHIFT] * 25; th->momz = finetangent[an2>>ANGLETOFINESHIFT] * 25; P_CheckMissileSpawn(th); } while ((type != MT_PLASMA2) && (type = MT_PLASMA2)); //killough: obfuscated! }
void A_SpectralMissile (AActor *self, const char *missilename) { if (self->target != NULL) { AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, PClass::FindClass(missilename), false); if (missile != NULL) { missile->tracer = self->target; P_CheckMissileSpawn(missile); } } }
dd_bool EV_ThingProjectile(byte* args, dd_bool gravity) { uint an; int tid, searcher; angle_t angle; coord_t speed, vspeed; mobjtype_t moType; mobj_t* mobj, *newMobj; dd_bool success; 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; an = angle >> ANGLETOFINESHIFT; speed = FIX2FLT((int) args[3] << 13); vspeed = FIX2FLT((int) args[4] << 13); while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if((newMobj = P_SpawnMobj(moType, mobj->origin, angle, 0))) { if(newMobj->info->seeSound) S_StartSound(newMobj->info->seeSound, newMobj); newMobj->target = mobj; // Originator newMobj->mom[MX] = speed * FIX2FLT(finecosine[an]); newMobj->mom[MY] = speed * FIX2FLT(finesine[an]); newMobj->mom[MZ] = vspeed; newMobj->flags2 |= MF2_DROPPED; // Don't respawn if(gravity == true) { newMobj->flags &= ~MF_NOGRAVITY; newMobj->flags2 |= MF2_LOGRAV; } if(P_CheckMissileSpawn(newMobj) == true) { 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; }
boolean EV_ThingProjectile(byte *args, boolean gravity) { int tid; angle_t angle; int fineAngle; fixed_t speed; fixed_t vspeed; mobjtype_t moType; mobj_t *mobj; mobj_t *newMobj; int searcher; boolean success; 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; fineAngle = angle >> ANGLETOFINESHIFT; speed = (int) args[3] << 13; vspeed = (int) args[4] << 13; while((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { newMobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, moType); if(newMobj->info->seesound) { S_StartSound(newMobj->info->seesound, newMobj); } newMobj->target = mobj; // Originator newMobj->angle = angle; newMobj->momx = FixedMul(speed, finecosine[fineAngle]); newMobj->momy = FixedMul(speed, finesine[fineAngle]); newMobj->momz = vspeed; newMobj->flags2 |= MF2_DROPPED; // Don't respawn if(gravity == true) { newMobj->flags &= ~MF_NOGRAVITY; newMobj->flags2 |= MF2_LOGRAV; } if(P_CheckMissileSpawn(newMobj) == true) { success = true; } } return success; }
DEFINE_ACTION_FUNCTION(AActor, A_MntrFloorFire) { PARAM_ACTION_PROLOGUE; AActor *mo; self->SetZ(self->floorz); double x = pr_fire.Random2() / 64.; double y = pr_fire.Random2() / 64.; mo = Spawn("MinotaurFX3", self->Vec2OffsetZ(x, y, self->floorz), ALLOW_REPLACE); mo->target = self->target; mo->Vel.X = MinVel; // Force block checking P_CheckMissileSpawn (mo, self->radius); return 0; }
DEFINE_ACTION_FUNCTION(AActor, A_LichIceImpact) { int i; angle_t angle; AActor *shard; for (i = 0; i < 8; i++) { shard = Spawn("HeadFX2", self->x, self->y, self->z, ALLOW_REPLACE); angle = i*ANG45; shard->target = self->target; shard->angle = angle; angle >>= ANGLETOFINESHIFT; shard->velx = FixedMul (shard->Speed, finecosine[angle]); shard->vely = FixedMul (shard->Speed, finesine[angle]); shard->velz = -FRACUNIT*6/10; P_CheckMissileSpawn (shard); } }
DEFINE_ACTION_FUNCTION(AActor, A_VolcanoBlast) { int i; int count; AActor *blast; angle_t angle; count = 1 + (pr_blast() % 3); for (i = 0; i < count; i++) { blast = Spawn("VolcanoBlast", self->x, self->y, self->z + 44*FRACUNIT, ALLOW_REPLACE); blast->target = self; angle = pr_blast () << 24; blast->angle = angle; angle >>= ANGLETOFINESHIFT; blast->velx = FixedMul (1*FRACUNIT, finecosine[angle]); blast->vely = FixedMul (1*FRACUNIT, finesine[angle]); blast->velz = (FRACUNIT*5/2) + (pr_blast() << 10); S_Sound (blast, CHAN_BODY, "world/volcano/shoot", 1, ATTN_NORM); P_CheckMissileSpawn (blast, self->radius); } }
void A_VolcanoBlast (AActor *volcano) { int i; int count; AActor *blast; angle_t angle; count = 1 + (pr_blast() % 3); for (i = 0; i < count; i++) { blast = Spawn<AVolcanoBlast> (volcano->x, volcano->y, volcano->z + 44*FRACUNIT, ALLOW_REPLACE); blast->target = volcano; angle = pr_blast () << 24; blast->angle = angle; angle >>= ANGLETOFINESHIFT; blast->momx = FixedMul (1*FRACUNIT, finecosine[angle]); blast->momy = FixedMul (1*FRACUNIT, finesine[angle]); blast->momz = (FRACUNIT*5/2) + (pr_blast() << 10); S_Sound (blast, CHAN_BODY, "world/volcano/shoot", 1, ATTN_NORM); P_CheckMissileSpawn (blast); } }
mobj_t *Mobj_ExplodeIfObstructed(mobj_t *mob) { return P_CheckMissileSpawn(mob)? mob : nullptr; }
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; }
// The second half of random spawning. Now that the spawner is initialized, the // real actor can be created. If the following code were in BeginPlay instead, // missiles would not have yet obtained certain information that is absolutely // necessary to them -- such as their source and destination. void PostBeginPlay() { AActor * newmobj = NULL; bool boss = false; Super::PostBeginPlay(); if (Species == NAME_None) { Destroy(); return; } const PClass * cls = PClass::FindClass(Species); if (this->flags & MF_MISSILE && target && target->target) // Attempting to spawn a missile. { if ((tracer == NULL) && (flags2 & MF2_SEEKERMISSILE)) tracer = target->target; newmobj = P_SpawnMissileXYZ(x, y, z, target, target->target, cls, false); } else newmobj = Spawn(cls, x, y, z, NO_REPLACE); if (newmobj != NULL) { // copy everything relevant newmobj->SpawnAngle = newmobj->angle = angle; newmobj->SpawnPoint[2] = SpawnPoint[2]; newmobj->special = special; newmobj->args[0] = args[0]; newmobj->args[1] = args[1]; newmobj->args[2] = args[2]; newmobj->args[3] = args[3]; newmobj->args[4] = args[4]; newmobj->special1 = special1; newmobj->special2 = special2; newmobj->SpawnFlags = SpawnFlags; newmobj->HandleSpawnFlags(); newmobj->tid = tid; newmobj->AddToHash(); newmobj->velx = velx; newmobj->vely = vely; newmobj->velz = velz; newmobj->master = master; // For things such as DamageMaster/DamageChildren, transfer mastery. newmobj->target = target; newmobj->tracer = tracer; newmobj->CopyFriendliness(this, false); // This handles things such as projectiles with the MF4_SPECTRAL flag that have // a health set to -2 after spawning, for internal reasons. if (health != SpawnHealth()) newmobj->health = health; if (!(flags & MF_DROPPED)) newmobj->flags &= ~MF_DROPPED; // Handle special altitude flags if (newmobj->flags & MF_SPAWNCEILING) { newmobj->z = newmobj->ceilingz - newmobj->height - SpawnPoint[2]; } else if (newmobj->flags2 & MF2_SPAWNFLOAT) { fixed_t space = newmobj->ceilingz - newmobj->height - newmobj->floorz; if (space > 48*FRACUNIT) { space -= 40*FRACUNIT; newmobj->z = MulScale8 (space, pr_randomspawn()) + newmobj->floorz + 40*FRACUNIT; } newmobj->z += SpawnPoint[2]; } if (newmobj->flags & MF_MISSILE) P_CheckMissileSpawn(newmobj); // Bouncecount is used to count how many recursions we're in. if (newmobj->IsKindOf(PClass::FindClass("RandomSpawner"))) newmobj->bouncecount = ++bouncecount; // If the spawned actor has either of those flags, it's a boss. if ((newmobj->flags4 & MF4_BOSSDEATH) || (newmobj->flags2 & MF2_BOSS)) boss = true; // If a replaced actor has either of those same flags, it's also a boss. AActor * rep = GetDefaultByType(GetClass()->ActorInfo->GetReplacee()->Class); if (rep && ((rep->flags4 & MF4_BOSSDEATH) || (rep->flags2 & MF2_BOSS))) boss = true; // [BB] If we're the server, tell clients to spawn the actor. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { SERVERCOMMANDS_SpawnThing( newmobj ); // [BB] Also set the angle and momentum if necessary. SERVER_SetThingNonZeroAngleAndMomentum( newmobj ); } // [BB] The client did the spawning, so this has to be a client side only actor. else if ( ( NETWORK_GetState( ) == NETSTATE_CLIENT ) || ( CLIENTDEMO_IsPlaying( ) ) ) newmobj->ulNetworkFlags |= NETFL_CLIENTSIDEONLY; } if (boss) this->tracer = newmobj; // [BB] Only destroy the actor if it's not needed for a map reset. Otherwise just hide it. else HideOrDestroyIfSafe(); // "else" because a boss-replacing spawner must wait until it can call A_BossDeath. // [BB] Workaround to ensure that the spawner is properly reset in GAME_ResetMap. this->ulSTFlags |= STFL_POSITIONCHANGED; }
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_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; }
// The second half of random spawning. Now that the spawner is initialized, the // real actor can be created. If the following code were in BeginPlay instead, // missiles would not have yet obtained certain information that is absolutely // necessary to them -- such as their source and destination. void PostBeginPlay() { AActor * newmobj = NULL; bool boss = false; Super::PostBeginPlay(); if (Species == NAME_None) { Destroy(); return; } const PClass * cls = PClass::FindClass(Species); if (this->flags & MF_MISSILE && target && target->target) // Attempting to spawn a missile. { if ((tracer == NULL) && (flags2 & MF2_SEEKERMISSILE)) tracer = target->target; newmobj = P_SpawnMissileXYZ(Pos(), target, target->target, cls, false); } else newmobj = Spawn(cls, Pos(), NO_REPLACE); if (newmobj != NULL) { // copy everything relevant newmobj->SpawnAngle = newmobj->angle = angle; newmobj->SpawnPoint[2] = SpawnPoint[2]; newmobj->special = special; newmobj->args[0] = args[0]; newmobj->args[1] = args[1]; newmobj->args[2] = args[2]; newmobj->args[3] = args[3]; newmobj->args[4] = args[4]; newmobj->special1 = special1; newmobj->special2 = special2; newmobj->SpawnFlags = SpawnFlags & ~MTF_SECRET; // MTF_SECRET needs special treatment to avoid incrementing the secret counter twice. It had already been processed for the spawner itself. newmobj->HandleSpawnFlags(); newmobj->SpawnFlags = SpawnFlags; newmobj->tid = tid; newmobj->AddToHash(); newmobj->velx = velx; newmobj->vely = vely; newmobj->velz = velz; newmobj->master = master; // For things such as DamageMaster/DamageChildren, transfer mastery. newmobj->target = target; newmobj->tracer = tracer; newmobj->CopyFriendliness(this, false); // This handles things such as projectiles with the MF4_SPECTRAL flag that have // a health set to -2 after spawning, for internal reasons. if (health != SpawnHealth()) newmobj->health = health; if (!(flags & MF_DROPPED)) newmobj->flags &= ~MF_DROPPED; // Handle special altitude flags if (newmobj->flags & MF_SPAWNCEILING) { newmobj->SetZ(newmobj->ceilingz - newmobj->height - SpawnPoint[2]); } else if (newmobj->flags2 & MF2_SPAWNFLOAT) { fixed_t space = newmobj->ceilingz - newmobj->height - newmobj->floorz; if (space > 48*FRACUNIT) { space -= 40*FRACUNIT; newmobj->SetZ(MulScale8 (space, pr_randomspawn()) + newmobj->floorz + 40*FRACUNIT); } newmobj->AddZ(SpawnPoint[2]); } if (newmobj->flags & MF_MISSILE) P_CheckMissileSpawn(newmobj, 0); // Bouncecount is used to count how many recursions we're in. if (newmobj->IsKindOf(PClass::FindClass("RandomSpawner"))) newmobj->bouncecount = ++bouncecount; // If the spawned actor has either of those flags, it's a boss. if ((newmobj->flags4 & MF4_BOSSDEATH) || (newmobj->flags2 & MF2_BOSS)) boss = true; // If a replaced actor has either of those same flags, it's also a boss. AActor * rep = GetDefaultByType(GetClass()->ActorInfo->GetReplacee()->Class); if (rep && ((rep->flags4 & MF4_BOSSDEATH) || (rep->flags2 & MF2_BOSS))) boss = true; } if (boss) this->tracer = newmobj; else // "else" because a boss-replacing spawner must wait until it can call A_BossDeath. Destroy(); }
DEFINE_ACTION_FUNCTION(AActor, A_LichAttack) { int i; AActor *fire; AActor *baseFire; AActor *mo; AActor *target; int randAttack; static const int atkResolve1[] = { 50, 150 }; static const int atkResolve2[] = { 150, 200 }; int dist; // Ice ball (close 20% : far 60%) // Fire column (close 40% : far 20%) // Whirlwind (close 40% : far 20%) // Distance threshold = 8 cells target = self->target; if (target == NULL) { return; } A_FaceTarget (self); if (self->CheckMeleeRange ()) { int damage = pr_atk.HitDice (6); P_DamageMobj (target, self, self, damage, NAME_Melee); P_TraceBleed (damage, target, self); return; } dist = P_AproxDistance (self->x-target->x, self->y-target->y) > 8*64*FRACUNIT; randAttack = pr_atk (); if (randAttack < atkResolve1[dist]) { // Ice ball P_SpawnMissile (self, target, PClass::FindClass("HeadFX1")); S_Sound (self, CHAN_BODY, "ironlich/attack2", 1, ATTN_NORM); } else if (randAttack < atkResolve2[dist]) { // Fire column baseFire = P_SpawnMissile (self, target, PClass::FindClass("HeadFX3")); if (baseFire != NULL) { baseFire->SetState (baseFire->FindState("NoGrow")); for (i = 0; i < 5; i++) { fire = Spawn("HeadFX3", baseFire->x, baseFire->y, baseFire->z, ALLOW_REPLACE); if (i == 0) { S_Sound (self, CHAN_BODY, "ironlich/attack1", 1, ATTN_NORM); } fire->target = baseFire->target; fire->angle = baseFire->angle; fire->velx = baseFire->velx; fire->vely = baseFire->vely; fire->velz = baseFire->velz; fire->Damage = 0; fire->health = (i+1) * 2; P_CheckMissileSpawn (fire); } } } else { // Whirlwind mo = P_SpawnMissile (self, target, RUNTIME_CLASS(AWhirlwind)); if (mo != NULL) { mo->z -= 32*FRACUNIT; mo->tracer = target; mo->special1 = 60; mo->special2 = 50; // Timer for active sound mo->health = 20*TICRATE; // Duration S_Sound (self, CHAN_BODY, "ironlich/attack3", 1, ATTN_NORM); } } }