- MBF21: ported the code pointers to ZScript.

So far it's just the functions and some initial changes to Dehacked's parser. None of this is usable yet.
This commit is contained in:
Christoph Oelckers 2021-06-30 23:15:56 +02:00
parent f701ef5c68
commit c700682a36
18 changed files with 677 additions and 60 deletions

View File

@ -1111,3 +1111,4 @@ xx(zoomsize)
xx(ScreenJobRunner)
xx(RazeStatusBar)
xx(RipSound)
xx(Archvile)

View File

@ -66,6 +66,7 @@
#include "types.h"
#include "m_argv.h"
#include "actorptrselect.h"
#include "g_levellocals.h"
void JitDumpLog(FILE *file, VMScriptFunction *func);
@ -147,14 +148,15 @@ struct MBFParamState
{
FState *state;
int pointer;
int args[8];
int argsused;
};
static TArray<MBFParamState> MBFParamStates;
// Data on how to correctly modify the codepointers
struct CodePointerAlias
{
FName name;
char alias[20];
uint8_t params;
FString alias;
};
static TArray<CodePointerAlias> MBFCodePointers;
@ -630,7 +632,7 @@ static int GetLine (void)
// misc1 = vrange (arg +3), misc2 = hrange (arg+4)
static void CreateMushroomFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateMushroomFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_Mushroom
emitters.AddParameterPointerConst(PClass::FindClass("FatShot")); // itemtype
emitters.AddParameterIntConst(0); // numspawns
@ -640,7 +642,7 @@ static void CreateMushroomFunc(FunctionCallEmitter &emitters, int value1, int va
}
// misc1 = type (arg +0), misc2 = Z-pos (arg +2)
static void CreateSpawnFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateSpawnFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_SpawnItem
if (InfoNames[value1-1] == nullptr)
{
@ -655,13 +657,13 @@ static void CreateSpawnFunc(FunctionCallEmitter &emitters, int value1, int value
// misc1 = angle (in degrees) (arg +0 but factor in current actor angle too)
static void CreateTurnFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateTurnFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_Turn
emitters.AddParameterFloatConst(value1); // angle
}
// misc1 = angle (in degrees) (arg +0)
static void CreateFaceFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateFaceFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_SetAngle
emitters.AddParameterFloatConst(value1); // angle
emitters.AddParameterIntConst(0); // flags
@ -669,7 +671,7 @@ static void CreateFaceFunc(FunctionCallEmitter &emitters, int value1, int value2
}
// misc1 = damage, misc 2 = sound
static void CreateScratchFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateScratchFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_CustomMeleeAttack
emitters.AddParameterIntConst(value1); // damage
emitters.AddParameterIntConst(value2 ? (int)SoundMap[value2 - 1] : 0); // hit sound
@ -679,7 +681,7 @@ static void CreateScratchFunc(FunctionCallEmitter &emitters, int value1, int val
}
// misc1 = sound, misc2 = attenuation none (true) or normal (false)
static void CreatePlaySoundFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreatePlaySoundFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_PlaySound
emitters.AddParameterIntConst(value1 ? (int)SoundMap[value1 - 1] : 0); // soundid
emitters.AddParameterIntConst(CHAN_BODY); // channel
@ -691,7 +693,7 @@ static void CreatePlaySoundFunc(FunctionCallEmitter &emitters, int value1, int v
}
// misc1 = state, misc2 = probability
static void CreateRandomJumpFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateRandomJumpFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_Jump
auto symlabel = StateLabels.AddPointer(FindState(value1));
@ -701,7 +703,7 @@ static void CreateRandomJumpFunc(FunctionCallEmitter &emitters, int value1, int
}
// misc1 = Boom linedef type, misc2 = sector tag
static void CreateLineEffectFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateLineEffectFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_LineEffect
// This is the second MBF codepointer that couldn't be translated easily.
// Calling P_TranslateLineDef() here was a simple matter, as was adding an
@ -713,7 +715,7 @@ static void CreateLineEffectFunc(FunctionCallEmitter &emitters, int value1, int
}
// No misc, but it's basically A_Explode with an added effect
static void CreateNailBombFunc(FunctionCallEmitter &emitters, int value1, int value2)
static void CreateNailBombFunc(FunctionCallEmitter &emitters, int value1, int value2, int* args, int argsused)
{ // A_Explode
// This one does not actually have MBF-style parameters. But since
// we're aliasing it to an extension of A_Explode...
@ -728,8 +730,17 @@ static void CreateNailBombFunc(FunctionCallEmitter &emitters, int value1, int va
emitters.AddParameterIntConst(NAME_None); // damage type
}
static PClassActor* GetDehType(int num)
{
PClassActor* type = nullptr;
if (num >= 0 && num < int(InfoNames.Size())) type = InfoNames[num];
if (type == nullptr) type = RUNTIME_CLASS(AActor);
}
// This array must be in sync with the Aliases array in DEHSUPP.
static void (*MBFCodePointerFactories[])(FunctionCallEmitter&, int, int) =
static void (*MBFCodePointerFactories[])(FunctionCallEmitter&, int, int, int*, int) =
{
// Die and Detonate are not in this list because these codepointers have
// no dehacked arguments and therefore do not need special handling.
@ -742,12 +753,12 @@ static void (*MBFCodePointerFactories[])(FunctionCallEmitter&, int, int) =
CreatePlaySoundFunc,
CreateRandomJumpFunc,
CreateLineEffectFunc,
CreateNailBombFunc
CreateNailBombFunc,
};
// Creates new functions for the given state so as to convert MBF-args (misc1 and misc2) into real args.
static void SetDehParams(FState *state, int codepointer, VMDisassemblyDumper &disasmdump)
static void SetDehParams(FState *state, int codepointer, VMDisassemblyDumper &disasmdump, int* args, int argsused)
{
static const uint8_t regts[] = { REGT_POINTER, REGT_POINTER, REGT_POINTER };
int value1 = state->GetMisc1();
@ -786,7 +797,7 @@ static void SetDehParams(FState *state, int codepointer, VMDisassemblyDumper &di
emitters.AddParameterPointer(i, false);
}
// Emit code for action parameters.
MBFCodePointerFactories[codepointer](emitters, value1, value2);
MBFCodePointerFactories[codepointer](emitters, value1, value2, args, argsused);
auto where = emitters.EmitCall(&buildit);
if (!returnsState) buildit.Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
else buildit.Emit(OP_RET, RET_FINAL, EncodeRegType(where), where.RegNum);
@ -1039,7 +1050,7 @@ static int PatchThing (int thingy)
}
else if (linelen == 11 && stricmp(Line1, "melee range") == 0)
{
info->meleerange = DEHToDouble(val);
info->meleerange = DEHToDouble(val) - 20; // -20 is needed because DSDA subtracts it in P_CheckMeleeRange, while GZDoom does not.
}
else if (linelen == 10 && stricmp(Line1, "MBF21 Bits") == 0)
{
@ -1502,6 +1513,8 @@ DehBits sbits[] = {
static int PatchFrame (int frameNum)
{
int args[8]{};
int argsused = 0;
int result;
int tics, misc1, frame;
FState *info, dummy;
@ -1583,6 +1596,19 @@ static int PatchFrame (int frameNum)
{
frame = val;
}
else if (keylen == 5 && strnicmp(Line1, "Args", 4) == 0)
{
int arg = Line1[4] - '1';
if (arg < 0 || arg >= 8)
{
Printf("Invalid frame arg %d\n", arg);
}
else
{
args[arg] = val;
argsused |= (1 << arg);
}
}
else if (stricmp(Line1, "MBF21 Bits") == 0)
{
uint32_t value = 0;
@ -1962,12 +1988,11 @@ static int PatchWeapon (int weapNum)
return result;
}
static void SetPointer(FState *state, PFunction *sym, int frame = 0)
static int SetPointer(FState *state, PFunction *sym, int frame = 0)
{
if (sym == NULL)
{
state->ClearAction();
return;
}
else
{
@ -1977,14 +2002,12 @@ static void SetPointer(FState *state, PFunction *sym, int frame = 0)
{
if (sym->SymbolName == MBFCodePointers[i].name)
{
MBFParamState newstate;
newstate.state = state;
newstate.pointer = i;
MBFParamStates.Push(newstate);
break; // No need to cycle through the rest of the list.
MBFParamState newstate = { state, int(i) };
return MBFParamStates.Push(newstate);
}
}
}
return -1;
}
static int PatchPointer (int ptrNum)
@ -2885,7 +2908,7 @@ static void UnloadDehSupp ()
// Handle MBF params here, before the required arrays are cleared
for (unsigned int i=0; i < MBFParamStates.Size(); i++)
{
SetDehParams(MBFParamStates[i].state, MBFParamStates[i].pointer, disasmdump);
SetDehParams(MBFParamStates[i].state, MBFParamStates[i].pointer, disasmdump, MBFParamStates[i].args, MBFParamStates[i].argsused);
}
MBFParamStates.Clear();
MBFParamStates.ShrinkToFit();
@ -3202,14 +3225,10 @@ static bool LoadDehSupp ()
{
CodePointerAlias temp;
sc.MustGetString();
strncpy(temp.alias, sc.String, 19);
temp.alias[19]=0;
temp.alias = sc.String;
sc.MustGetStringName(",");
sc.MustGetString();
temp.name = sc.String;
sc.MustGetStringName(",");
sc.MustGetNumber();
temp.params = sc.Number;
MBFCodePointers.Push(temp);
if (sc.CheckString("}")) break;
sc.MustGetStringName(",");
@ -3401,3 +3420,241 @@ DEFINE_ACTION_FUNCTION(ADehackedPickup, DetermineType)
}
ACTION_RETURN_POINTER(nullptr);
}
// Handling the flags is not as trivial as it seems...
struct FlagHandler
{
void (*setter)(AActor*);
void (*clearer)(AActor*);
bool (*checker)(AActor*);
};
// cut down on boilerplate
#define F(flag) { [](AActor* a) { a->flags |= flag; }, [](AActor* a) { a->flags &= ~flag; }, [](AActor* a)->bool { return a->flags & flag; } }
#define F2(flag) { [](AActor* a) { a->flags2 |= flag; }, [](AActor* a) { a->flags2 &= ~flag; }, [](AActor* a)->bool { return a->flags2 & flag; } }
#define F3(flag) { [](AActor* a) { a->flags3 |= flag; }, [](AActor* a) { a->flags3 &= ~flag; }, [](AActor* a)->bool { return a->flags3 & flag; } }
#define F4(flag) { [](AActor* a) { a->flags4 |= flag; }, [](AActor* a) { a->flags4 &= ~flag; }, [](AActor* a)->bool { return a->flags4 & flag; } }
#define F8(flag) { [](AActor* a) { a->flags8 |= flag; }, [](AActor* a) { a->flags8 &= ~flag; }, [](AActor* a)->bool { return a->flags8 & flag; } }
#define DEPF(flag) { [](AActor* a) { HandleDeprecatedFlags(a, nullptr, true, flag); }, [](AActor* a) { HandleDeprecatedFlags(a, nullptr, false, flag); }, [](AActor* a)->bool { return CheckDeprecatedFlags(a, nullptr, flag); } }
void SetNoSector(AActor* a)
{
a->UnlinkFromWorld(nullptr);
a->flags |= MF_NOSECTOR;
a->LinkToWorld(nullptr);
}
void ClearNoSector(AActor* a)
{
a->UnlinkFromWorld(nullptr);
a->flags &= ~MF_NOSECTOR;
a->LinkToWorld(nullptr);
}
void SetNoBlockmap(AActor* a)
{
a->UnlinkFromWorld(nullptr);
a->flags |= MF_NOBLOCKMAP;
a->LinkToWorld(nullptr);
}
void ClearNoBlockmap(AActor* a)
{
a->UnlinkFromWorld(nullptr);
a->flags &= ~MF_NOBLOCKMAP;
a->LinkToWorld(nullptr);
}
void SetCountkill(AActor* a)
{
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters--;
a->flags |= MF_COUNTKILL;
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters++;
}
void ClearCountkill(AActor* a)
{
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters--;
a->flags &= ~MF_COUNTKILL;
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters++;
}
void SetCountitem(AActor* a)
{
if (!(a->flags & MF_COUNTITEM))
{
a->flags |= MF_COUNTITEM;
a->Level->total_items++;
}
}
void ClearCountitem(AActor* a)
{
if (a->flags & MF_COUNTITEM)
{
a->flags |= MF_COUNTITEM;
a->Level->total_items--;
}
}
void SetFriendly(AActor* a)
{
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters--;
a->flags |= MF_FRIENDLY;
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters++;
}
void ClearFriendly(AActor* a)
{
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters--;
a->flags &= ~MF_FRIENDLY;
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters++;
}
void SetFullVol(AActor* a)
{
a->flags8 |= MF8_FULLVOLSEE;
a->flags3 |= MF3_FULLVOLDEATH;
}
void ClearFullVol(AActor* a)
{
a->flags8 &= ~MF8_FULLVOLSEE;
a->flags3 &= ~MF3_FULLVOLDEATH;
}
static FlagHandler flag1handlers[32] = {
F(MF_SPECIAL),
F(MF_SOLID),
F(MF_SHOOTABLE),
{ SetNoSector, ClearNoSector, [](AActor* a)->bool { return a->flags & MF_NOSECTOR; } },
{ SetNoBlockmap, ClearNoBlockmap, [](AActor* a)->bool { return a->flags & MF_NOBLOCKMAP; } },
F(MF_AMBUSH),
F(MF_JUSTHIT),
F(MF_JUSTATTACKED),
F(MF_SPAWNCEILING),
F(MF_NOGRAVITY),
F(MF_DROPOFF),
F(MF_PICKUP),
F(MF_NOCLIP),
{ nullptr, nullptr, nullptr}, // MF_SLIDE no longer exists.
F(MF_FLOAT),
F(MF_TELEPORT),
F(MF_MISSILE),
F(MF_DROPPED),
F(MF_SHADOW),
F(MF_NOBLOOD),
F(MF_CORPSE),
F(MF_INFLOAT),
{ SetCountkill, ClearCountkill, [](AActor* a)->bool { return a->flags & MF_COUNTKILL; } },
{ SetCountitem, ClearCountitem, [](AActor* a)->bool { return a->flags & MF_COUNTITEM; } },
F(MF_SKULLFLY),
F(MF_NOTDMATCH),
// translation, unused and translucent are no longer checkable in any way. Pity.
};
static FlagHandler flag2handlers[32] = {
DEPF(DEPF_LOWGRAVITY),
DEPF(DEPF_SHORTMISSILERANGE),
F3(MF3_NOTARGET),
F3(MF3_NORADIUSDMG),
F4(MF4_FORCERADIUSDMG),
DEPF(DEPF_HIGHERMPROB),
F4(MF4_MISSILEMORE),
F4(MF4_QUICKTORETALIATE),
DEPF(DEPF_LONGMELEERANGE),
F2(MF2_BOSS),
F8(MF8_MAP07BOSS1),
F8(MF8_MAP07BOSS2),
F8(MF8_E1M8BOSS),
F8(MF8_E2M8BOSS),
F8(MF8_E3M8BOSS),
F8(MF8_E4M6BOSS),
F8(MF8_E4M8BOSS),
F2(MF2_RIP),
{ SetFullVol, ClearFullVol, [](AActor* a)->bool { return a->flags8 & MF8_FULLVOLSEE; } }, // checking one of the two flags should suffice here.
};
//
// A_JumpIfFlagsSet
// Jumps to a state if caller has the specified thing flags set.
// args[0]: State to jump to
// args[1]: Standard Flag(s) to check
// args[2]: MBF21 Flag(s) to check
//
DEFINE_ACTION_FUNCTION(AActor, MBF21_JumpIfFlagsSet)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_POINTER(tstate, FState);
PARAM_INT(flags);
PARAM_INT(flags2);
for (int i = 0; i < 32; i++)
{
if (flags & (1 << i) && flag1handlers[i].checker)
{
if (!flag1handlers[i].checker(self)) return 0;
}
if (flags2 & (1 << i) && flag2handlers[i].checker)
{
if (!flag2handlers[i].checker(self)) return 0;
}
}
self->SetState(tstate);
return 0;
}
//
// A_AddFlags
// Adds the specified thing flags to the caller.
// args[0]: Standard Flag(s) to add
// args[1]: MBF21 Flag(s) to add
//
DEFINE_ACTION_FUNCTION(AActor, MBF21_AddFlags)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(flags);
PARAM_INT(flags2);
for (int i = 0; i < 32; i++)
{
if (flags & (1 << i) && flag1handlers[i].setter)
{
flag1handlers[i].setter(self);
}
if (flags2 & (1 << i) && flag2handlers[i].setter)
{
flag2handlers[i].setter(self);
}
}
return 0;
}
//
// A_RemoveFlags
// Removes the specified thing flags from the caller.
// args[0]: Flag(s) to remove
// args[1]: MBF21 Flag(s) to remove
//
DEFINE_ACTION_FUNCTION(AActor, MBF21_RemoveFlags)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(flags);
PARAM_INT(flags2);
for (int i = 0; i < 32; i++)
{
if (flags & (1 << i) && flag1handlers[i].clearer)
{
flag1handlers[i].clearer(self);
}
if (flags2 & (1 << i) && flag2handlers[i].clearer)
{
flag2handlers[i].clearer(self);
}
}
return 0;
}

View File

@ -697,7 +697,7 @@ public:
void AlterWeaponSprite(visstyle_t *vis);
// Returns true if this actor is within melee range of its target
bool CheckMeleeRange();
bool CheckMeleeRange(double range = -1);
bool CheckNoDelay();

View File

@ -260,7 +260,7 @@ void P_NoiseAlert (AActor *emitter, AActor *target, bool splash, double maxdist)
//
//----------------------------------------------------------------------------
bool AActor::CheckMeleeRange ()
bool AActor::CheckMeleeRange (double range)
{
AActor *pl = target;
@ -270,8 +270,9 @@ bool AActor::CheckMeleeRange ()
return false;
dist = Distance2D (pl);
if (range < 0) range = meleerange;
if (dist >= meleerange + pl->radius)
if (dist >= range + pl->radius)
return false;
// [RH] If moving toward goal, then we've reached it.
@ -300,7 +301,8 @@ bool AActor::CheckMeleeRange ()
DEFINE_ACTION_FUNCTION(AActor, CheckMeleeRange)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_INT(self->CheckMeleeRange());
PARAM_FLOAT(range);
ACTION_RETURN_INT(self->CheckMeleeRange(range));
}
//=============================================================================
@ -2616,7 +2618,7 @@ bool P_CanResurrect(AActor *raiser, AActor *thing)
//
//==========================================================================
bool P_CheckForResurrection(AActor *self, bool usevilestates)
bool P_CheckForResurrection(AActor* self, bool usevilestates, FState* state = nullptr, FSoundID sound = 0)
{
const AActor *info;
AActor *temp;
@ -2702,8 +2704,8 @@ bool P_CheckForResurrection(AActor *self, bool usevilestates)
self->target = temp;
// Make the state the monster enters customizable.
FState * state = self->FindState(NAME_Heal);
if (state != NULL)
if (state == nullptr) state = self->FindState(NAME_Heal);
if (state != nullptr)
{
self->SetState(state);
}
@ -2711,13 +2713,14 @@ bool P_CheckForResurrection(AActor *self, bool usevilestates)
{
// For Dehacked compatibility this has to use the Arch Vile's
// heal state as a default if the actor doesn't define one itself.
PClassActor *archvile = PClass::FindActor("Archvile");
PClassActor *archvile = PClass::FindActor(NAME_Archvile);
if (archvile != NULL)
{
self->SetState(archvile->FindState(NAME_Heal));
}
}
S_Sound(corpsehit, CHAN_BODY, 0, "vile/raise", 1, ATTN_IDLE);
if (sound == 0) sound = "vile/raise";
S_Sound(corpsehit, CHAN_BODY, 0, sound, 1, ATTN_IDLE);
info = corpsehit->GetDefault();
if (GetTranslationType(corpsehit->Translation) == TRANSLATION_Blood)

View File

@ -72,7 +72,7 @@ void A_DoChase(AActor *actor, bool fastchase, FState *meleestate, FState *missil
void A_Chase(AActor *self);
void A_FaceTarget(AActor *actor);
void A_Face(AActor *self, AActor *other, DAngle max_turn = 0., DAngle max_pitch = 270., DAngle ang_offset = 0., DAngle pitch_offset = 0., int flags = 0, double z_add = 0);
bool P_CheckForResurrection(AActor *self, bool usevilestates);
class FSoundID;
int CheckBossDeath (AActor *);
int P_Massacre (bool baddies = false, PClassActor *cls = nullptr);

View File

@ -228,8 +228,9 @@ enum PCM
};
int P_CheckFov(AActor* t1, AActor* t2, double fov);
AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params = NULL);
AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false, bool frontonly = false);
AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false, bool frontonly = false, double fov = 0);
//
// P_MAP

