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_SpectreChunkLarge) { // [BB] Clients may not do this. if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) || ( CLIENTDEMO_IsPlaying( ))) return; AActor *foo = Spawn("AlienChunkLarge", self->x, self->y, self->z + 10*FRACUNIT, ALLOW_REPLACE); if (foo != NULL) { int t; t = pr_spectrechunk() & 7; foo->momx = (t - (pr_spectrechunk() & 15)) << FRACBITS; t = pr_spectrechunk() & 7; foo->momy = (t - (pr_spectrechunk() & 15)) << FRACBITS; foo->momz = (pr_spectrechunk() & 7) << FRACBITS; // [BB] Tell clients to spawn the actor. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_SpawnThing( foo ); } }
DEFINE_ACTION_FUNCTION(AActor, A_SpectreChunkSmall) { // [BB] Clients may not do this. if ( NETWORK_InClientMode() ) return; AActor *foo = Spawn("AlienChunkSmall", self->x, self->y, self->z + 10*FRACUNIT, ALLOW_REPLACE); if (foo != NULL) { int t; t = pr_spectrechunk() & 15; foo->velx = (t - (pr_spectrechunk() & 7)) << FRACBITS; t = pr_spectrechunk() & 15; foo->vely = (t - (pr_spectrechunk() & 7)) << FRACBITS; foo->velz = (pr_spectrechunk() & 15) << FRACBITS; // [BB] Tell clients to spawn the actor. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_SpawnThing( foo ); } }
DEFINE_ACTION_FUNCTION(AActor, A_BishopSpawnBlur) { // [BB] This is server-side. if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) || ( CLIENTDEMO_IsPlaying( ))) { return; } AActor *mo; if (!--self->special1) { self->momx = 0; self->momy = 0; // [BB] If we're the server, update the thing's momentum. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_MoveThingExact( self, CM_MOMX|CM_MOMY ); if (pr_sblur() > 96) { // [BB] If we're the server, tell the clients of the state change. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_SetThingState( self, STATE_SEE ); self->SetState (self->SeeState); } else { // [BB] If we're the server, tell the clients of the state change. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_SetThingState( self, STATE_MISSILE ); self->SetState (self->MissileState); } } mo = Spawn ("BishopBlur", self->x, self->y, self->z, ALLOW_REPLACE); if (mo) { mo->angle = self->angle; // [BB] If we're the server, tell the clients to spawn the thing and set its angle. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { SERVERCOMMANDS_SpawnThing( mo ); SERVERCOMMANDS_SetThingAngle( mo ); } } }
DEFINE_ACTION_FUNCTION(AActor, A_BishopPainBlur) { AActor *mo; // [BB] This is server-side. if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) || ( CLIENTDEMO_IsPlaying( ))) { return; } if (pr_pain() < 64) { // [BB] If we're the server, tell the clients to update this thing's state. if ( ( NETWORK_GetState( ) == NETSTATE_SERVER ) ) SERVERCOMMANDS_SetThingFrame( self, self->FindState ("Blur") ); self->SetState (self->FindState ("Blur")); return; } fixed_t x = self->x + (pr_pain.Random2()<<12); fixed_t y = self->y + (pr_pain.Random2()<<12); fixed_t z = self->z + (pr_pain.Random2()<<11); mo = Spawn ("BishopPainBlur", x, y, z, ALLOW_REPLACE); if (mo) { mo->angle = self->angle; // [BB] If we're the server, tell the clients to spawn the thing and set its angle. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { SERVERCOMMANDS_SpawnThing( mo ); SERVERCOMMANDS_SetThingAngle( mo ); } } }
static void SpawnFly(AActor *self, const PClass *spawntype, FSoundID sound) { AActor *newmobj; AActor *fog = NULL; AActor *eye = self->master; // The eye is the spawnshot's master, not the target! AActor *targ = self->target; // Unlike other projectiles, the target is the intended destination. int r; // [BC] Brain spitting is server-side. if ( NETWORK_InClientMode() ) { return; } // [GZ] Should be more viable than a countdown... if (self->special2 != 0) { if (self->special2 > level.maptime) return; // still flying } else { if (self->reactiontime == 0 || --self->reactiontime != 0) return; // still flying } if (spawntype != NULL) { fog = Spawn (spawntype, targ->x, targ->y, targ->z, ALLOW_REPLACE); if (fog != NULL) S_Sound (fog, CHAN_BODY, sound, 1, ATTN_NORM); } // [BC] If we're the server, spawn the fire, and tell clients to play the sound. if (( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( fog )) { SERVERCOMMANDS_SpawnThing( fog ); SERVERCOMMANDS_SoundPoint( fog->x, fog->y, fog->z, CHAN_BODY, "brain/spawn", 1, ATTN_NORM ); } FName SpawnName; FDropItem *di; // di will be our drop item list iterator FDropItem *drop; // while drop stays as the reference point. int n = 0; // First see if this cube has its own actor list drop = self->GetDropItems(); // If not, then default back to its master's list if (drop == NULL && eye != NULL) drop = eye->GetDropItems(); if (drop != NULL) { for (di = drop; di != NULL; di = di->Next) { if (di->Name != NAME_None) { if (di->amount < 0) { di->amount = 1; // default value is -1, we need a positive value. } n += di->amount; // this is how we can weight the list. } } di = drop; n = pr_spawnfly(n); while (n >= 0) { if (di->Name != NAME_None) { n -= di->amount; // logically, none of the -1 values have survived by now. } if ((di->Next != NULL) && (n >= 0)) { di = di->Next; } else { n = -1; } } SpawnName = di->Name; } if (SpawnName == NAME_None) { // Randomly select monster to spawn. r = pr_spawnfly (); // Probability distribution (kind of :), // decreasing likelihood. if (r < 50) SpawnName = "DoomImp"; else if (r < 90) SpawnName = "Demon"; else if (r < 120) SpawnName = "Spectre"; else if (r < 130) SpawnName = "PainElemental"; else if (r < 160) SpawnName = "Cacodemon"; else if (r < 162) SpawnName = "Archvile"; else if (r < 172) SpawnName = "Revenant"; else if (r < 192) SpawnName = "Arachnotron"; else if (r < 222) SpawnName = "Fatso"; else if (r < 246) SpawnName = "HellKnight"; else SpawnName = "BaronOfHell"; } spawntype = PClass::FindClass(SpawnName); if (spawntype != NULL) { newmobj = Spawn (spawntype, targ->x, targ->y, targ->z, ALLOW_REPLACE); if (newmobj != NULL) { // Make the new monster hate what the boss eye hates if (eye != NULL) { newmobj->CopyFriendliness (eye, false); } // Make it act as if it was around when the player first made noise // (if the player has made noise). newmobj->LastHeard = newmobj->Sector->SoundTarget; if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true, NULL)) { newmobj->SetState (newmobj->SeeState); } if (!(newmobj->ObjectFlags & OF_EuthanizeMe)) { // telefrag anything in this spot P_TeleportMove (newmobj, newmobj->x, newmobj->y, newmobj->z, true); // [BC] If we're the server, tell clients to spawn the new monster. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { SERVERCOMMANDS_SpawnThing( newmobj ); if ( newmobj->state == newmobj->SeeState ) SERVERCOMMANDS_SetThingState( newmobj, STATE_SEE ); } } newmobj->flags4 |= MF4_BOSSSPAWNED; } } // [BC] Tell clients to destroy the cube. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_DestroyThing( self ); // remove self (i.e., cube). self->Destroy (); }
DEFINE_ACTION_FUNCTION(AActor, A_Beacon) { AActor *owner = self->target; AActor *rebel; angle_t an; // [BC] AActor *pFog; // [BC] This is handled server-side. if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) || ( CLIENTDEMO_IsPlaying( ))) { return; } rebel = Spawn("Rebel1", self->x, self->y, self->floorz, ALLOW_REPLACE); if (!P_TryMove (rebel, rebel->x, rebel->y, true)) { rebel->Destroy (); return; } // Once the rebels start teleporting in, you can't pick up the beacon anymore. self->flags &= ~MF_SPECIAL; static_cast<AInventory *>(self)->DropTime = 0; // Set up the new rebel. rebel->threshold = BASETHRESHOLD; rebel->target = NULL; rebel->flags4 |= MF4_INCOMBAT; rebel->LastHeard = owner; // Make sure the rebels look for targets if (deathmatch) { rebel->health *= 2; } if (owner != NULL) { // Rebels are the same color as their owner (but only in multiplayer) // [BB] Changed "multiplayer" check if ( NETWORK_GetState( ) != NETSTATE_SINGLE ) { rebel->Translation = owner->Translation; } rebel->FriendPlayer = owner->player != NULL ? BYTE(owner->player - players + 1) : 0; // Set the rebel's target to whatever last hurt the player, so long as it's not // one of the player's other rebels. if (owner->target != NULL && !rebel->IsFriend (owner->target)) { rebel->target = owner->target; } } // [BC] Spawn the rebel, and put him in the see state. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { SERVERCOMMANDS_SpawnThing( rebel ); SERVERCOMMANDS_SetThingState( rebel, STATE_SEE ); } rebel->SetState (rebel->SeeState); rebel->angle = self->angle; an = self->angle >> ANGLETOFINESHIFT; pFog = Spawn<ATeleportFog> (rebel->x + 20*finecosine[an], rebel->y + 20*finesine[an], rebel->z + TELEFOGHEIGHT, ALLOW_REPLACE); // [BC] Spawn the teleport fog. if (( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( pFog )) { SERVERCOMMANDS_SpawnThing( pFog ); } if (--self->health < 0) { // [BB] Tell clients to set the thing's state. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_SetThingState( self, STATE_DEATH ); self->SetState(self->FindState(NAME_Death)); } }
// 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; }
// // A_PainShootSkull // Spawn a lost soul and launch it at the target // void A_PainShootSkull (AActor *self, angle_t angle) { fixed_t x, y, z; AActor *other; angle_t an; int prestep; // [BC] Spawning of additional lost souls is server-side. if ( NETWORK_GetState( ) == NETSTATE_CLIENT ) return; const PClass *spawntype = NULL; int index=CheckIndex(1, NULL); if (index>=0) { spawntype = PClass::FindClass((ENamedName)StateParameters[index]); } if (spawntype == NULL) spawntype = RUNTIME_CLASS(ALostSoul); // [RH] check to make sure it's not too close to the ceiling if (self->z + self->height + 8*FRACUNIT > self->ceilingz) { if (self->flags & MF_FLOAT) { self->momz -= 2*FRACUNIT; self->flags |= MF_INFLOAT; self->flags4 |= MF4_VFRICTION; } return; } // [RH] make this optional if (i_compatflags & COMPATF_LIMITPAIN) { // count total number of skulls currently on the level // if there are already 20 skulls on the level, don't spit another one int count = 20; FThinkerIterator iterator (spawntype); DThinker *othink; while ( (othink = iterator.Next ()) ) { if (--count == 0) return; } } // okay, there's room for another one an = angle >> ANGLETOFINESHIFT; prestep = 4*FRACUNIT + 3*(self->radius + GetDefaultByType(spawntype)->radius)/2; x = self->x + FixedMul (prestep, finecosine[an]); y = self->y + FixedMul (prestep, finesine[an]); z = self->z + 8*FRACUNIT; // Check whether the Lost Soul is being fired through a 1-sided // phares // wall or an impassible line, or a "monsters can't cross" line.// | // If it is, then we don't allow the spawn. // V if (Check_Sides (self, x, y)) { return; } other = Spawn (spawntype, x, y, z, ALLOW_REPLACE); // [BC] If we're the server, tell clients to spawn the actor. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_SpawnThing( other ); // Check to see if the new Lost Soul's z value is above the // ceiling of its new sector, or below the floor. If so, kill it. if ((other->z > (other->Sector->ceilingplane.ZatPoint (other->x, other->y) - other->height)) || (other->z < other->Sector->floorplane.ZatPoint (other->x, other->y))) { // kill it immediately P_DamageMobj (other, self, self, 1000000, MOD_UNKNOWN); // ^ return; // | } // phares // Check for movements. if (!P_CheckPosition (other, other->x, other->y)) { // kill it immediately P_DamageMobj (other, self, self, 1000000, MOD_UNKNOWN); return; } // [RH] Lost souls hate the same things as their pain elementals other->CopyFriendliness (self, true); A_SkullAttack (other); }