- added RandomSpawner update from Gez's experimental build.

- added thing activation types for BUMPSPECIAL and USESPECIAL. Also added
  a new ClearSpecial flag to the activation type.
- added MBF's code for dogs jumping down, controlled by the MF6_JUMPDOWN
  flag.


SVN r1835 (trunk)
This commit is contained in:
Christoph Oelckers 2009-09-15 14:16:55 +00:00
parent f2a122e076
commit 5910d90a19
9 changed files with 224 additions and 83 deletions

View file

@ -1,4 +1,9 @@
September 15, 2009 (Changes by Graf Zahl)
- added RandomSpawner update from Gez's experimental build.
- added thing activation types for BUMPSPECIAL and USESPECIAL. Also added
a new ClearSpecial flag to the activation type.
- added MBF's code for dogs jumping down, controlled by the MF6_JUMPDOWN
flag.
- fixed: pr_bounce was declared statically in o_mobj.cpp and redeclared externally
in p_map.cpp resulting in a CRC conflict.
- fixed: The Dehacked flags parser fix from May 31 (r1624) was undone by

View file

@ -441,6 +441,7 @@ enum EThingSpecialActivationType
THINGSPEC_TriggerTargets = 4, // The trigger changes its target to the thing
THINGSPEC_MonsterTrigger = 8, // The thing can be triggered by a monster
THINGSPEC_MissileTrigger = 16, // The thing can be triggered by a projectile
THINGSPEC_ClearSpecial = 32, // Clears special after successful activation
};
// [RH] Like msecnode_t, but for the blockmap

View file