View File

@ -1679,6 +1679,23 @@ FPathTraverse::~FPathTraverse()
}
//
// P_CheckFov
// Returns true if t2 is within t1's field of view.
//
int P_CheckFov(AActor* t1, AActor* t2, double fov)
{
return absangle(t1->AngleTo(t2), t1->Angles.Yaw) <= fov;
}
DEFINE_ACTION_FUNCTION(AActor, CheckFov, P_CheckFov)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_POINTER(t, AActor);
PARAM_FLOAT(fov);
ACTION_RETURN_BOOL(P_CheckFov(self, t, fov));
}
//===========================================================================
//
// P_RoughMonsterSearch
@ -1784,6 +1801,7 @@ struct BlockCheckInfo
bool onlyseekable;
bool frontonly;
divline_t frontline;
double fov;
};
//===========================================================================
@ -1810,6 +1828,12 @@ static AActor *RoughBlockCheck (AActor *mo, int index, void *param)
{
continue;
}
// skip actors outside of specified FOV
if (info->fov > 0 && !P_CheckFov(mo, link->Me, info->fov))
{
continue;
}
if (mo->IsOkayToAttack (link->Me))
{
return link->Me;
@ -1819,7 +1843,7 @@ static AActor *RoughBlockCheck (AActor *mo, int index, void *param)
return NULL;
}
AActor *P_RoughMonsterSearch(AActor *mo, int distance, bool onlyseekable, bool frontonly)
AActor *P_RoughMonsterSearch(AActor *mo, int distance, bool onlyseekable, bool frontonly, double fov)
{
BlockCheckInfo info;
info.onlyseekable = onlyseekable;

View File

@ -3306,7 +3306,7 @@ bool AActor::IsOkayToAttack (AActor *link)
// its target; and for a summoned minion, its tracer.
AActor * Friend;
if (flags5 & MF5_SUMMONEDMONSTER) Friend = tracer;
else if (flags2 & MF2_SEEKERMISSILE) Friend = target;
else if (flags & MF_MISSILE) Friend = target;
else if ((flags & MF_FRIENDLY) && FriendPlayer) Friend = Level->Players[FriendPlayer-1]->mo;
else Friend = this;

View File

@ -228,6 +228,7 @@ enum
DEPF_HEXENBOUNCE = 10,
DEPF_DOOMBOUNCE = 11,
DEPF_INTERHUBSTRIP = 12,
DEPF_HIGHERMPROB = 13,
};
// Types of old style decorations

