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; }
// 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; }
// 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(); }
// // P_SpawnMapThing // The fields of the mapthing should // already be in host byte order. // // [RH] position is used to weed out unwanted start spots // void P_SpawnMapThing (mapthing2_t *mthing, int position) { int i; int bit; AActor *mobj; fixed_t x, y, z; if (mthing->type == 0 || mthing->type == -1) return; // count deathmatch start positions if (mthing->type == 11 || ((mthing->type == 5080 || mthing->type == 5081 || mthing->type == 5082)) && !sv_teamspawns) { if (deathmatch_p == &deathmatchstarts[MaxDeathmatchStarts]) { // [RH] Get more deathmatchstarts int offset = MaxDeathmatchStarts; MaxDeathmatchStarts *= 2; deathmatchstarts = (mapthing2_t *)Realloc (deathmatchstarts, MaxDeathmatchStarts * sizeof(mapthing2_t)); deathmatch_p = &deathmatchstarts[offset]; } memcpy (deathmatch_p, mthing, sizeof(*mthing)); deathmatch_p++; return; } // [Toke - CTF - starts] CTF starts - count Blue team start positions if (mthing->type == 5080 && sv_teamspawns) { if (blueteam_p == &blueteamstarts[MaxBlueTeamStarts]) { int offset = MaxBlueTeamStarts; MaxBlueTeamStarts *= 2; blueteamstarts = (mapthing2_t *)Realloc (blueteamstarts, MaxBlueTeamStarts * sizeof(mapthing2_t)); blueteam_p = &blueteamstarts[offset]; } memcpy (blueteam_p, mthing, sizeof(*mthing)); blueteam_p++; return; } // [Toke - CTF - starts] CTF starts - count Red team start positions if (mthing->type == 5081 && sv_teamspawns) { if (redteam_p == &redteamstarts[MaxRedTeamStarts]) { int offset = MaxRedTeamStarts; MaxRedTeamStarts *= 2; redteamstarts = (mapthing2_t *)Realloc (redteamstarts, MaxRedTeamStarts * sizeof(mapthing2_t)); redteam_p = &redteamstarts[offset]; } memcpy (redteam_p, mthing, sizeof(*mthing)); redteam_p++; return; } // [RH] Record polyobject-related things if (HexenHack) { switch (mthing->type) { case PO_HEX_ANCHOR_TYPE: mthing->type = PO_ANCHOR_TYPE; break; case PO_HEX_SPAWN_TYPE: mthing->type = PO_SPAWN_TYPE; break; case PO_HEX_SPAWNCRUSH_TYPE: mthing->type = PO_SPAWNCRUSH_TYPE; break; } } if (mthing->type == PO_ANCHOR_TYPE || mthing->type == PO_SPAWN_TYPE || mthing->type == PO_SPAWNCRUSH_TYPE) { polyspawns_t *polyspawn = new polyspawns_t; polyspawn->next = polyspawns; polyspawn->x = mthing->x << FRACBITS; polyspawn->y = mthing->y << FRACBITS; polyspawn->angle = mthing->angle; polyspawn->type = mthing->type; polyspawns = polyspawn; if (mthing->type != PO_ANCHOR_TYPE) po_NumPolyobjs++; return; } // check for players specially if ((mthing->type <= 4 && mthing->type > 0) || (mthing->type >= 4001 && mthing->type <= 4001 + MAXPLAYERSTARTS - 4)) { // [RH] Only spawn spots that match position. if (mthing->args[0] != position) return; playerstarts.push_back(*mthing); return; } // [RH] sound sequence overrides if (mthing->type >= 1400 && mthing->type < 1410) { return; } else if (mthing->type == 1411) { int type; if (mthing->args[0] == 255) type = -1; else type = mthing->args[0]; if (type > 63) { Printf (PRINT_HIGH, "Sound sequence %d out of range\n", type); } else { } return; } /*if (deathmatch) { if (!(mthing->flags & MTF_DEATHMATCH)) return; } else if (multiplayer) { if (!(mthing->flags & MTF_COOPERATIVE)) return; } if (!multiplayer) { if (!(mthing->flags & MTF_SINGLE)) return; }*/ // GhostlyDeath -- Correctly spawn things if (sv_gametype != GM_COOP && !(mthing->flags & MTF_DEATHMATCH)) return; if (sv_gametype == GM_COOP && sv_maxplayers == 1 && !(mthing->flags & MTF_SINGLE)) return; if (sv_gametype == GM_COOP && sv_maxplayers != 1 && !(mthing->flags & MTF_COOPERATIVE)) return; // check for apropriate skill level if (sv_skill == sk_baby) bit = 1; else if (sv_skill == sk_nightmare) bit = 4; else bit = 1 << ((int)sv_skill - 2); if (!(mthing->flags & bit)) return; // [RH] Determine if it is an old ambient thing, and if so, // map it to MT_AMBIENT with the proper parameter. if (mthing->type >= 14001 && mthing->type <= 14064) { mthing->args[0] = mthing->type - 14000; mthing->type = 14065; i = MT_AMBIENT; } // [RH] Check if it's a particle fountain else if (mthing->type >= 9027 && mthing->type <= 9033) { mthing->args[0] = mthing->type - 9026; i = MT_FOUNTAIN; } else { // find which type to spawn for (i = 0; i < NUMMOBJTYPES; i++) if (mthing->type == mobjinfo[i].doomednum) break; } if (i >= NUMMOBJTYPES) { // [RH] Don't die if the map tries to spawn an unknown thing Printf (PRINT_HIGH, "Unknown type %i at (%i, %i)\n", mthing->type, mthing->x, mthing->y); i = MT_UNKNOWNTHING; } // [RH] If the thing's corresponding sprite has no frames, also map // it to the unknown thing. else if (sprites[states[mobjinfo[i].spawnstate].sprite].numframes == 0) { Printf (PRINT_HIGH, "Type %i at (%i, %i) has no frames\n", mthing->type, mthing->x, mthing->y); i = MT_UNKNOWNTHING; } // don't spawn keycards and players in deathmatch if (sv_gametype != GM_COOP && mobjinfo[i].flags & MF_NOTDMATCH) return; // don't spawn deathmatch weapons in offline single player mode if (!multiplayer) { switch (i) { case MT_CHAINGUN: case MT_SHOTGUN: case MT_SUPERSHOTGUN: case MT_MISC25: // BFG case MT_MISC26: // chainsaw case MT_MISC27: // rocket launcher case MT_MISC28: // plasma gun if ((mthing->flags & (MTF_DEATHMATCH|MTF_SINGLE)) == MTF_DEATHMATCH) return; break; default: break; } } if (sv_nomonsters) if (i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL) ) return; // spawn it x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (i == MT_WATERZONE) { sector_t *sec = R_PointInSubsector (x, y)->sector; sec->waterzone = 1; return; } else if (i == MT_SECRETTRIGGER) { level.total_secrets++; } if (mobjinfo[i].flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mobj = new AActor (x, y, z, (mobjtype_t)i); if (z == ONFLOORZ) mobj->z += mthing->z << FRACBITS; else if (z == ONCEILINGZ) mobj->z -= mthing->z << FRACBITS; mobj->spawnpoint = *mthing; if (mobj->flags2 & MF2_FLOATBOB) { // Seed random starting index for bobbing motion mobj->health = M_Random(); mobj->special1 = mthing->z << FRACBITS; } // [RH] Set the thing's special mobj->special = mthing->special; memcpy (mobj->args, mthing->args, sizeof(mobj->args)); if (mobj->tics > 0) mobj->tics = 1 + (P_Random () % mobj->tics); if (mobj->flags & MF_COUNTKILL) level.total_monsters++; if (mobj->flags & MF_COUNTITEM) level.total_items++; if (i != MT_SPARK) mobj->angle = ANG45 * (mthing->angle/45); if (mthing->flags & MTF_AMBUSH) mobj->flags |= MF_AMBUSH; // [RH] Add ThingID to mobj and link it in with the others mobj->tid = mthing->thingid; mobj->AddToHash (); SV_SpawnMobj(mobj); if (sv_gametype == GM_CTF) { // [Toke - CTF] Setup flag sockets if (mthing->type == ID_BLUE_FLAG) { flagdata *data = &CTFdata[it_blueflag]; if (data->flaglocated) return; CTF_RememberFlagPos (mthing); CTF_SpawnFlag(it_blueflag); } if (mthing->type == ID_RED_FLAG) { flagdata *data = &CTFdata[it_redflag]; if (data->flaglocated) return; CTF_RememberFlagPos (mthing); CTF_SpawnFlag(it_redflag); } } // [RH] Go dormant as needed if (mthing->flags & MTF_DORMANT) P_DeactivateMobj (mobj); }