@ -1,6 +1,8 @@
/*
** a_randomspawner.cpp
** A thing that randomly spawns one item in a list of many, before disappearing.
** bouncecount is used to keep track of recursions (so as to prevent infinite loops).
** Species is used to store the index of the spawned actor's name.
*/
#include "actor.h"
@ -21,14 +23,16 @@ class ARandomSpawner : public AActor
{
DECLARE_CLASS (ARandomSpawner, AActor)
void PostBeginPlay()
// To handle "RandomSpawning" missiles, the code has to be split in two parts.
// If the following code is not done in BeginPlay, missiles will use the
// random spawner's velocity (0...) instead of their own.
void BeginPlay()
{
AActor *newmobj = NULL;
FDropItem *di; // di will be our drop item list iterator
FDropItem *drop; // while drop stays as the reference point.
int n=0;
Super::PostBeginPlay();
Super::BeginPlay();
drop = di = GetDropItems();
if (di != NULL)
{
@ -46,7 +50,7 @@ class ARandomSpawner : public AActor
// Take a random number...
n = pr_randomspawn(n);
// And iterate in the array up to the random number chosen.
while (n > 0)
while (n > -1)
{
if (di->Name != NAME_None)
{
@ -55,64 +59,109 @@ class ARandomSpawner : public AActor
}
}
// So now we can spawn the dropped item.
if (special1 >= MAX_RANDOMSPAWNERS_RECURSION) // Prevents infinite recursions
if (bouncecount >= MAX_RANDOMSPAWNERS_RECURSION) // Prevents infinite recursions
{
Spawn("Unknown", x, y, z, NO_REPLACE); // Show that there's a problem.
Destroy(); return;
}
else if (pr_randomspawn() <= di->probability) // prob 255 = always spawn, prob 0 = never spawn.
{
newmobj = Spawn(di->Name, x, y, z, ALLOW_REPLACE);
// copy everything relevant
newmobj->SpawnAngle = newmobj->angle = angle;
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->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);
if (!(flags & MF_DROPPED)) newmobj->flags &= ~MF_DROPPED;
// Handle special altitude flags
if (newmobj->flags & MF_SPAWNCEILING)
// Handle replacement here so as to get the proper speed and flags for missiles
const PClass * cls; PClass * rep;
cls = PClass::FindClass(di->Name);
if (cls) rep = cls->ActorInfo->GetReplacement()->Class;
if (rep) cls = rep;
if (cls)
{
newmobj->z = newmobj->ceilingz - newmobj->height;
Species = cls->TypeName;
AActor * defmobj = GetDefaultByType(cls);
this->Speed = defmobj->Speed;
this->flags |= (defmobj->flags & MF_MISSILE);
this->flags2 |= (defmobj->flags2 & MF2_SEEKERMISSILE);
this->flags4 |= (defmobj->flags4 & MF4_SPECTRAL);
}
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;
}
}
// Special1 is used to count how many recursions we're in.
if (newmobj->IsKindOf(PClass::FindClass("RandomSpawner")))
newmobj->special1 = ++special1;
else Species = NAME_None;
}
}
if ((newmobj != NULL) && ((newmobj->flags4 & MF4_BOSSDEATH) || (newmobj->flags2 & MF2_BOSS)))
this->target = newmobj; // If the spawned actor has either of those flags, it's a boss.
}
// 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;
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->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->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;
}
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;
}
}
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;
}
if (boss) this->tracer = newmobj;
else Destroy(); // "else" because a boss-replacing spawner must wait until it can call A_BossDeath.
}
void Tick() // This function is needed for handling boss replacers
{
Super::Tick();
if (target == NULL || target->health <= 0)
if (tracer == NULL || tracer->health <= 0)
{
health = 0;
CALL_ACTION(A_BossDeath, this);
Destroy();
}

View file

@ -61,6 +61,7 @@ static FRandom pr_dropitem ("DropItem");
static FRandom pr_look2 ("LookyLooky");
static FRandom pr_look3 ("IGotHooky");
static FRandom pr_slook ("SlooK");
static FRandom pr_dropoff ("Dropoff");
static FRandom pr_skiptarget("SkipTarget");
@ -380,6 +381,7 @@ bool P_Move (AActor *actor)
int speed = actor->Speed;
int movefactor = ORIG_FRICTION_FACTOR;
int friction = ORIG_FRICTION;
int dropoff = 0;
if (actor->flags2 & MF2_BLASTED)
{
@ -410,23 +412,18 @@ bool P_Move (AActor *actor)
if ((unsigned)actor->movedir >= 8)
I_Error ("Weird actor->movedir!");
speed = actor->Speed;
#if 0 // todo
// killough 10/98: allow dogs to drop off of taller ledges sometimes.
// dropoff==1 means always allow it, dropoff==2 means only up to 128 high,
// and only if the target is immediately on the other side of the line.
AActor *target = actor->target;
if (actor->flags6 & MF6_JUMPDOWN && target &&
if ((actor->flags6 & MF6_JUMPDOWN) && target &&
!(target->IsFriend(actor)) &&
P_AproxDistance(actor->x - target->x,
actor->y - target->y) < FRACUNIT*144 &&
P_Random(pr_dropoff) < 235)
P_AproxDistance(actor->x - target->x, actor->y - target->y) < FRACUNIT*144 &&
pr_dropoff() < 235)
{
dropoff = 2;
}
#endif
// [RH] I'm not so sure this is such a good idea
// [GZ] That's why it's compat-optioned.
@ -479,12 +476,12 @@ bool P_Move (AActor *actor)
try_ok = true;
for(int i=1; i < steps; i++)
{
try_ok = P_TryMove(actor, origx + Scale(deltax, i, steps), origy + Scale(deltay, i, steps), false, false, tm);
try_ok = P_TryMove(actor, origx + Scale(deltax, i, steps), origy + Scale(deltay, i, steps), dropoff, false, tm);
if (!try_ok) break;
}
// killough 3/15/98: don't jump over dropoffs:
if (try_ok) try_ok = P_TryMove (actor, tryx, tryy, false, false, tm);
if (try_ok) try_ok = P_TryMove (actor, tryx, tryy, dropoff, false, tm);
// [GrafZahl] Interpolating monster movement as it is done here just looks bad
// so make it switchable!

View file

@ -377,8 +377,8 @@ bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm);
bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y);
AActor *P_CheckOnmobj (AActor *thing);
void P_FakeZMovement (AActor *mo);
bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, bool dropoff, const secplane_t * onfloor, FCheckPosition &tm);
bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, bool dropoff, const secplane_t * onfloor = NULL);
bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, int dropoff, const secplane_t * onfloor, FCheckPosition &tm);
bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, int dropoff, const secplane_t * onfloor = NULL);
bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y);
void P_ApplyTorque(AActor *mo);
bool P_TeleportMove (AActor* thing, fixed_t x, fixed_t y, fixed_t z, bool telefrag); // [RH] Added z and telefrag parameters

View file