View File

@ -397,6 +397,7 @@ static FFlagDef MoreFlagDefs[] =
DEFINE_DEPRECATED_FLAG(HERETICBOUNCE),
DEFINE_DEPRECATED_FLAG(HEXENBOUNCE),
DEFINE_DEPRECATED_FLAG(DOOMBOUNCE),
DEFINE_DEPRECATED_FLAG(HIGHERMPROB),
// Deprecated flags with no more existing functionality.
DEFINE_DUMMY_FLAG(FASTER, true), // obsolete, replaced by 'Fast' state flag

View File

@ -325,6 +325,10 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in
defaults->IntVar(NAME_InterHubAmount) = set ? 0 : 1;
break;
case DEPF_HIGHERMPROB:
defaults->MinMissileChance = set ? 160 : 200;
break;
default:
break; // silence GCC
}
@ -384,6 +388,9 @@ bool CheckDeprecatedFlags(AActor *actor, PClassActor *info, int index)
case DEPF_INTERHUBSTRIP:
return !(actor->IntVar(NAME_InterHubAmount));
case DEPF_HIGHERMPROB:
return actor->MinMissileChance <= 160;
}
return false; // Any entirely unknown flag is not set

View File

@ -59,6 +59,7 @@
DVector2 AM_GetPosition();
int Net_GetLatency(int *ld, int *ad);
void PrintPickupMessage(bool localview, const FString &str);
bool P_CheckForResurrection(AActor* self, bool usevilestates, FState* state = nullptr, FSoundID sound = 0);
// FCheckPosition requires explicit construction and destruction when used in the VM
@ -328,6 +329,18 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetDamage, SetDamage)
return 0;
}
static double PitchFromVel(AActor* self)
{
return self->Vel.Pitch().Degrees;
}
DEFINE_ACTION_FUNCTION_NATIVE(AActor, PitchFromVel, PitchFromVel)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_FLOAT(PitchFromVel(self));
}
// This combines all 3 variations of the internal function
static void VelFromAngle(AActor *self, double speed, double angle)
{
@ -1373,7 +1386,8 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, RoughMonsterSearch, P_RoughMonsterSearch)
PARAM_INT(distance);
PARAM_BOOL(onlyseekable);
PARAM_BOOL(frontonly);
ACTION_RETURN_OBJECT(P_RoughMonsterSearch(self, distance, onlyseekable, frontonly));
PARAM_FLOAT(fov);
ACTION_RETURN_OBJECT(P_RoughMonsterSearch(self, distance, onlyseekable, frontonly, fov));
}
DEFINE_ACTION_FUNCTION_NATIVE(AActor, CheckSight, P_CheckSight)
@ -1576,15 +1590,17 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_ExtChase, A_ExtChase)
return 0;
}
int CheckForResurrection(AActor *self)
int CheckForResurrection(AActor *self, FState* state, int sound)
{
return P_CheckForResurrection(self, false);
return P_CheckForResurrection(self, false, state, sound);
}
DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_CheckForResurrection, CheckForResurrection)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_BOOL(P_CheckForResurrection(self, false));
PARAM_STATE(state);
PARAM_INT(sound);
ACTION_RETURN_BOOL(P_CheckForResurrection(self, false, state, sound));
}
static void ZS_Face(AActor *self, AActor *faceto, double max_turn, double max_pitch, double ang_offset, double pitch_offset, int flags, double z_add)

