PClassActor *PClassActor::GetReplacement(bool lookskill) { FName skillrepname; if (lookskill && AllSkills.Size() > (unsigned)gameskill) { skillrepname = AllSkills[gameskill].GetReplacement(TypeName); if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) { Printf("Warning: incorrect actor name in definition of skill %s: \n" "class %s is replaced by non-existent class %s\n" "Skill replacement will be ignored for this actor.\n", AllSkills[gameskill].Name.GetChars(), TypeName.GetChars(), skillrepname.GetChars()); AllSkills[gameskill].SetReplacement(TypeName, NAME_None); AllSkills[gameskill].SetReplacedBy(skillrepname, NAME_None); lookskill = false; skillrepname = NAME_None; } } if (Replacement == NULL && (!lookskill || skillrepname == NAME_None)) { return this; } // The Replacement field is temporarily NULLed to prevent // potential infinite recursion. PClassActor *savedrep = Replacement; Replacement = NULL; PClassActor *rep = savedrep; // Handle skill-based replacement here. It has precedence on DECORATE replacement // in that the skill replacement is applied first, followed by DECORATE replacement // on the actor indicated by the skill replacement. if (lookskill && (skillrepname != NAME_None)) { rep = PClass::FindActor(skillrepname); } // Now handle DECORATE replacement chain // Skill replacements are not recursive, contrarily to DECORATE replacements rep = rep->GetReplacement(false); // Reset the temporarily NULLed field Replacement = savedrep; return rep; }
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; }
void cht_Give (player_t *player, const char *name, int amount) { enum { ALL_NO, ALL_YES, ALL_YESYES } giveall; int i; PClassActor *type; if (player != &players[consoleplayer]) Printf ("%s is a cheater: give %s\n", player->userinfo.GetName(), name); if (player->mo == NULL || player->health <= 0) { return; } giveall = ALL_NO; if (stricmp (name, "all") == 0) { giveall = ALL_YES; } else if (stricmp (name, "everything") == 0) { giveall = ALL_YESYES; } if (stricmp (name, "health") == 0) { if (amount > 0) { player->mo->health += amount; player->health = player->mo->health; } else { player->health = player->mo->health = player->mo->GetMaxHealth(); } } if (giveall || stricmp (name, "backpack") == 0) { // Select the correct type of backpack based on the game type = PClass::FindActor(gameinfo.backpacktype); if (type != NULL) { player->mo->GiveInventory(static_cast<PClassInventory *>(type), 1, true); } if (!giveall) return; } if (giveall || stricmp (name, "ammo") == 0) { // Find every unique type of ammo. Give it to the player if // he doesn't have it already, and set each to its maximum. for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { PClassActor *type = PClassActor::AllActorClasses[i]; if (type->ParentClass == RUNTIME_CLASS(AAmmo)) { PClassAmmo *atype = static_cast<PClassAmmo *>(type); AInventory *ammo = player->mo->FindInventory(atype); if (ammo == NULL) { ammo = static_cast<AInventory *>(Spawn (atype)); ammo->AttachToOwner (player->mo); ammo->Amount = ammo->MaxAmount; } else if (ammo->Amount < ammo->MaxAmount) { ammo->Amount = ammo->MaxAmount; } } } if (!giveall) return; } if (giveall || stricmp (name, "armor") == 0) { if (gameinfo.gametype != GAME_Hexen) { ABasicArmorPickup *armor = Spawn<ABasicArmorPickup> (); armor->SaveAmount = 100*deh.BlueAC; armor->SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5; if (!armor->CallTryPickup (player->mo)) { armor->Destroy (); } } else { for (i = 0; i < 4; ++i) { AHexenArmor *armor = Spawn<AHexenArmor> (); armor->health = i; armor->Amount = 0; if (!armor->CallTryPickup (player->mo)) { armor->Destroy (); } } } if (!giveall) return; } if (giveall || stricmp (name, "keys") == 0) { for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { if (PClassActor::AllActorClasses[i]->IsDescendantOf (RUNTIME_CLASS(AKey))) { AKey *key = (AKey *)GetDefaultByType (PClassActor::AllActorClasses[i]); if (key->KeyNumber != 0) { key = static_cast<AKey *>(Spawn(static_cast<PClassActor *>(PClassActor::AllActorClasses[i]))); if (!key->CallTryPickup (player->mo)) { key->Destroy (); } } } } if (!giveall) return; } if (giveall || stricmp (name, "weapons") == 0) { AWeapon *savedpending = player->PendingWeapon; for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { type = PClassActor::AllActorClasses[i]; // Don't give replaced weapons unless the replacement was done by Dehacked. if (type != RUNTIME_CLASS(AWeapon) && type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) && (static_cast<PClassActor *>(type)->GetReplacement() == type || static_cast<PClassActor *>(type)->GetReplacement()->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup)))) { // Give the weapon only if it belongs to the current game or if (player->weapons.LocateWeapon(static_cast<PClassWeapon*>(type), NULL, NULL)) { AWeapon *def = (AWeapon*)GetDefaultByType (type); if (giveall == ALL_YESYES || !(def->WeaponFlags & WIF_CHEATNOTWEAPON)) { player->mo->GiveInventory(static_cast<PClassInventory *>(type), 1, true); } } } } player->PendingWeapon = savedpending; if (!giveall) return; } if (giveall || stricmp (name, "artifacts") == 0) { for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { type = PClassActor::AllActorClasses[i]; if (type->IsDescendantOf (RUNTIME_CLASS(AInventory))) { AInventory *def = (AInventory*)GetDefaultByType (type); if (def->Icon.isValid() && def->MaxAmount > 1 && !type->IsDescendantOf (RUNTIME_CLASS(APuzzleItem)) && !type->IsDescendantOf (RUNTIME_CLASS(APowerup)) && !type->IsDescendantOf (RUNTIME_CLASS(AArmor))) { // Do not give replaced items unless using "give everything" if (giveall == ALL_YESYES || type->GetReplacement() == type) { player->mo->GiveInventory(static_cast<PClassInventory *>(type), amount <= 0 ? def->MaxAmount : amount, true); } } } } if (!giveall) return; } if (giveall || stricmp (name, "puzzlepieces") == 0) { for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { type = PClassActor::AllActorClasses[i]; if (type->IsDescendantOf (RUNTIME_CLASS(APuzzleItem))) { AInventory *def = (AInventory*)GetDefaultByType (type); if (def->Icon.isValid()) { // Do not give replaced items unless using "give everything" if (giveall == ALL_YESYES || type->GetReplacement() == type) { player->mo->GiveInventory(static_cast<PClassInventory *>(type), amount <= 0 ? def->MaxAmount : amount, true); } } } } if (!giveall) return; } if (giveall) return; type = PClass::FindActor(name); if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AInventory))) { if (player == &players[consoleplayer]) Printf ("Unknown item \"%s\"\n", name); } else { player->mo->GiveInventory(static_cast<PClassInventory *>(type), amount, true); } return; }
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; }