@ -834,6 +834,29 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm)
P_DamageMobj (thing, NULL, NULL, thing->health, NAME_None, DMG_FORCED); // kill object
return true;
}
// Check for MF6_BUMPSPECIAL
// By default, only players can activate things by bumping into them
if ((thing->flags6 & MF6_BUMPSPECIAL))
{
if (((tm.thing->player != NULL)
|| ((thing->activationtype & THINGSPEC_MonsterTrigger) && (thing->flags3 & MF3_ISMONSTER))
|| ((thing->activationtype & THINGSPEC_MissileTrigger) && (thing->flags & MF_MISSILE))
))
{ // Target switching mechanism
if (thing->activationtype & THINGSPEC_ThingTargets) thing->target = tm.thing;
if (thing->activationtype & THINGSPEC_TriggerTargets) tm.thing->target = thing;
// Run the special
int res = LineSpecials[thing->special] (NULL,
((thing->activationtype & THINGSPEC_ThingActs) ? thing : tm.thing), // Who triggers?
false, thing->args[0], thing->args[1], thing->args[2], thing->args[3], thing->args[4]);
if (thing->activationtype & THINGSPEC_ClearSpecial && res) thing->special = 0;
}
}
// Check for skulls slamming into things
if (tm.thing->flags & MF_SKULLFLY)
{
@ -860,13 +883,6 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm)
return false;
}
}
// Check for players touching a thing with MF6_BUMPSPECIAL
// A blind recreation of what the Skulltag code is probably like.
if (tm.thing->player && (thing->flags6 & MF6_BUMPSPECIAL) && thing->special)
{
LineSpecials[thing->special] (NULL, tm.thing, false, thing->args[0],
thing->args[1], thing->args[2], thing->args[3], thing->args[4]);
}
// Check for missile or non-solid MBF bouncer
if (tm.thing->flags & MF_MISSILE || ((tm.thing->BounceFlags & BOUNCE_MBF) && !(tm.thing->flags & MF_SOLID)))
{
@ -1537,7 +1553,7 @@ static void CheckForPushSpecial (line_t *line, int side, AActor *mobj)
// crossing special lines unless MF_TELEPORT is set.
//
bool P_TryMove (AActor *thing, fixed_t x, fixed_t y,
bool dropoff, // killough 3/15/98: allow dropoff as option
int dropoff, // killough 3/15/98: allow dropoff as option
const secplane_t *onfloor, // [RH] Let P_TryMove keep the thing on the floor
FCheckPosition &tm)
{
@ -1667,6 +1683,13 @@ bool P_TryMove (AActor *thing, fixed_t x, fixed_t y,
dropoff = false;
}
if (dropoff==2 && // large jump down (e.g. dogs)
(tm.floorz-tm.dropoffz > 128*FRACUNIT || thing->target == NULL || thing->target->z >tm.dropoffz))
{
dropoff = false;
}
// killough 3/15/98: Allow certain objects to drop off
if ((!dropoff && !(thing->flags & (MF_DROPOFF|MF_FLOAT|MF_MISSILE))) || (thing->flags5&MF5_NODROPOFF))
{
@ -1894,7 +1917,7 @@ pushline:
}
bool P_TryMove (AActor *thing, fixed_t x, fixed_t y,
bool dropoff, // killough 3/15/98: allow dropoff as option
int dropoff, // killough 3/15/98: allow dropoff as option
const secplane_t *onfloor) // [RH] Let P_TryMove keep the thing on the floor
{
FCheckPosition tm;
@ -3745,12 +3768,20 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline
// Check thing
// Check for puzzle item use or USESPECIAL flag
// Extended to use the same activationtype mechanism as BUMPSPECIAL does
if (in->d.thing->flags5 & MF5_USESPECIAL || in->d.thing->special == UsePuzzleItem)
{
if (LineSpecials[in->d.thing->special] (NULL, usething, false,
{ // Target switching mechanism
if (in->d.thing->activationtype & THINGSPEC_ThingTargets) in->d.thing->target = usething;
if (in->d.thing->activationtype & THINGSPEC_TriggerTargets) usething->target = in->d.thing;
// Run the special
if (LineSpecials[in->d.thing->special] (NULL, // Who triggers?
((in->d.thing->activationtype & THINGSPEC_ThingActs) ? in->d.thing : usething), false,
in->d.thing->args[0], in->d.thing->args[1], in->d.thing->args[2],
in->d.thing->args[3], in->d.thing->args[4]))
{
if (in->d.thing->activationtype & THINGSPEC_ClearSpecial) in->d.thing->special = 0;
return true;
}
}
// Dead things can't talk.
if (in->d.thing->health <= 0)

View file

@ -395,18 +395,14 @@ void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *
//
//==========================================================================
static int ParseMorphStyle (FScanner &sc)
struct FParseValue
{
static const char * morphstyles[]={
"MRF_ADDSTAMINA", "MRF_FULLHEALTH", "MRF_UNDOBYTOMEOFPOWER", "MRF_UNDOBYCHAOSDEVICE",
"MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", "MRF_LOSEACTUALWEAPON",
"MRF_NEWTIDBEHAVIOUR", "MRF_UNDOBYDEATH", "MRF_UNDOBYDEATHFORCED", "MRF_UNDOBYDEATHSAVES", NULL};
static const int morphstyle_values[]={
MORPH_ADDSTAMINA, MORPH_FULLHEALTH, MORPH_UNDOBYTOMEOFPOWER, MORPH_UNDOBYCHAOSDEVICE,
MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE, MORPH_LOSEACTUALWEAPON,
MORPH_NEWTIDBEHAVIOUR, MORPH_UNDOBYDEATH, MORPH_UNDOBYDEATHFORCED, MORPH_UNDOBYDEATHSAVES};
const char *Name;
int Flag;
};
int ParseFlagExpressionString(FScanner &sc, const FParseValue *vals)
{
// May be given flags by number...
if (sc.CheckNumber())
{
@ -422,7 +418,7 @@ static int ParseMorphStyle (FScanner &sc)
do
{
sc.MustGetString();
style |= morphstyle_values[sc.MustMatchString(morphstyles)];
style |= vals[sc.MustMatchString(&vals->Name, sizeof (*vals))].Flag;
}
while (sc.CheckString("|"));
if (gotparen)
@ -433,6 +429,46 @@ static int ParseMorphStyle (FScanner &sc)
return style;
}
static int ParseMorphStyle (FScanner &sc)
{
static const FParseValue morphstyles[]={
{ "MRF_ADDSTAMINA", MORPH_ADDSTAMINA},
{ "MRF_FULLHEALTH", MORPH_FULLHEALTH},
{ "MRF_UNDOBYTOMEOFPOWER", MORPH_UNDOBYTOMEOFPOWER},
{ "MRF_UNDOBYCHAOSDEVICE", MORPH_UNDOBYCHAOSDEVICE},
{ "MRF_FAILNOTELEFRAG", MORPH_FAILNOTELEFRAG},
{ "MRF_FAILNOLAUGH", MORPH_FAILNOLAUGH},
{ "MRF_WHENINVULNERABLE", MORPH_WHENINVULNERABLE},
{ "MRF_LOSEACTUALWEAPON", MORPH_LOSEACTUALWEAPON},
{ "MRF_NEWTIDBEHAVIOUR", MORPH_NEWTIDBEHAVIOUR},
{ "MRF_UNDOBYDEATH", MORPH_UNDOBYDEATH},
{ "MRF_UNDOBYDEATHFORCED", MORPH_UNDOBYDEATHFORCED},
{ "MRF_UNDOBYDEATHSAVES", MORPH_UNDOBYDEATHSAVES},
{ NULL, 0 }
};
return ParseFlagExpressionString(sc, morphstyles);
}
static int ParseThingActivation (FScanner &sc)
{
static const FParseValue activationstyles[]={
{ "THINGSPEC_Default", THINGSPEC_Default},
{ "THINGSPEC_ThingActs", THINGSPEC_ThingActs},
{ "THINGSPEC_ThingTargets", THINGSPEC_ThingTargets},
{ "THINGSPEC_TriggerTargets", THINGSPEC_TriggerTargets},
{ "THINGSPEC_MonsterTrigger", THINGSPEC_MonsterTrigger},
{ "THINGSPEC_MissileTrigger", THINGSPEC_MissileTrigger},
{ "THINGSPEC_ClearSpecial", THINGSPEC_ClearSpecial},
{ NULL, 0 }
};
return ParseFlagExpressionString(sc, activationstyles);
}
//==========================================================================
//
// For getting a state address from the parent

View file

@ -1057,6 +1057,16 @@ DEFINE_PROPERTY(projectile, 0, Actor)
if (gameinfo.gametype&GAME_Raven) defaults->flags5|=MF5_BLOODSPLATTER;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(activation, N, Actor)
{
// How the thing behaves when activated with MF5_USESPECIAL or MF6_BUMPSPECIAL
PROP_INT_PARM(val, 0);
defaults->activationtype = val;
}
//==========================================================================
//
// Special inventory properties

View file

@ -61,6 +61,18 @@ const int MRF_UNDOBYDEATHSAVES = 2048;
const int RGF_SILENT = 1;
const int RGF_NOPIERCING = 2;
enum
{
THINGSPEC_Default = 0,
THINGSPEC_ThingActs = 1,
THINGSPEC_ThingTargets = 2,
THINGSPEC_TriggerTargets = 4,
THINGSPEC_MonsterTrigger = 8,
THINGSPEC_MissileTrigger = 16,
THINGSPEC_ClearSpecial = 32,
};
// constants for A_PlaySound
enum
{