View File

@ -1185,13 +1185,27 @@ WeaponNames
// when parsing a DEHACKED lump.
Aliases
{
A_Mushroom, A_Mushroom, 5,
A_Spawn, A_SpawnItem, 5,
A_Turn, A_Turn, 1,
A_Face, A_SetAngle, 1,
A_Scratch, A_CustomMeleeAttack, 5, // 19 characters for "A_CustomMeleeAttack"!
A_PlaySound, A_PlaySound, 5, // A function name any longer and the definition
A_RandomJump, A_Jump, 3, // for CodePointerAlias would need to be updated.
A_LineEffect, A_LineEffect, 2,
A_NailBomb, A_Explode, 7,
A_Mushroom, A_Mushroom,
A_Spawn, A_SpawnItem,
A_Turn, A_Turn,
A_Face, A_SetAngle,
A_Scratch, A_CustomMeleeAttack,
A_PlaySound, A_PlaySound,
A_RandomJump, A_Jump,
A_LineEffect, A_LineEffect,
A_NailBomb, A_Explode,
A_SpawnObject, MBF21_SpawnObject,
A_MonsterProjectile, MBF21_MonsterProjectile,
A_MonsterBulletAttack, MBF21_MonsterBulletAttack,
A_MonsterMeleeAttack, MBF21_MonsterMeleeAttack
A_RadiusDamage, A_Explode,
A_NoiseAlert, SoundAlert,
A_HealChase, MBF21_HealChase,
A_SeekTracer, A_SeekerMissile,
A_FindTracer, A_FindTracer,
A_JumpIfHealthBelow, MBF21_JumpIfHealthBelow,
A_JumpIfTracerInSight, MBF21_JumpIfTracerInSight,
A_JumpIfTracerCloser, MBF21_JumpIfTracerCloser,
A_JumpIfTracerInSight, MBF21_JumpIfTracerInSight,
A_JumpIfTracerCloser, MBF21_JumpIfTracerCloser,
};

View File

@ -46,6 +46,7 @@ version "4.5"
#include "zscript/actors/actions.zs"
#include "zscript/actors/attacks.zs"
#include "zscript/actors/morph.zs"
#include "zscript/actors/mbf21.zs"
#include "zscript/actors/inventory/inventory.zs"
#include "zscript/actors/inventory/inv_misc.zs"

View File

@ -682,10 +682,11 @@ class Actor : Thinker native
native Actor SpawnSubMissile(Class<Actor> type, Actor target);
native Actor, Actor SpawnPlayerMissile(class<Actor> type, double angle = 1e37, double x = 0, double y = 0, double z = 0, out FTranslatedLineTarget pLineTarget = null, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0);
native void SpawnTeleportFog(Vector3 pos, bool beforeTele, bool setTarget);
native Actor RoughMonsterSearch(int distance, bool onlyseekable = false, bool frontonly = false);
native Actor RoughMonsterSearch(int distance, bool onlyseekable = false, bool frontonly = false, double fov = 0);
native int ApplyDamageFactor(Name damagetype, int damage);
native int GetModifiedDamage(Name damagetype, int damage, bool passive, Actor inflictor = null, Actor source = null, int flags = 0);
native bool CheckBossDeath();
native bool CheckFov(Actor target, double fov);
void A_Light(int extralight) { if (player) player.extralight = clamp(extralight, -20, 20); }
void A_Light0() { if (player) player.extralight = 0; }
@ -706,7 +707,7 @@ class Actor : Thinker native
native void TraceBleedAngle(int damage, double angle, double pitch);
native void SetIdle(bool nofunction = false);
native bool CheckMeleeRange();
native bool CheckMeleeRange(double range = -1);
native bool TriggerPainChance(Name mod, bool forcedPain = false);
native virtual int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0);
native void PoisonMobj (Actor inflictor, Actor source, int damage, int duration, int period, Name type);
@ -759,6 +760,7 @@ class Actor : Thinker native
native clearscope vector2 Vec2Angle(double length, double angle, bool absolute = false) const;
native clearscope vector2 Vec2Offset(double x, double y, bool absolute = false) const;
native clearscope vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false) const;
native double PitchFromVel();
native void VelIntercept(Actor targ, double speed = -1, bool aimpitch = true, bool oldvel = false, bool resetvel = false);
native void VelFromAngle(double speed = 1e37, double angle = 1e37);
native void Vel3DFromAngle(double speed, double angle, double pitch);
@ -1061,7 +1063,7 @@ class Actor : Thinker native
native void A_Look();
native void A_Chase(statelabel melee = '_a_chase_default', statelabel missile = '_a_chase_default', int flags = 0);
native void A_VileChase();
native bool A_CheckForResurrection();
native bool A_CheckForResurrection(State state = null, Sound snd = 0);
native void A_BossDeath();
bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0)
{

View File

@ -80,6 +80,7 @@ class Weapon : StateProvider
flagdef NoDeathDeselect: WeaponFlags, 16; // Don't jump to the Deselect state when the player dies
flagdef NoDeathInput: WeaponFlags, 17; // The weapon cannot be fired/reloaded/whatever when the player is dead
flagdef CheatNotWeapon: WeaponFlags, 18; // Give cheat considers this not a weapon (used by Sigil)
flagdef NoAutoSwitchTo : WeaponFlags, 19; // do not auto switch to this weapon ever!
// no-op flags
flagdef NoLMS: none, 0;

View File

@ -0,0 +1,288 @@
// no attempt has been made to merge this with existing code.
extend class Actor
{
//
// [XA] New mbf21 codepointers
//
//
// A_SpawnObject
// Basically just A_Spawn with better behavior and more args.
// args[0]: Type of actor to spawn
// args[1]: Angle (degrees, in fixed point), relative to calling actor's angle
// args[2]: X spawn offset (fixed point), relative to calling actor
// args[3]: Y spawn offset (fixed point), relative to calling actor
// args[4]: Z spawn offset (fixed point), relative to calling actor
// args[5]: X velocity (fixed point)
// args[6]: Y velocity (fixed point)
// args[7]: Z velocity (fixed point)
//
deprecated("2.3", "for Dehacked use only")
void MBF21_SpawnObject(class<Actor> type, double angle, double xofs, double yofs, double zofs, double xvel, double yvel, double zvel)
{
if (type == null)
return;
// Don't spawn monsters if this actor has been massacred
if (DamageType == 'Massacre' && GetDefaultByType(type).bIsMonster)
{
return;
}
// calculate position offsets
angle += self.Angle;
double s = sin(angle);
double c = cos(angle);
let pos = Vec2Offset(xofs * c - yofs * s, xofs * s + yofs * c);
// spawn it, yo
let mo = Spawn(type, (pos, self.pos.Z - Floorclip + GetBobOffset() + zofs), ALLOW_REPLACE);
if (!mo)
return;
// angle dangle
mo.angle = angle;
// set velocity
// Same orientation issue here!
mo.vel.X = xvel * c - yvel * s;
mo.vel.Y = xvel * s + yvel * c;
mo.vel.Z = zvel;
// if spawned object is a missile, set target+tracer
if (mo.bMissile || mo.bMbfBouncer)
{
// if spawner is also a missile, copy 'em
if (bMissile || bMbfBouncer)
{
mo.target = self.target;
mo.tracer = self.tracer;
}
// otherwise, set 'em as if a monster fired 'em
else
{
mo.target = self;
mo.tracer = self.target;
}
}
// [XA] don't bother with the dont-inherit-friendliness hack
// that exists in A_Spawn, 'cause WTF is that about anyway?
// unfortunately this means that this function cannot transfer friendliness at all. Oh well...
}
//
// A_MonsterProjectile
// A parameterized monster projectile attack.
// args[0]: Type of actor to spawn
// args[1]: Angle (degrees, in fixed point), relative to calling actor's angle
// args[2]: Pitch (degrees, in fixed point), relative to calling actor's pitch; approximated
// args[3]: X/Y spawn offset, relative to calling actor's angle
// args[4]: Z spawn offset, relative to actor's default projectile fire height
//
deprecated("2.3", "for Dehacked use only")
void MBF21_MonsterProjectile(class<Actor> type, double angle, double pitch, double spawnofs_xy, double spawnofs_z)
{
if (!target || !type)
return;
A_FaceTarget();
let mo = SpawnMissile(target, type);
if (!mo)
return;
// adjust angle
mo.angle += angle;
Pitch += mo.PitchFromVel();
let missilespeed = abs(cos(Pitch) * mo.Speed);
mo.Vel3DFromAngle(mo.Speed, mo.angle, Pitch);
// adjust position
double x = Spawnofs_xy * cos(self.angle);
double y = Spawnofs_xy * sin(self.angle);
mo.SetOrigin(mo.Vec3Offset(x, y, Spawnofs_z), false);
// always set the 'tracer' field, so this pointer
// can be used to fire seeker missiles at will.
mo.tracer = target;
}
//
// A_MonsterBulletAttack
// A parameterized monster bullet attack.
// args[0]: Horizontal spread (degrees, in fixed point)
// args[1]: Vertical spread (degrees, in fixed point)
// args[2]: Number of bullets to fire; if not set, defaults to 1
// args[3]: Base damage of attack (e.g. for 3d5, customize the 3); if not set, defaults to 3
// args[4]: Attack damage modulus (e.g. for 3d5, customize the 5); if not set, defaults to 5
//
deprecated("2.3", "for Dehacked use only")
void MBF21_MonsterBulletAttack(double hspread, double vspread, int numbullets, int damagebase, int damagemod)
{
if (!target)
return;
A_FaceTarget();
A_StartSound(AttackSound, CHAN_WEAPON);
let bangle = angle;
let slope = AimLineAttack(bangle, MISSILERANGE);
for (int i = 0; i < numbullets; i++)
{
int damage = (random[mbf21]() % damagemod + 1) * damagebase;
let pangle = bangle + hspread * Random2[mbf21]() / 255.;
let pslope = slope + vspread * Random2[mbf21]() / 255.;
LineAttack(pangle, MISSILERANGE, pslope, damage, "Hitscan", "Bulletpuff");
}
}
//
// A_MonsterMeleeAttack
// A parameterized monster melee attack.
// args[0]: Base damage of attack (e.g. for 3d8, customize the 3); if not set, defaults to 3
// args[1]: Attack damage modulus (e.g. for 3d8, customize the 8); if not set, defaults to 8
// args[2]: Sound to play if attack hits
// args[3]: Range (fixed point); if not set, defaults to monster's melee range
//
deprecated("2.3", "for Dehacked use only")
void MBF21_MonsterMeleeAttack(int damagebase, int damagemod, Sound hitsound, double range)
{
let targ = target;
if (!targ)
return;
if (range == 0) range = meleerange;
else range -= 20; // DSDA always subtracts 20 from the melee range.
A_FaceTarget();
if (!CheckMeleeRange(range))
return;
A_StartSound(hitsound, CHAN_WEAPON);
int damage = (random[mbf21]() % damagemod + 1) * damagebase;
int newdam = targ.DamageMobj(self, self, damage, "Melee");
targ.TraceBleed(newdam > 0 ? newdam : damage, self);
}
//
// A_HealChase
// A parameterized version of A_VileChase.
// args[0]: State to jump to on the calling actor when resurrecting a corpse
// args[1]: Sound to play when resurrecting a corpse
//
deprecated("2.3", "for Dehacked use only")
void MBF21_HealChase(State healstate, Sound healsound)
{
if (!A_CheckForResurrection(healstate, healsound))
A_Chase();
}
//
// A_FindTracer
// Search for a valid tracer (seek target), if the calling actor doesn't already have one.
// args[0]: field-of-view to search in (degrees, in fixed point); if zero, will search in all directions
// args[1]: distance to search (map blocks, i.e. 128 units)
//
void A_FindTracer(double fov, int dist)
{
if (!tracer) tracer = RoughMonsterSearch(dist, fov: fov);
}
//
// A_ClearTracer
// Clear current tracer (seek target).
//
void A_ClearTracer()
{
tracer = NULL;
}
//
// A_JumpIfHealthBelow
// Jumps to a state if caller's health is below the specified threshold.
// args[0]: State to jump to
// args[1]: Health threshold
//
deprecated("2.3", "for Dehacked use only")
void MBF21_JumpIfHealthBelow(State tstate, int health)
{
if (self.health < health) self.SetState(tstate);
}
//
// A_JumpIfTargetInSight
// Jumps to a state if caller's target is in line-of-sight.
// args[0]: State to jump to
// args[1]: Field-of-view to check (degrees, in fixed point); if zero, will check in all directions
//
deprecated("2.3", "for Dehacked use only")
void MBF21_JumpIfTargetInSight(State tstate, double fov)
{
if (!target)
return;
// Check FOV first since it's faster
if (fov > 0 && !CheckFov(target, fov))
return;
if (CheckSight(target)) self.SetState(tstate);
}
//
// A_JumpIfTargetCloser
// Jumps to a state if caller's target is closer than the specified distance.
// args[0]: State to jump to
// args[1]: Distance threshold
//
deprecated("2.3", "for Dehacked use only")
void MBF21_JumpIfTargetCloser(State tstate, double dist)
{
if (!target)
return;
if (dist > Distance2D(target)) self.SetState(tstate);
}
//
// A_JumpIfTracerInSight
// Jumps to a state if caller's tracer (seek target) is in line-of-sight.
// args[0]: State to jump to
// args[1]: Field-of-view to check (degrees, in fixed point); if zero, will check in all directions
//
deprecated("2.3", "for Dehacked use only")
void MBF21_JumpIfTracerInSight(State tstate, double fov)
{
if (!tracer)
return;
// Check FOV first since it's faster
if (fov > 0 && !CheckFov(tracer, fov))
return;
if (CheckSight(tracer)) self.SetState(tstate);
}
//
// A_JumpIfTracerCloser
// Jumps to a state if caller's tracer (seek target) is closer than the specified distance.
// args[0]: State to jump to
// args[1]: Distance threshold (fixed point)
//
deprecated("2.3", "for Dehacked use only")
void MBF21_JumpIfTracerCloser(State tstate, double dist)
{
if (!tracer)
return;
if (dist > Distance2D(tracer)) self.SetState(tstate);
}
// These are native to lock away the insanity.
deprecated("2.3", "for Dehacked use only") native void MBF21_JumpIfFlagsSet(State tstate, int flags, int flags2);
deprecated("2.3", "for Dehacked use only") native void MBF21_AddFlags(int flags, int flags2);
deprecated("2.3", "for Dehacked use only") native void MBF21_RemoveFlags(int flags, int flags2);
}

View File

@ -333,8 +333,8 @@ class PlayerPawn : Actor
(player.ReadyWeapon == NULL || player.ReadyWeapon.bWimpy_Weapon))
{
let best = BestWeapon (ammotype);
if (best != NULL && && !best.bNoAutoSwitchTo && (player.ReadyWeapon == NULL ||
best.SelectionOrder < player.ReadyWeapon.SelectionOrder))
if (best != NULL && !best.bNoAutoSwitchTo &&
(player.ReadyWeapon == NULL || best.SelectionOrder < player.ReadyWeapon.SelectionOrder))
{
player.PendingWeapon = best;
}