This commit is contained in:
nashmuhandes 2024-10-06 21:29:38 +08:00
commit 58ddcd4806
65 changed files with 1310 additions and 332 deletions

View file

@ -19,6 +19,8 @@ LineEdit::LineEdit(Widget* parent) : Widget(parent)
LineEdit::~LineEdit()
{
delete timer;
delete scroll_timer;
}
bool LineEdit::IsReadOnly() const

View file

@ -114,6 +114,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
// texture to behave like an impassable line (projectiles
// pass through it).
checkswitchrange = <bool>; // Switches can only be activated when vertically reachable.
walking = <bool>; // Crossing activation checks if creature is on the ground, and fails if they are not.
blockprojectiles = <bool>; // Line blocks all projectiles
blockuse = <bool>; // Line blocks all use actions
blocksight = <bool>; // Line blocks monster line of sight
@ -306,6 +307,8 @@ Note: All <bool> fields default to false unless mentioned otherwise.
leakiness = <int>; // Probability of leaking through radiation suit (0 = never, 256 = always), default = 0.
damageterraineffect = <bool>; // Will spawn a terrain splash when damage is inflicted. Default = false.
damagehazard = <bool>; // Changes damage model to Strife's delayed damage for the given sector. Default = false.
hurtmonsters = <bool>; // Non-players like monsters and decorations are hurt by this sector in the same manner as player. Doesn't work with damagehazard.
harminair = <bool>; // Actors in this sector are harmed by the damage effects of the floor even if they aren't touching it.
floorterrain = <string>; // Sets the terrain for the sector's floor. Default = 'use the flat texture's terrain definition.'
ceilingterrain = <string>; // Sets the terrain for the sector's ceiling. Default = 'use the flat texture's terrain definition.'
floor_reflect = <float>; // reflectiveness of floor (OpenGL only, not functional on sloped sectors)

View file

@ -592,6 +592,10 @@ void DShape2D::OnDestroy() {
void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
{
// bail if shape is null (shouldn't happen but it might)
if (!shape)
ThrowAbortException(X_OTHER, "shape is null");
// [MK] bail out if vertex/coord array sizes are mismatched
if ( shape->mVertices.Size() != shape->mCoords.Size() )
ThrowAbortException(X_OTHER, "Mismatch in vertex/coord count: %u != %u", shape->mVertices.Size(), shape->mCoords.Size());

View file

@ -130,6 +130,24 @@ static FileReader OpenMusic(const char* musicname)
return reader;
}
bool MusicExists(const char* music_name)
{
if (music_name == nullptr)
return false;
if (FileExists(music_name))
return true;
else
{
int lumpnum;
lumpnum = mus_cb.FindMusic(music_name);
if (lumpnum == -1) lumpnum = fileSystem.CheckNumForName(music_name, FileSys::ns_music);
if (lumpnum != -1 && fileSystem.FileLength(lumpnum) != 0)
return true;
}
return false;
}
void S_SetMusicCallbacks(MusicCallbacks* cb)
{
mus_cb = *cb;

View file

@ -45,6 +45,9 @@ bool S_StartMusic (const char *music_name);
// Start music using <music_name>, and set whether looping
bool S_ChangeMusic (const char *music_name, int order=0, bool looping=true, bool force=false);
// Check if <music_name> exists
bool MusicExists(const char* music_name);
void S_RestartMusic ();
void S_MIDIDeviceChanged(int newdev);

View file

@ -278,6 +278,8 @@ xx(BuiltinNameToClass)
xx(BuiltinClassCast)
xx(BuiltinFunctionPtrCast)
xx(BuiltinFindTranslation)
xx(HandleDeprecatedFlags)
xx(CheckDeprecatedFlags)
xx(ScreenJobRunner)
xx(Action)

View file

@ -197,7 +197,10 @@ void FSerializer::Close()
}
if (mErrors > 0)
{
I_Error("%d errors parsing JSON", mErrors);
if (mLumpName.IsNotEmpty())
I_Error("%d errors parsing JSON lump %s", mErrors, mLumpName.GetChars());
else
I_Error("%d errors parsing JSON", mErrors);
}
}
@ -331,6 +334,28 @@ bool FSerializer::HasObject(const char* name)
//
//==========================================================================
bool FSerializer::IsKeyNull(const char* name)
{
if (isReading())
{
auto val = r->FindKey(name);
if (val != nullptr)
{
if (val->IsNull())
{
return true;
}
}
}
return false;
}
//==========================================================================
//
//
//
//==========================================================================
void FSerializer::EndObject()
{
if (isWriting())

View file

@ -94,6 +94,7 @@ public:
void EndObject();
bool HasKey(const char* name);
bool HasObject(const char* name);
bool IsKeyNull(const char* name);
bool BeginArray(const char *name);
void EndArray();
unsigned GetSize(const char *group);
@ -225,6 +226,7 @@ public:
int mErrors = 0;
int mObjectErrors = 0;
FString mLumpName;
};
FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval);

View file

@ -2828,83 +2828,104 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx)
// Special case: Assignment to a bitfield.
IsBitWrite = Base->GetBitValue();
if (IsBitWrite >= 0x10000)
{
// internal flags - need more here
IsBitWrite &= 0xffff;
}
return this;
}
ExpEmit FxAssign::Emit(VMFunctionBuilder *build)
{
static const uint8_t loadops[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP };
assert(Base->ValueType->GetRegType() == Right->ValueType->GetRegType());
ExpEmit pointer = Base->Emit(build);
Address = pointer;
ExpEmit result;
bool intconst = false;
int intconstval = 0;
if (Right->isConstant() && Right->ValueType->GetRegType() == REGT_INT)
if (IsBitWrite < 64)
{
intconst = true;
intconstval = static_cast<FxConstant*>(Right)->GetValue().GetInt();
result.Konst = true;
result.RegType = REGT_INT;
}
else
{
result = Right->Emit(build);
}
assert(result.RegType <= REGT_TYPE);
static const uint8_t loadops[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP };
assert(Base->ValueType->GetRegType() == Right->ValueType->GetRegType());
if (pointer.Target)
{
if (result.Konst)
ExpEmit pointer = Base->Emit(build);
Address = pointer;
ExpEmit result;
bool intconst = false;
int intconstval = 0;
if (Right->isConstant() && Right->ValueType->GetRegType() == REGT_INT)
{
if (intconst) build->EmitLoadInt(pointer.RegNum, intconstval);
else build->Emit(loadops[result.RegType], pointer.RegNum, result.RegNum);
intconst = true;
intconstval = static_cast<FxConstant*>(Right)->GetValue().GetInt();
result.Konst = true;
result.RegType = REGT_INT;
}
else
{
build->Emit(Right->ValueType->GetMoveOp(), pointer.RegNum, result.RegNum);
result = Right->Emit(build);
}
}
else
{
if (result.Konst)
assert(result.RegType <= REGT_TYPE);
if (pointer.Target)
{
if (result.Konst)
{
if (intconst) build->EmitLoadInt(pointer.RegNum, intconstval);
else build->Emit(loadops[result.RegType], pointer.RegNum, result.RegNum);
}
else
{
build->Emit(Right->ValueType->GetMoveOp(), pointer.RegNum, result.RegNum);
}
}
else
{
if (result.Konst)
{
ExpEmit temp(build, result.RegType);
if (intconst) build->EmitLoadInt(temp.RegNum, intconstval);
else build->Emit(loadops[result.RegType], temp.RegNum, result.RegNum);
result.Free(build);
result = temp;
}
if (IsBitWrite == -1)
{
build->Emit(Base->ValueType->GetStoreOp(), pointer.RegNum, result.RegNum, build->GetConstantInt(0));
}
else
{
build->Emit(OP_SBIT, pointer.RegNum, result.RegNum, 1 << IsBitWrite);
}
}
if (AddressRequested)
{
ExpEmit temp(build, result.RegType);
if (intconst) build->EmitLoadInt(temp.RegNum, intconstval);
else build->Emit(loadops[result.RegType], temp.RegNum, result.RegNum);
result.Free(build);
result = temp;
return pointer;
}
if (IsBitWrite == -1)
{
build->Emit(Base->ValueType->GetStoreOp(), pointer.RegNum, result.RegNum, build->GetConstantInt(0));
pointer.Free(build);
if (intconst)
{ //fix int constant return for assignment
return Right->Emit(build);
}
else
{
build->Emit(OP_SBIT, pointer.RegNum, result.RegNum, 1 << IsBitWrite);
return result;
}
}
if (AddressRequested)
{
result.Free(build);
return pointer;
}
pointer.Free(build);
if(intconst)
{ //fix int constant return for assignment
return Right->Emit(build);
}
else
{
return result;
VMFunction* callfunc;
auto sym = FindBuiltinFunction(NAME_HandleDeprecatedFlags);
assert(sym);
callfunc = sym->Variants[0].Implementation;
FunctionCallEmitter emitters(callfunc);
emitters.AddParameter(build, Base);
emitters.AddParameter(build, Right);
emitters.AddParameterIntConst(IsBitWrite - 64);
return emitters.EmitCall(build);
}
}
@ -2934,23 +2955,40 @@ FxExpression *FxAssignSelf::Resolve(FCompileContext &ctx)
ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build)
{
ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it
if (!pointer.Target)
if (Assignment->IsBitWrite < 64)
{
ExpEmit out(build, ValueType->GetRegType(), ValueType->GetRegCount());
if (Assignment->IsBitWrite != -1)
ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it
if (!pointer.Target)
{
build->Emit(OP_LBIT, out.RegNum, pointer.RegNum, 1 << Assignment->IsBitWrite);
ExpEmit out(build, ValueType->GetRegType(), ValueType->GetRegCount());
if (Assignment->IsBitWrite == -1)
{
build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, build->GetConstantInt(0));
}
else
{
build->Emit(OP_LBIT, out.RegNum, pointer.RegNum, 1 << Assignment->IsBitWrite);
}
return out;
}
else
{
build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, build->GetConstantInt(0));
return pointer;
}
return out;
}
else
{
return pointer;
VMFunction* callfunc;
auto sym = FindBuiltinFunction(NAME_CheckDeprecatedFlags);
assert(sym);
callfunc = sym->Variants[0].Implementation;
FunctionCallEmitter emitters(callfunc);
emitters.AddParameter(build, Assignment->Base);
emitters.AddParameterIntConst(Assignment->IsBitWrite - 64);
emitters.AddReturn(REGT_INT);
return emitters.EmitCall(build);
}
}
@ -7728,56 +7766,73 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
ExpEmit FxStructMember::Emit(VMFunctionBuilder *build)
{
ExpEmit obj = classx->Emit(build);
assert(obj.RegType == REGT_POINTER);
if (obj.Konst)
if (membervar->BitValue < 64 || AddressRequested)
{
// If the situation where we are dereferencing a constant
// pointer is common, then it would probably be worthwhile
// to add new opcodes for those. But as of right now, I
// don't expect it to be a particularly common case.
ExpEmit newobj(build, REGT_POINTER);
build->Emit(OP_LKP, newobj.RegNum, obj.RegNum);
obj = newobj;
}
ExpEmit obj = classx->Emit(build);
assert(obj.RegType == REGT_POINTER);
if (membervar->Flags & VARF_Meta)
{
obj.Free(build);
ExpEmit meta(build, REGT_POINTER);
build->Emit(OP_META, meta.RegNum, obj.RegNum);
obj = meta;
}
if (AddressRequested)
{
if (membervar->Offset == 0)
if (obj.Konst)
{
return obj;
// If the situation where we are dereferencing a constant
// pointer is common, then it would probably be worthwhile
// to add new opcodes for those. But as of right now, I
// don't expect it to be a particularly common case.
ExpEmit newobj(build, REGT_POINTER);
build->Emit(OP_LKP, newobj.RegNum, obj.RegNum);
obj = newobj;
}
if (membervar->Flags & VARF_Meta)
{
obj.Free(build);
ExpEmit meta(build, REGT_POINTER);
build->Emit(OP_META, meta.RegNum, obj.RegNum);
obj = meta;
}
if (AddressRequested)
{
if (membervar->Offset == 0)
{
return obj;
}
obj.Free(build);
ExpEmit out(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, out.RegNum, obj.RegNum, build->GetConstantInt((int)membervar->Offset));
return out;
}
int offsetreg = build->GetConstantInt((int)membervar->Offset);
ExpEmit loc(build, membervar->Type->GetRegType(), membervar->Type->GetRegCount());
if (membervar->BitValue == -1)
{
build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, obj.RegNum, offsetreg);
}
else
{
ExpEmit out(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, out.RegNum, obj.RegNum, offsetreg);
build->Emit(OP_LBIT, loc.RegNum, out.RegNum, 1 << membervar->BitValue);
out.Free(build);
}
obj.Free(build);
ExpEmit out(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, out.RegNum, obj.RegNum, build->GetConstantInt((int)membervar->Offset));
return out;
}
int offsetreg = build->GetConstantInt((int)membervar->Offset);
ExpEmit loc(build, membervar->Type->GetRegType(), membervar->Type->GetRegCount());
if (membervar->BitValue == -1)
{
build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, obj.RegNum, offsetreg);
return loc;
}
else
{
ExpEmit out(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, out.RegNum, obj.RegNum, offsetreg);
build->Emit(OP_LBIT, loc.RegNum, out.RegNum, 1 << membervar->BitValue);
out.Free(build);
VMFunction* callfunc;
auto sym = FindBuiltinFunction(NAME_CheckDeprecatedFlags);
assert(sym);
callfunc = sym->Variants[0].Implementation;
FunctionCallEmitter emitters(callfunc);
emitters.AddParameter(build, classx);
emitters.AddParameterIntConst(membervar->BitValue - 64);
emitters.AddReturn(REGT_INT);
return emitters.EmitCall(build);
}
obj.Free(build);
return loc;
}

View file

@ -330,7 +330,8 @@ void PType::StaticInit()
TypeVoidPtr = NewPointer(TypeVoid, false);
TypeRawFunction = new PPointer;
TypeRawFunction->mDescriptiveName = "Raw Function Pointer";
TypeRawFunction->mDescriptiveName = "Raw Function Pointer";
TypeTable.AddType(TypeRawFunction, NAME_None);
TypeVMFunction = NewPointer(NewStruct("VMFunction", nullptr, true));
TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
TypeStringStruct = NewStruct("Stringstruct", nullptr, true);

View file

@ -1162,6 +1162,17 @@ DEFINE_ACTION_FUNCTION(_Console, PrintfEx)
return 0;
}
DEFINE_ACTION_FUNCTION(_Console, DebugPrintf)
{
PARAM_PROLOGUE;
PARAM_INT(debugLevel);
PARAM_VA_POINTER(va_reginfo);
FString s = FStringFormat(VM_ARGS_NAMES, 1);
DPrintf(debugLevel, "%s\n", s.GetChars());
return 0;
}
static void StopAllSounds()
{
soundEngine->StopAllChannels();

View file

@ -372,6 +372,13 @@ FStartScreen* GetGameStartScreen(int max_progress)
return nullptr;
}
FStartScreen::~FStartScreen()
{
if (StartupTexture) delete StartupTexture;
if (HeaderTexture) delete HeaderTexture;
if (NetTexture) delete NetTexture;
}
//==========================================================================
//
// ST_Util_ClearBlock

View file

@ -70,7 +70,7 @@ protected:
FGameTexture* NetTexture = nullptr;
public:
FStartScreen(int maxp) { MaxPos = maxp; }
virtual ~FStartScreen() = default;
virtual ~FStartScreen();
void Render(bool force = false);
bool Progress(int);
void NetProgress(int count);

View file

@ -164,6 +164,7 @@ bool M_SetSpecialMenu(FName& menu, int param); // game specific checks
const FIWADInfo *D_FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad);
void InitWidgetResources(const char* basewad);
void CloseWidgetResources();
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
@ -3938,6 +3939,7 @@ int GameMain()
M_SaveDefaultsFinal();
DeleteStartupScreen();
C_UninitCVars(); // must come last so that nothing will access the CVARs anymore after deletion.
CloseWidgetResources();
delete Args;
Args = nullptr;
return ret;
@ -4030,7 +4032,7 @@ void D_Cleanup()
//
//==========================================================================
UNSAFE_CCMD(restart)
UNSAFE_CCMD(debug_restart)
{
// remove command line args that would get in the way during restart
Args->RemoveArgs("-iwad");

View file

@ -199,6 +199,7 @@ enum SPAC
SPAC_UseBack = 1<<10, // Can be used from the backside
SPAC_Damage = 1<<11, // [ZZ] when linedef receives damage
SPAC_Death = 1<<12, // [ZZ] when linedef receives damage and has 0 health
SPAC_Walking = 1<<13, // Can only be used when standing on a floor (not falling or floating) - only for line cross, not use
SPAC_PlayerActivate = (SPAC_Cross|SPAC_Use|SPAC_Impact|SPAC_Push|SPAC_AnyCross|SPAC_UseThrough|SPAC_UseBack),
};

View file

@ -125,6 +125,9 @@ FGameConfigFile::FGameConfigFile ()
SetValueForKey ("Path", "/usr/local/share/games/doom", true);
SetValueForKey ("Path", "/usr/share/doom", true);
SetValueForKey ("Path", "/usr/share/games/doom", true);
SetValueForKey ("Path", SHARE_DIR "/doom", true);
SetValueForKey ("Path", SHARE_DIR "/games/doom", true);
#endif
}
@ -143,6 +146,8 @@ FGameConfigFile::FGameConfigFile ()
SetValueForKey ("Path", "$HOME/" GAME_DIR, true);
SetValueForKey ("Path", "$HOME/.local/share/games/doom", true);
SetValueForKey ("Path", SHARE_DIR, true);
SetValueForKey ("Path", SHARE_DIR "/doom", true);
SetValueForKey ("Path", SHARE_DIR "/games/doom", true);
SetValueForKey ("Path", "/usr/local/share/doom", true);
SetValueForKey ("Path", "/usr/local/share/games/doom", true);
SetValueForKey ("Path", "/usr/share/doom", true);
@ -180,6 +185,11 @@ FGameConfigFile::FGameConfigFile ()
SetValueForKey("Path", "/usr/share/doom/fm_banks", true);
SetValueForKey("Path", "/usr/share/games/doom/soundfonts", true);
SetValueForKey("Path", "/usr/share/games/doom/fm_banks", true);
SetValueForKey("Path", SHARE_DIR "/doom/soundfonts", true);
SetValueForKey("Path", SHARE_DIR "/doom/fm_banks", true);
SetValueForKey("Path", SHARE_DIR "/games/doom/soundfonts", true);
SetValueForKey("Path", SHARE_DIR "/games/doom/fm_banks", true);
SetValueForKey("Path", "/usr/share/soundfonts", true);
#endif
}

View file

@ -1153,7 +1153,7 @@ static const struct DehFlags2 deh_mobjflags_mbf21[] = {
{"NORADIUSDMG", [](AActor* defaults) { defaults->flags3 |= MF3_NORADIUSDMG; }}, // doesn't take splash damage
{"FORCERADIUSDMG", [](AActor* defaults) { defaults->flags4 |= MF4_FORCERADIUSDMG; }}, // causes splash damage even if target immune
{"HIGHERMPROB", [](AActor* defaults) { defaults->MinMissileChance = 160; }}, // higher missile attack probability
{"RANGEHALF", [](AActor* defaults) { defaults->flags4 |= MF4_MISSILEMORE; }}, // use half distance for missile attack probability
{"RANGEHALF", [](AActor* defaults) { defaults->missilechancemult = 0.5; }}, // use half distance for missile attack probability
{"NOTHRESHOLD", [](AActor* defaults) { defaults->flags4 |= MF4_QUICKTORETALIATE; }}, // no targeting threshold
{"LONGMELEE", [](AActor* defaults) { defaults->meleethreshold = 196; }}, // long melee range
{"BOSS", [](AActor* defaults) { defaults->flags2 |= MF2_BOSS; defaults->flags3 |= MF3_NORADIUSDMG; }}, // full volume see / death sound + splash immunity
@ -1174,9 +1174,10 @@ static void ClearBits2Stuff(AActor* defaults)
defaults->maxtargetrange = 0;
defaults->MinMissileChance = 200;
defaults->meleethreshold = 0;
defaults->missilechancemult = 1;
defaults->flags2 &= ~(MF2_BOSS | MF2_RIP);
defaults->flags3 &= ~(MF3_NOTARGET | MF3_NORADIUSDMG | MF3_FULLVOLDEATH);
defaults->flags4 &= ~(MF4_MISSILEMORE | MF4_QUICKTORETALIATE | MF4_FORCERADIUSDMG);
defaults->flags4 &= ~(MF4_QUICKTORETALIATE | MF4_FORCERADIUSDMG);
defaults->flags8 &= ~(MF8_E1M8BOSS | MF8_E2M8BOSS | MF8_E3M8BOSS | MF8_E4M8BOSS | MF8_E4M6BOSS | MF8_MAP07BOSS1 | MF8_MAP07BOSS2 | MF8_FULLVOLSEE);
}
@ -1437,6 +1438,27 @@ static int PatchThing (int thingy, int flags)
}
DPrintf(DMSG_SPAMMY, "MBF21 Bits: %d (0x%08x)\n", info->flags.GetValue(), info->flags.GetValue());
}
// New fields from Crispy Doom
else if (!stricmp(Line1, "Melee threshold"))
{
info->meleethreshold = DEHToDouble(val);
}
else if (!stricmp(Line1, "Max target range"))
{
// [crispy] Maximum distance range to start shooting (zero for unlimited)
info->maxtargetrange = DEHToDouble(val);
}
else if (!stricmp(Line1, "Min missile chance"))
{
// [crispy] Minimum chance for firing a missile
info->MinMissileChance = DEHToDouble(val);
}
else if (!stricmp(Line1, "Missile chance multiplier"))
{
// [crispy] Multiplies the chance of firing a missile (65536 = normal chance)
info->missilechancemult = DEHToDouble(val);
}
else if (linelen > 6)
{
if (stricmp (Line1 + linelen - 6, " frame") == 0)
@ -3861,7 +3883,7 @@ struct FlagHandler
#define F4(flag) { [](AActor* a) { a->flags4 |= flag; }, [](AActor* a) { a->flags4 &= ~flag; }, [](AActor* a)->bool { return a->flags4 & flag; } }
#define F6(flag) { [](AActor* a) { a->flags6 |= flag; }, [](AActor* a) { a->flags6 &= ~flag; }, [](AActor* a)->bool { return a->flags6 & 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); } }
#define DEPF(flag) { [](AActor* a) { HandleDeprecatedFlags(a, true, flag); }, [](AActor* a) { HandleDeprecatedFlags(a, false, flag); }, [](AActor* a)->bool { return !!CheckDeprecatedFlags(a, flag); } }
void SetNoSector(AActor* a)
{
@ -3930,7 +3952,7 @@ void ClearCountitem(AActor* a)
{
if (a->flags & MF_COUNTITEM)
{
a->flags |= MF_COUNTITEM;
a->flags &= ~MF_COUNTITEM;
a->Level->total_items--;
}
}
@ -3939,6 +3961,7 @@ void SetFriendly(AActor* a)
{
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters--;
a->flags |= MF_FRIENDLY;
a->flags3 |= MF3_NOBLOCKMONST;
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters++;
}
@ -3946,6 +3969,7 @@ void ClearFriendly(AActor* a)
{
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters--;
a->flags &= ~MF_FRIENDLY;
a->flags3 &= ~MF3_NOBLOCKMONST;
if (a->CountsAsKill() && a->health > 0) a->Level->total_monsters++;
}
@ -4101,7 +4125,7 @@ static FlagHandler flag1handlers[32] = {
{ SetTranslation2, ClearTranslation2, CheckTranslation2 },
F6(MF6_TOUCHY),
{ SetBounces, ClearBounces, [](AActor* a)->bool { return a->BounceFlags & BOUNCE_DEH; } },
F(MF_FRIENDLY),
{ SetFriendly, ClearFriendly, [](AActor* a)->bool { return a->flags & MF_FRIENDLY; } },
{ SetTranslucent, ClearTranslucent, CheckTranslucent },
};
@ -4112,7 +4136,7 @@ static FlagHandler flag2handlers[32] = {
F3(MF3_NORADIUSDMG),
F4(MF4_FORCERADIUSDMG),
DEPF(DEPF_HIGHERMPROB),
F4(MF4_MISSILEMORE),
DEPF(DEPF_MISSILEMORE),
F4(MF4_QUICKTORETALIATE),
DEPF(DEPF_LONGMELEERANGE),
{ SetBoss, ClearBoss, [](AActor* a)->bool { return a->flags2 & MF2_BOSS; } },

View file

@ -164,6 +164,20 @@ bool CheckWarpTransMap (FString &mapname, bool substitute)
//
//==========================================================================
bool SecretLevelVisited()
{
for (unsigned int i = 0; i < wadlevelinfos.Size(); i++)
if ((wadlevelinfos[i].flags3 & LEVEL3_SECRET) && (wadlevelinfos[i].flags & LEVEL_VISITED))
return true;
return false;
}
//==========================================================================
//
//
//==========================================================================
static int FindWadClusterInfo (int cluster)
{
for (unsigned int i = 0; i < wadclusterinfos.Size(); i++)
@ -255,6 +269,7 @@ void level_info_t::Reset()
Music = "";
LevelName = "";
AuthorName = "";
MapLabel = "";
FadeTable = "COLORMAP";
CustomColorMap = "COLORMAP";
WallHorizLight = -8;
@ -281,6 +296,8 @@ void level_info_t::Reset()
RedirectCVARMapName = "";
EnterPic = "";
ExitPic = "";
EnterAnim = "";
ExitAnim = "";
Intermission = NAME_None;
deathsequence = NAME_None;
slideshow = NAME_None;
@ -1013,6 +1030,13 @@ DEFINE_MAP_OPTION(author, true)
info->AuthorName = parse.sc.String;
}
DEFINE_MAP_OPTION(label, true)
{
parse.ParseAssign();
parse.sc.MustGetString();
info->MapLabel = parse.sc.String;
}
DEFINE_MAP_OPTION(secretnext, true)
{
parse.ParseAssign();
@ -1257,6 +1281,20 @@ DEFINE_MAP_OPTION(enterpic, true)
info->EnterPic = parse.sc.String;
}
DEFINE_MAP_OPTION(exitanim, true)
{
parse.ParseAssign();
parse.sc.MustGetString();
info->ExitAnim = parse.sc.String;
}
DEFINE_MAP_OPTION(enteranim, true)
{
parse.ParseAssign();
parse.sc.MustGetString();
info->EnterAnim = parse.sc.String;
}
DEFINE_MAP_OPTION(specialaction, true)
{
parse.ParseAssign();
@ -2401,9 +2439,13 @@ static void SetLevelNum (level_info_t *info, int num)
for (unsigned int i = 0; i < wadlevelinfos.Size(); ++i)
{
if (wadlevelinfos[i].levelnum == num)
{
wadlevelinfos[i].levelnum = 0;
wadlevelinfos[i].broken_id24_levelnum = 0;
}
}
info->levelnum = num;
info->broken_id24_levelnum = num; // at least make it work - somehow.
}
//==========================================================================
@ -2631,6 +2673,7 @@ void G_ParseMapInfo (FString basemapinfo)
{
int lump, lastlump = 0;
level_info_t gamedefaults;
TArray<FString> secretMaps;
int flags1 = 0, flags2 = 0;
if (gameinfo.gametype == GAME_Doom)
@ -2747,5 +2790,23 @@ void G_ParseMapInfo (FString basemapinfo)
{
I_FatalError ("You cannot use clearskills in a MAPINFO if you do not define any new skills after it.");
}
// Find any and all secret maps.
for (unsigned int i = 0; i < wadlevelinfos.Size(); i++)
{
if (wadlevelinfos[i].NextSecretMap.IsNotEmpty() && wadlevelinfos[i].NextSecretMap != wadlevelinfos[i].NextMap)
{
secretMaps.Push(wadlevelinfos[i].NextSecretMap);
}
}
// ...and then mark them all as secret maps.
for (unsigned int i = 0; i < secretMaps.Size(); i++)
{
auto* li = FindLevelInfo(secretMaps[i].GetChars(), false);
if (li)
{
li->flags3 |= LEVEL3_SECRET;
}
}
}

View file

@ -271,6 +271,7 @@ enum ELevelFlags : unsigned int
LEVEL3_NOJUMPDOWN = 0x00040000, // only for MBF21. Inverse of MBF's dog_jumping flag.
LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame
LEVEL3_NOFOGOFWAR = 0x00100000, // disables effect of r_radarclipper CVAR on this map
LEVEL3_SECRET = 0x00200000, // level is a secret level
// VKDoom custom flags
VKDLEVELFLAG_NOUSERSAVE = 0x00000001,
@ -328,6 +329,7 @@ struct FExitText
struct level_info_t
{
int levelnum;
int broken_id24_levelnum;
FString MapName;
FString NextMap;
@ -355,6 +357,7 @@ struct level_info_t
FString LightningSound = "world/thunder";
FString Music;
FString LevelName;
FString MapLabel;
FString AuthorName;
int8_t WallVertLight, WallHorizLight;
int musicorder;
@ -396,6 +399,8 @@ struct level_info_t
FString EnterPic;
FString ExitPic;
FString EnterAnim;
FString ExitAnim;
FString InterMusic;
int intermusicorder;
TMap <FName, std::pair<FString, int> > MapInterMusic;
@ -495,6 +500,8 @@ level_info_t *FindLevelInfo (const char *mapname, bool allowdefault=true);
level_info_t *FindLevelByNum (int num);
level_info_t *CheckLevelRedirect (level_info_t *info);
bool SecretLevelVisited();
FString CalcMapName (int episode, int level);
void G_ClearMapinfo();

View file

@ -504,6 +504,8 @@ enum
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
SECMF_LIFT = 2048, // For MBF monster AI
SECMF_HURTMONSTERS = 4096, // Monsters in this sector are hurt like players.
SECMF_HARMINAIR = 8192, // Actors in this sector are also hurt mid-air.
};
enum

View file

@ -832,8 +832,10 @@ void FTextureAnimator::ParseFireTexture(FScanner& sc)
sc.MustGetValue(false);
a = sc.Number;
palette.Push(PalEntry(a, r, g, b));
if (a != 255 && a != 0);
if (a != 255 && a != 0)
{
gametex->SetTranslucent(true);
}
if (palette.Size() > 256)
{

View file

@ -41,6 +41,8 @@ struct UMapEntry
FString LevelName;
FString InterText;
FString InterTextSecret;
FString author;
FString label;
TArray<FSpecialAction> BossActions;
bool BossCleared = false;
@ -52,10 +54,13 @@ struct UMapEntry
char endpic[9] = "";
char exitpic[9] = "";
char enterpic[9] = "";
char exitanim[9] = "";
char enteranim[9] = "";
char interbackdrop[9] = "FLOOR4_8";
char intermusic[9] = "";
int partime = 0;
int nointermission = 0;
int id24_levelnum = 0; // note that this one's semantics are massively screwed up. Only to be used for ID24-style intermissions.
};
static TArray<UMapEntry> Maps;
@ -122,7 +127,7 @@ static int ParseLumpName(FScanner &scanner, char *buffer)
//
// -----------------------------------------------
static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape)
static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape, int *id24_levelnum)
{
// find the next line with content.
// this line is no property.
@ -136,6 +141,16 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape)
scanner.MustGetToken(TK_StringConst);
mape->LevelName = scanner.String;
}
else if (!pname.CompareNoCase("author"))
{
scanner.MustGetToken(TK_StringConst);
mape->author = scanner.String;
}
else if (!pname.CompareNoCase("label"))
{
scanner.MustGetToken(TK_StringConst);
mape->label = scanner.String;
}
else if (!pname.CompareNoCase("next"))
{
ParseLumpName(scanner, mape->nextmap);
@ -186,6 +201,14 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape)
{
ParseLumpName(scanner, mape->enterpic);
}
else if (!pname.CompareNoCase("exitanim"))
{
ParseLumpName(scanner, mape->exitanim);
}
else if (!pname.CompareNoCase("enteranim"))
{
ParseLumpName(scanner, mape->enteranim);
}
else if (!pname.CompareNoCase("nointermission"))
{
scanner.MustGetBoolToken();
@ -244,6 +267,7 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape)
epi.mEpisodeMap = mape->MapName;
epi.mPicName = split[0];
epi.mNoSkill = false;
mape->id24_levelnum = *id24_levelnum = 1;
unsigned i;
for (i = 0; i < AllEpisodes.Size(); i++)
@ -314,15 +338,16 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape)
//
// -----------------------------------------------
static int ParseMapEntry(FScanner &scanner, UMapEntry *val)
static int ParseMapEntry(FScanner &scanner, UMapEntry *val, int *id24_levelnum)
{
scanner.MustGetToken(TK_Identifier);
val->MapName = scanner.String;
val->id24_levelnum = ++(*id24_levelnum);
scanner.MustGetToken('{');
while(!scanner.CheckToken('}'))
{
ParseStandardProperty(scanner, val);
ParseStandardProperty(scanner, val, id24_levelnum);
}
return 1;
}
@ -338,12 +363,13 @@ int ParseUMapInfo(int lumpnum)
FScanner scanner(lumpnum);
unsigned int i;
int id24_levelnum = 1;
while (scanner.GetToken())
{
scanner.TokenMustBe(TK_Map);
UMapEntry parsed;
ParseMapEntry(scanner, &parsed);
ParseMapEntry(scanner, &parsed, &id24_levelnum);
// Endpic overrides level exits.
if (parsed.endpic[0])
@ -403,6 +429,14 @@ void CommitUMapinfo(level_info_t *defaultinfo)
levelinfo->LevelName = map.LevelName;
levelinfo->PName = ""; // clear the map name patch to force the string version to be shown - unless explicitly overridden right next.
}
if (map.author.IsNotEmpty())
{
levelinfo->AuthorName = map.author;
}
if (map.label.IsNotEmpty())
{
levelinfo->MapLabel = map.label;
}
if (map.levelpic[0]) levelinfo->PName = map.levelpic;
if (map.nextmap[0]) levelinfo->NextMap = map.nextmap;
else if (map.endpic[0])
@ -443,6 +477,9 @@ void CommitUMapinfo(level_info_t *defaultinfo)
if (map.partime > 0) levelinfo->partime = map.partime;
if (map.enterpic[0]) levelinfo->EnterPic = map.enterpic;
if (map.exitpic[0]) levelinfo->ExitPic = map.exitpic;
if (map.enteranim[0]) levelinfo->EnterAnim = map.enteranim;
if (map.exitanim[0]) levelinfo->ExitAnim = map.exitanim;
levelinfo->broken_id24_levelnum = map.id24_levelnum;
/* UMAPINFO's intermusic is for the text screen, not the summary.
if (map.intermusic[0])
{

View file

@ -1034,6 +1034,10 @@ public:
// This switch contains all keys of the UDMF base spec which only apply to Hexen format specials
if (!isTranslated) switch (key.GetIndex())
{
case NAME_Walking:
Flag(ld->activation, SPAC_Walking, key);
continue;
case NAME_Playercross:
Flag(ld->activation, SPAC_Cross, key);
continue;
@ -2030,6 +2034,14 @@ public:
Flag(sec->Flags, SECF_HAZARD, key);
break;
case NAME_hurtmonsters:
Flag(sec->MoreFlags, SECMF_HURTMONSTERS, key);
break;
case NAME_harminair:
Flag(sec->MoreFlags, SECMF_HARMINAIR, key);
break;
case NAME_floorterrain:
sec->terrainnum[sector_t::floor] = P_FindTerrain(CheckString(key));
break;

View file

@ -625,6 +625,7 @@ xx(ScaleY)
xx(FriendlySeeBlocks)
xx(Floatbobphase)
xx(Floatbobstrength)
xx(FloatBobFactor)
xx(Target)
xx(Master)
xx(Tracer)
@ -670,6 +671,7 @@ xx(Missilecross)
xx(Anycross)
xx(Monsteruse)
xx(Monsterpush)
xx(Walking)
xx(ZDoom)
xx(ZDoomTranslated)
@ -808,6 +810,8 @@ xx(damageinterval)
xx(leakiness)
xx(damageterraineffect)
xx(damagehazard)
xx(hurtmonsters)
xx(harminair)
xx(floorterrain)
xx(ceilingterrain)
xx(floor_reflect)

View file

@ -262,8 +262,8 @@ enum ActorFlag4
MF4_STRIFEDAMAGE = 0x00000100, // Strife projectiles only do up to 4x damage, not 8x
MF4_CANUSEWALLS = 0x00000200, // Can activate 'use' specials
MF4_MISSILEMORE = 0x00000400, // increases the chance of a missile attack
MF4_MISSILEEVENMORE = 0x00000800, // significantly increases the chance of a missile attack
// = 0x00000400,
// = 0x00000800,
MF4_FORCERADIUSDMG = 0x00001000, // if put on an object it will override MF3_NORADIUSDMG
MF4_DONTFALL = 0x00002000, // Doesn't have NOGRAVITY disabled when dying.
MF4_SEESDAGGERS = 0x00004000, // This actor can see you striking with a dagger
@ -442,7 +442,9 @@ enum ActorFlag9
MF9_DOSHADOWBLOCK = 0x00000002, // [inkoalawetrust] Should the monster look for SHADOWBLOCK actors ?
MF9_SHADOWBLOCK = 0x00000004, // [inkoalawetrust] Actors in the line of fire with this flag trigger the MF_SHADOW aiming penalty.
MF9_SHADOWAIMVERT = 0x00000008, // [inkoalawetrust] Monster aim is also offset vertically when aiming at shadow actors.
MF9_DECOUPLEDANIMATIONS = 0x00000010, // [RL0] Decouple model animations from states
MF9_DECOUPLEDANIMATIONS = 0x00000010, // [RL0] Decouple model animations from states
MF9_NOSECTORDAMAGE = 0x00000020, // [inkoalawetrust] Actor ignores any sector-based damage (i.e damaging floors, NOT crushers)
MF9_ISPUFF = 0x00000040, // [AA] Set on actors by P_SpawnPuff
};
// --- mobj.renderflags ---
@ -1231,6 +1233,7 @@ public:
TObjPtr<AActor*> alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals.
TObjPtr<AActor*> tracer; // Thing being chased/attacked for tracers
TObjPtr<AActor*> master; // Thing which spawned this one (prevents mutual attacks)
TObjPtr<AActor*> damagesource; // [AA] Thing that fired a hitscan using this actor as a puff
int tid; // thing identifier
int special; // special
@ -1252,6 +1255,7 @@ public:
// but instead tries to come closer for a melee attack.
// This is not the same as meleerange
double maxtargetrange; // any target farther away cannot be attacked
double missilechancemult; // distance multiplier for CheckMeleeRange, formerly done with MISSILE(EVEN)MORE flags.
double bouncefactor; // Strife's grenades use 50%, Hexen's Flechettes 70.
double wallbouncefactor; // The bounce factor for walls can be different.
double Gravity; // [GRB] Gravity factor
@ -1304,6 +1308,7 @@ public:
uint8_t FloatBobPhase;
uint8_t FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc)
double FloatBobStrength;
double FloatBobFactor;
PalEntry BloodColor;
FTranslationID BloodTranslation;
@ -1524,9 +1529,13 @@ public:
{
return Z() + Height;
}
double CenterOffset() const
{
return Height / 2;
}
double Center() const
{
return Z() + Height/2;
return Z() + CenterOffset();
}
void SetZ(double newz, bool moving = true)
{
@ -1603,6 +1612,11 @@ public:
Vel.Y += speed * angle.Sin();
}
void Thrust(const DVector3& vel)
{
Vel += vel;
}
void Vel3DFromAngle(DAngle angle, DAngle pitch, double speed)
{
double cospitch = pitch.Cos();

View file

@ -69,7 +69,7 @@ inline double AActor::GetBobOffset(double ticfrac) const
{
return 0;
}
return BobSin(FloatBobPhase + Level->maptime + ticfrac) * FloatBobStrength;
return BobSin(FloatBobPhase + Level->maptime * FloatBobFactor + ticfrac) * FloatBobStrength;
}
inline double AActor::GetCameraHeight() const

View file

@ -198,15 +198,15 @@ void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flags, int
//==========================================================================
//
// P_PlayerOnSpecial3DFloor
// Checks to see if a player is standing on or is inside a 3D floor (water)
// P_ActorOnSpecial3DFloor
// Checks to see if an actor is standing on or is inside a 3D floor (water)
// and applies any specials..
//
//==========================================================================
void P_PlayerOnSpecial3DFloor(player_t* player)
void P_ActorOnSpecial3DFloor(AActor* victim)
{
for(auto rover : player->mo->Sector->e->XFloor.ffloors)
for(auto rover : victim->Sector->e->XFloor.ffloors)
{
if (!(rover->flags & FF_EXISTS)) continue;
if (rover->flags & FF_FIX) continue;
@ -215,22 +215,22 @@ void P_PlayerOnSpecial3DFloor(player_t* player)
if(rover->flags & FF_SOLID)
{
// Player must be on top of the floor to be affected...
if(player->mo->Z() != rover->top.plane->ZatPoint(player->mo)) continue;
if(victim->Z() != rover->top.plane->ZatPoint(victim)) continue;
}
else
{
//Water and DEATH FOG!!! heh
if ((rover->flags & FF_NODAMAGE) ||
player->mo->Z() > rover->top.plane->ZatPoint(player->mo) ||
player->mo->Top() < rover->bottom.plane->ZatPoint(player->mo))
victim->Z() > rover->top.plane->ZatPoint(victim) ||
victim->Top() < rover->bottom.plane->ZatPoint(victim))
continue;
}
// Apply sector specials
P_PlayerInSpecialSector(player, rover->model);
P_ActorInSpecialSector(victim, rover->model);
// Apply flat specials (using the ceiling!)
P_PlayerOnSpecialFlat(player, rover->model->GetTerrain(rover->top.isceiling));
P_ActorOnSpecialFlat(victim, rover->model->GetTerrain(rover->top.isceiling));
break;
}

View file

@ -113,8 +113,7 @@ struct lightlist_t
class player_t;
void P_PlayerOnSpecial3DFloor(player_t* player);
void P_ActorOnSpecial3DFloor(AActor* victim);
bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger);
bool P_CheckFor3DCeilingHit(AActor * mo, double z, bool trigger);

View file

@ -373,8 +373,7 @@ static int P_CheckMissileRange (AActor *actor)
if (actor->MeleeState != nullptr && dist < actor->meleethreshold)
return false; // From the Revenant: close enough for fist attack
if (actor->flags4 & MF4_MISSILEMORE) dist *= 0.5;
if (actor->flags4 & MF4_MISSILEEVENMORE) dist *= 0.125;
dist *= actor->missilechancemult;
int mmc = int(actor->MinMissileChance * G_SkillProperty(SKILLP_Aggressiveness));
return pr_checkmissilerange() >= min(int(dist), mmc);
@ -465,6 +464,12 @@ static int P_IsUnderDamage(AActor* actor)
dir |= cl->getDirection();
}
// Q: consider crushing 3D floors too?
// [inkoalawetrust] Check for sectors that can harm the actor.
if (!(actor->flags9 & MF9_NOSECTORDAMAGE) && seclist->m_sector->damageamount > 0)
{
if (seclist->m_sector->MoreFlags & SECMF_HARMINAIR || actor->isAtZ(seclist->m_sector->LowestFloorAt(actor)) || actor->waterlevel)
return (actor->player || (actor->player == nullptr && seclist->m_sector->MoreFlags & SECMF_HURTMONSTERS)) ? -1 : 0;
}
}
return dir;
}
@ -1835,14 +1840,6 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params)
if (!P_IsVisible (actor, player->mo, allaround, params))
continue; // out of sight
// [SP] Deathmatch fixes - if we have MF_FRIENDLY we're definitely in deathmatch
// We're going to ignore our master, but go after his enemies.
if ( actor->flags & MF_FRIENDLY )
{
if ( actor->IsFriend(player->mo) )
continue;
}
// [RC] Well, let's let special monsters with this flag active be able to see
// the player then, eh?
if(!(actor->flags6 & MF6_SEEINVISIBLE))

View file

@ -408,7 +408,8 @@ enum
RADF_OLDRADIUSDAMAGE = 32,
RADF_THRUSTLESS = 64,
RADF_NOALLIES = 128,
RADF_CIRCULAR = 256
RADF_CIRCULAR = 256,
RADF_CIRCULARTHRUST = 512,
};
int P_GetRadiusDamage(AActor *self, AActor *thing, int damage, double distance, double fulldmgdistance, bool oldradiusdmg, bool circular);
int P_RadiusAttack (AActor *spot, AActor *source, int damage, double distance,

View file

@ -6236,24 +6236,44 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, double
if (!(thing->flags7 & MF7_DONTTHRUST))
{
thrust = points * 0.5 / (double)thing->Mass;
if (bombsource == thing)
{
thrust *= selfthrustscale;
}
vz = (thing->Center() - bombspot->Z()) * thrust;
if (bombsource != thing)
if (flags & RADF_CIRCULARTHRUST)
{
vz *= 0.5;
auto dir = bombspot->Vec3To(thing);
dir.Z += thing->CenterOffset() - bombspot->CenterOffset();
if (!dir.isZero())
{
dir.MakeUnit();
thing->Thrust(dir * thrust);
}
}
else
{
vz *= 0.8;
}
thing->Thrust(bombspot->AngleTo(thing), thrust);
if (!(flags & RADF_NODAMAGE) || (flags & RADF_THRUSTZ))
{
if (!(thing->Level->i_compatflags2 & COMPATF2_EXPLODE1) || (flags & RADF_THRUSTZ))
thing->Vel.Z += vz; // this really doesn't work well
thing->Thrust(bombspot->AngleTo(thing), thrust);
if (!(flags & RADF_NODAMAGE) || (flags & RADF_THRUSTZ))
{
if (!(thing->Level->i_compatflags2 & COMPATF2_EXPLODE1) || (flags & RADF_THRUSTZ))
{
vz = (thing->Center() - bombspot->Z()) * thrust;
if (bombsource != thing)
{
vz *= 0.5;
}
else
{
vz *= 0.8;
}
thing->Vel.Z += vz; // this really doesn't work well
}
}
}
}
}

View file

@ -168,6 +168,7 @@ IMPLEMENT_POINTERS_START(AActor)
IMPLEMENT_POINTER(target)
IMPLEMENT_POINTER(lastenemy)
IMPLEMENT_POINTER(tracer)
IMPLEMENT_POINTER(damagesource)
IMPLEMENT_POINTER(goal)
IMPLEMENT_POINTER(LastLookActor)
IMPLEMENT_POINTER(Inventory)
@ -217,6 +218,7 @@ void AActor::Serialize(FSerializer &arc)
A("angles", Angles)
A("frame", frame)
A("scale", Scale)
A("nolocalrender", NoLocalRender) // Note: This will probably be removed later since a better solution is needed
A("renderstyle", RenderStyle)
A("renderflags", renderflags)
A("renderflags2", renderflags2)
@ -289,6 +291,7 @@ void AActor::Serialize(FSerializer &arc)
A("inventoryid", InventoryID)
A("floatbobphase", FloatBobPhase)
A("floatbobstrength", FloatBobStrength)
A("floatbobfactor", FloatBobFactor)
A("translation", Translation)
A("bloodcolor", BloodColor)
A("bloodtranslation", BloodTranslation)
@ -317,6 +320,7 @@ void AActor::Serialize(FSerializer &arc)
A("wallbouncefactor", wallbouncefactor)
A("bouncecount", bouncecount)
A("maxtargetrange", maxtargetrange)
A("missilechancemult", missilechancemult)
A("meleethreshold", meleethreshold)
A("meleerange", meleerange)
A("damagetype", DamageType)
@ -398,7 +402,8 @@ void AActor::Serialize(FSerializer &arc)
("unmorphtime", UnmorphTime)
("morphflags", MorphFlags)
("premorphproperties", PremorphProperties)
("morphexitflash", MorphExitFlash);
("morphexitflash", MorphExitFlash)
("damagesource", damagesource);
SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain);
@ -798,7 +803,7 @@ DEFINE_ACTION_FUNCTION(AActor, GiveInventoryType)
void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth)
{
Level->total_monsters -= CountsAsKill();
if (health > 0) Level->total_monsters -= CountsAsKill();
TIDtoHate = other->TIDtoHate;
LastLookActor = other->LastLookActor;
LastLookPlayerNumber = other->LastLookPlayerNumber;
@ -813,7 +818,7 @@ void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealt
LastHeard = target = other->target;
}
if (resetHealth) health = SpawnHealth();
Level->total_monsters += CountsAsKill();
if (health > 0) Level->total_monsters += CountsAsKill();
}
DEFINE_ACTION_FUNCTION(AActor, CopyFriendliness)
@ -4430,6 +4435,14 @@ void AActor::Tick ()
// must have been removed
if (ObjectFlags & OF_EuthanizeMe) return;
}
//[inkoalawetrust] Genericized level damage handling that makes sector, 3D floor, and TERRAIN flat damage affect monsters and other NPCs too.
if (!(flags9 & MF9_NOSECTORDAMAGE) && (player || (player == nullptr && Sector->MoreFlags & SECMF_HURTMONSTERS)))
{
P_ActorOnSpecial3DFloor(this);
P_ActorInSpecialSector(this,Sector);
if (!isAbove(Sector->floorplane.ZatPoint(this)) || waterlevel) // Actor must be touching the floor for TERRAIN flats.
P_ActorOnSpecialFlat(this, P_GetThingFloorType(this));
}
if (tics != -1)
{
@ -6278,9 +6291,15 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, const DVector3 &pos1
if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER))
puff->target = source;
// [AA] Track the source of the attack unconditionally in a separate field.
puff->damagesource = source;
// Angle is the opposite of the hit direction (i.e. the puff faces the source.)
puff->Angles.Yaw = hitdir + DAngle::fromDeg(180);
// [AA] Mark the spawned actor as a puff with a flag.
puff->flags9 |= MF9_ISPUFF;
// If a puff has a crash state and an actor was not hit,
// it will enter the crash state. This is used by the StrifeSpark
// and BlasterPuff.

View file

@ -100,7 +100,7 @@
#include "c_console.h"
#include "p_spec_thinkers.h"
static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
static FRandom pr_actorinspecialsector ("ActorInSpecialSector");
EXTERN_CVAR(Bool, cl_predict_specials)
EXTERN_CVAR(Bool, forcewater)
@ -254,6 +254,13 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType,
return false;
}
if ((activationType & (SPAC_Cross|SPAC_MCross)) && (lineActivation & SPAC_Walking))
{
// not on floor
if ((mo->Pos().Z > mo->floorz) && !(mo->flags2 & MF2_ONMOBJ))
return false;
}
if (lineActivation & SPAC_UseThrough)
{
lineActivation |= SPAC_Use;
@ -412,22 +419,18 @@ bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType)
}
//
// P_PlayerInSpecialSector
// P_ActorInSpecialSector
// Called every tic frame
// that the player origin is in a special sector
// that the actor origin is in a special sector
//
void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
void P_ActorInSpecialSector (AActor *victim, sector_t * sector)
{
if (sector == NULL)
{
// Falling, not all the way down yet?
sector = player->mo->Sector;
if (!player->mo->isAtZ(sector->LowestFloorAt(player->mo))
&& !player->mo->waterlevel)
{
return;
}
}
sector = victim->Sector;
// Falling, not all the way down yet?
if (!(sector->MoreFlags & SECMF_HARMINAIR) && !victim->isAtZ(sector->LowestFloorAt(victim)) && !victim->waterlevel)
return;
// Has hit ground.
@ -438,7 +441,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
if (sector->damageinterval <= 0)
sector->damageinterval = 32; // repair invalid damageinterval values
if (sector->Flags & (SECF_EXIT1 | SECF_EXIT2))
if (victim->player && sector->Flags & (SECF_EXIT1 | SECF_EXIT2))
{
for (int i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
@ -457,7 +460,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
// different damage types yet, so that's not happening for now.
// [MK] account for subclasses that may have "Full" protection (i.e.: prevent leaky damage)
int ironfeet = 0;
for (auto i = player->mo->Inventory; i != NULL; i = i->Inventory)
for (auto i = victim->Inventory; i != NULL; i = i->Inventory)
{
if (i->IsKindOf(NAME_PowerIronFeet))
{
@ -469,28 +472,28 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
}
}
if (sector->Flags & SECF_ENDGODMODE) player->cheats &= ~CF_GODMODE;
if ((ironfeet == 0 || (ironfeet < 2 && pr_playerinspecialsector() < sector->leakydamage)))
if (victim->player && sector->Flags & SECF_ENDGODMODE) victim->player->cheats &= ~CF_GODMODE;
if ((ironfeet == 0 || (ironfeet < 2 && pr_actorinspecialsector() < sector->leakydamage)))
{
if (sector->Flags & SECF_HAZARD)
if (victim->player && sector->Flags & SECF_HAZARD)
{
player->hazardcount += sector->damageamount;
player->hazardtype = sector->damagetype;
player->hazardinterval = sector->damageinterval;
victim->player->hazardcount += sector->damageamount;
victim->player->hazardtype = sector->damagetype;
victim->player->hazardinterval = sector->damageinterval;
}
else if (Level->time % sector->damageinterval == 0)
{
if (!(player->cheats & (CF_GODMODE | CF_GODMODE2)))
if (!(victim->player && victim->player->cheats & (CF_GODMODE | CF_GODMODE2)))
{
P_DamageMobj(player->mo, NULL, NULL, sector->damageamount, sector->damagetype);
P_DamageMobj(victim, NULL, NULL, sector->damageamount, sector->damagetype);
}
if ((sector->Flags & SECF_ENDLEVEL) && player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT)))
if (victim->player && (sector->Flags & SECF_ENDLEVEL) && victim->player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT)))
{
Level->ExitLevel(0, false);
}
if (sector->Flags & SECF_DMGTERRAINFX)
{
P_HitWater(player->mo, player->mo->Sector, player->mo->Pos(), false, true, true);
P_HitWater(victim, victim->Sector, victim->Pos(), false, true, true);
}
}
}
@ -499,14 +502,14 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector)
{
if (Level->time % sector->damageinterval == 0)
{
P_GiveBody(player->mo, -sector->damageamount, 100);
P_GiveBody(victim, -sector->damageamount, 100);
}
}
if (sector->isSecret())
if (victim->player && sector->isSecret())
{
sector->ClearSecret();
P_GiveSecret(Level, player->mo, true, true, sector->Index());
P_GiveSecret(Level, victim, true, true, sector->Index());
}
}
@ -645,13 +648,13 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, GiveSecret)
//============================================================================
//
// P_PlayerOnSpecialFlat
// P_ActorOnSpecialFlat
//
//============================================================================
void P_PlayerOnSpecialFlat (player_t *player, int floorType)
void P_ActorOnSpecialFlat (AActor *victim, int floorType)
{
auto Level = player->mo->Level;
auto Level = victim->Level;
if (Terrains[floorType].DamageAmount &&
!(Level->time % (Terrains[floorType].DamageTimeMask+1)))
@ -661,7 +664,7 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType)
if (Terrains[floorType].AllowProtection)
{
auto pitype = PClass::FindActor(NAME_PowerIronFeet);
for (ironfeet = player->mo->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory)
for (ironfeet = victim->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory)
{
if (ironfeet->IsKindOf (pitype))
break;
@ -671,20 +674,18 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType)
int damage = 0;
if (ironfeet == NULL)
{
damage = P_DamageMobj (player->mo, NULL, NULL, Terrains[floorType].DamageAmount,
damage = P_DamageMobj (victim, NULL, NULL, Terrains[floorType].DamageAmount,
Terrains[floorType].DamageMOD);
}
if (damage > 0 && Terrains[floorType].Splash != -1)
{
S_Sound (player->mo, CHAN_AUTO, 0,
S_Sound (victim, CHAN_AUTO, 0,
Splashes[Terrains[floorType].Splash].NormalSplashSound, 1,
ATTN_IDLE);
}
}
}
//
// P_UpdateSpecials
// Animate planes, scroll walls, etc.

View file

@ -90,8 +90,8 @@ bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType, DVect
bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType, DVector3 *optpos = NULL);
bool P_PredictLine (line_t *ld, AActor *mo, int side, int activationType);
void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL);
void P_PlayerOnSpecialFlat (player_t *player, int floorType);
void P_ActorInSpecialSector (AActor *victim, sector_t * sector=NULL);
void P_ActorOnSpecialFlat (AActor *victim, int floorType);
void P_SectorDamage(FLevelLocals *Level, int tag, int amount, FName type, PClassActor *protectClass, int flags);
void P_SetSectorFriction (FLevelLocals *level, int tag, int amount, bool alterFlag);
double FrictionToMoveFactor(double friction);

View file

@ -520,6 +520,23 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, SetFOV)
return 0;
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, SetSkin)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_INT(skinIndex);
if (skinIndex >= 0 && skinIndex < Skins.size())
{
// commented code - cvar_set calls this automatically, along with saving the skin selection.
//self->userinfo.SkinNumChanged(skinIndex);
cvar_set("skin", Skins[skinIndex].Name.GetChars());
ACTION_RETURN_INT(self->userinfo.GetSkin());
}
else
{
ACTION_RETURN_INT(-1);
}
}
//===========================================================================
//
// EnumColorsets
@ -773,6 +790,12 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, GetSkin)
ACTION_RETURN_INT(self->userinfo.GetSkin());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetSkinCount)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_INT(Skins.size());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetGender)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
@ -1181,15 +1204,6 @@ DEFINE_ACTION_FUNCTION(APlayerPawn, CheckMusicChange)
void P_CheckEnvironment(player_t *player)
{
P_PlayerOnSpecial3DFloor(player);
P_PlayerInSpecialSector(player);
if (!player->mo->isAbove(player->mo->Sector->floorplane.ZatPoint(player->mo)) ||
player->mo->waterlevel)
{
// Player must be touching the floor
P_PlayerOnSpecialFlat(player, P_GetThingFloorType(player->mo));
}
if (player->mo->Vel.Z <= -player->mo->FloatVar(NAME_FallingScreamMinSpeed) &&
player->mo->Vel.Z >= -player->mo->FloatVar(NAME_FallingScreamMaxSpeed) && player->mo->alternative == nullptr &&
player->mo->waterlevel == 0)

View file

@ -88,6 +88,7 @@ inline AActor* CheckForShadows(AActor* self, AActor* other, DVector3 pos, double
if ((other && (other->flags & MF_SHADOW)) || (self->flags9 & MF9_DOSHADOWBLOCK))
{
AActor* shadowBlock = P_CheckForShadowBlock(self, other, pos, penaltyFactor);
if (other && !(other->flags & MF_SHADOW)) other = nullptr; //Other doesn't have MF_SHADOW, so don't count them as a valid return.
return shadowBlock ? shadowBlock : other;
}
return nullptr;

View file

@ -322,6 +322,7 @@ angle_t HWDrawInfo::FrustumAngle()
{
// If pitch is larger than this you can look all around at an FOV of 90 degrees
if (fabs(Viewpoint.HWAngles.Pitch.Degrees()) > 89.0) return 0xffffffff;
else if (fabs(Viewpoint.HWAngles.Pitch.Degrees()) > 46.0 && !Viewpoint.IsAllowedOoB()) return 0xffffffff; // Just like 4.12.2 and older did
int aspMult = AspectMultiplier(r_viewwindow.WidescreenRatio); // 48 == square window
double absPitch = fabs(Viewpoint.HWAngles.Pitch.Degrees());
// Smaller aspect ratios still clip too much. Need a better solution

View file

@ -81,6 +81,8 @@ EXTERN_CVAR(Bool, gl_aalines)
CVAR(Bool, gl_usecolorblending, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, gl_sprite_blend, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Int, gl_spriteclip, 2, CVAR_ARCHIVE)
CVAR(Bool, r_debug_nolimitanamorphoses, false, 0)
CVAR(Float, r_spriteclipanamorphicminbias, 0.6, CVAR_ARCHIVE)
CVAR(Float, gl_sclipthreshold, 10.0, CVAR_ARCHIVE)
CVAR(Float, gl_sclipfactor, 1.8f, CVAR_ARCHIVE)
CVAR(Int, gl_particles_style, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // 0 = square, 1 = round, 2 = smooth
@ -1099,7 +1101,7 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
r.Scale(sprscale.X, isSpriteShadow ? sprscale.Y * 0.15 * thing->isoscaleY : sprscale.Y * thing->isoscaleY);
if ((thing->renderflags & (RF_ROLLSPRITE|RF_FLATSPRITE)) || (thing->renderflags2 & RF2_SQUAREPIXELS))
if ((thing->renderflags & RF_ROLLSPRITE) || (thing->renderflags2 & RF2_SQUAREPIXELS))
{
double ps = di->Level->pixelstretch;
double mult = 1.0 / sqrt(ps); // shrink slightly
@ -1180,6 +1182,56 @@ void HWSprite::Process(HWDrawInfo *di, FRenderState& state, AActor* thing, secto
if(thing->renderflags2 & RF2_ISOMETRICSPRITES) depth = depth * vp.PitchCos - vp.PitchSin * z2; // Helps with stacking actors with small xy offsets
if (isSpriteShadow) depth += 1.f/65536.f; // always sort shadows behind the sprite.
if (gl_spriteclip == -1 && (thing->renderflags & RF_SPRITETYPEMASK) == RF_FACESPRITE) // perform anamorphosis
{
float minbias = r_spriteclipanamorphicminbias;
minbias = clamp(minbias, 0.3f, 1.0f);
float btm = thing->Sector->floorplane.ZatPoint(thing) - thing->Floorclip;
float top = thing->Sector->ceilingplane.ZatPoint(thingpos);
float vbtm = thing->Sector->floorplane.ZatPoint(vp.Pos);
float vtop = thing->Sector->ceilingplane.ZatPoint(vp.Pos);
float vpx = vp.Pos.X;
float vpy = vp.Pos.Y;
float vpz = vp.Pos.Z;
float tpx = thingpos.X;
float tpy = thingpos.Y;
float tpz = thingpos.Z;
if (!(r_debug_nolimitanamorphoses))
{
// this should help prevent clipping through walls ...
float objradiusbias = 1.f - thing->radius / sqrt((vpx - tpx) * (vpx - tpx) + (vpy - tpy) * (vpy - tpy));
minbias = max(minbias, objradiusbias);
}
float bintersect, tintersect;
if (z2 < vpz && vbtm < vpz)
bintersect = min((btm - vpz) / (z2 - vpz), (vbtm - vpz) / (z2 - vpz));
else
bintersect = 1.0;
if (z1 > vpz && vtop > vpz)
tintersect = min((top - vpz) / (z1 - vpz), (vtop - vpz) / (z1 - vpz));
else
tintersect = 1.0;
if (thing->waterlevel >= 1 && thing->waterlevel <= 2)
bintersect = tintersect = 1.0f;
float spbias = clamp(min(bintersect, tintersect), minbias, 1.0f);
float vpbias = 1.0 - spbias;
x1 = x1 * spbias + vpx * vpbias;
y1 = y1 * spbias + vpy * vpbias;
z1 = z1 * spbias + vpz * vpbias;
x2 = x2 * spbias + vpx * vpbias;
y2 = y2 * spbias + vpy * vpbias;
z2 = z2 * spbias + vpz * vpbias;
}
// light calculation
bool enhancedvision = false;

View file

@ -475,7 +475,7 @@ void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *
AActor *defaults = (AActor*)bag.Info->Defaults;
if (fd->structoffset == -1) // this is a deprecated flag that has been changed into a real property
{
HandleDeprecatedFlags(defaults, bag.Info, mod=='+', fd->flagbit);
HandleDeprecatedFlags(defaults, mod=='+', fd->flagbit);
}
else
{

View file

@ -66,12 +66,13 @@ struct FFlagDef
int structoffset;
int fieldsize;
int varflags;
VersionInfo deprecationVersion;
};
void FinalizeClass(PClass *cls, FStateDefinitions &statedef);
FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict = false);
void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int index);
bool CheckDeprecatedFlags(AActor *actor, PClassActor *info, int index);
void HandleDeprecatedFlags(AActor *defaults, int set, int index);
int CheckDeprecatedFlags(AActor *actor, int index);
const char *GetFlagName(unsigned int flagnum, int flagoffset);
void ModActorFlag(AActor *actor, FFlagDef *fd, bool set);
bool ModActorFlag(AActor *actor, const FString &flagname, bool set, bool printerror = true);
@ -229,6 +230,9 @@ enum
DEPF_DOOMBOUNCE = 11,
DEPF_INTERHUBSTRIP = 12,
DEPF_HIGHERMPROB = 13,
// all that follow need ZScript emulation!
DEPF_MISSILEMORE = 14,
DEPF_MISSILEEVENMORE = 15
};
// Types of old style decorations

View file

@ -75,8 +75,8 @@ extern float BackbuttonAlpha;
#define DEFINE_FLAG(prefix, name, type, variable) { (unsigned int)prefix##_##name, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native }
#define DEFINE_PROTECTED_FLAG(prefix, name, type, variable) { (unsigned int)prefix##_##name, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native|VARF_ReadOnly|VARF_InternalAccess }
#define DEFINE_FLAG2(symbol, name, type, variable) { (unsigned int)symbol, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native }
#define DEFINE_FLAG2_DEPRECATED(symbol, name, type, variable) { (unsigned int)symbol, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native|VARF_Deprecated }
#define DEFINE_DEPRECATED_FLAG(name) { DEPF_##name, #name, -1, 0, true }
#define DEFINE_FLAG2_DEPRECATED(symbol, name, type, variable, version) { (unsigned int)symbol, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native|VARF_Deprecated }
#define DEFINE_DEPRECATED_FLAG(name, version) { DEPF_##name, #name, -1, 0, VARF_Deprecated, version }
#define DEFINE_DUMMY_FLAG(name, deprec) { DEPF_UNUSED, #name, -1, 0, deprec? VARF_Deprecated:0 }
// internal flags. These do not get exposed to actor definitions but scripts need to be able to access them as variables.
@ -210,8 +210,6 @@ static FFlagDef ActorFlagDefs[]=
DEFINE_FLAG(MF4, ACTLIKEBRIDGE, AActor, flags4),
DEFINE_FLAG(MF4, STRIFEDAMAGE, AActor, flags4),
DEFINE_FLAG(MF4, CANUSEWALLS, AActor, flags4),
DEFINE_FLAG(MF4, MISSILEMORE, AActor, flags4),
DEFINE_FLAG(MF4, MISSILEEVENMORE, AActor, flags4),
DEFINE_FLAG(MF4, FORCERADIUSDMG, AActor, flags4),
DEFINE_FLAG(MF4, DONTFALL, AActor, flags4),
DEFINE_FLAG(MF4, SEESDAGGERS, AActor, flags4),
@ -353,6 +351,8 @@ static FFlagDef ActorFlagDefs[]=
DEFINE_FLAG(MF9, SHADOWBLOCK, AActor, flags9),
DEFINE_FLAG(MF9, SHADOWAIMVERT, AActor, flags9),
DEFINE_FLAG(MF9, DECOUPLEDANIMATIONS, AActor, flags9),
DEFINE_FLAG(MF9, NOSECTORDAMAGE, AActor, flags9),
DEFINE_PROTECTED_FLAG(MF9, ISPUFF, AActor, flags9), //[AA] was spawned by SpawnPuff
// Effect flags
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
@ -408,6 +408,11 @@ static FFlagDef ActorFlagDefs[]=
DEFINE_FLAG2(BOUNCE_NotOnSky, DONTBOUNCEONSKY, AActor, BounceFlags),
DEFINE_FLAG2(OF_Transient, NOSAVEGAME, AActor, ObjectFlags),
// Deprecated flags which need a ZScript workaround.
DEFINE_DEPRECATED_FLAG(MISSILEMORE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(MISSILEEVENMORE, MakeVersion(4, 13, 0)),
};
// These won't be accessible through bitfield variables
@ -415,24 +420,25 @@ static FFlagDef MoreFlagDefs[] =
{
// Deprecated flags. Handling must be performed in HandleDeprecatedFlags
DEFINE_DEPRECATED_FLAG(FIREDAMAGE),
DEFINE_DEPRECATED_FLAG(ICEDAMAGE),
DEFINE_DEPRECATED_FLAG(LOWGRAVITY),
DEFINE_DEPRECATED_FLAG(SHORTMISSILERANGE),
DEFINE_DEPRECATED_FLAG(LONGMELEERANGE),
DEFINE_DEPRECATED_FLAG(QUARTERGRAVITY),
DEFINE_DEPRECATED_FLAG(FIRERESIST),
DEFINE_DEPRECATED_FLAG(HERETICBOUNCE),
DEFINE_DEPRECATED_FLAG(HEXENBOUNCE),
DEFINE_DEPRECATED_FLAG(DOOMBOUNCE),
DEFINE_DEPRECATED_FLAG(HIGHERMPROB),
// Note: Although deprecated since DECORATE times, they were never actually flagged as deprecated before 4.13.0 and no message was output...
DEFINE_DEPRECATED_FLAG(FIREDAMAGE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(ICEDAMAGE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(LOWGRAVITY, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(SHORTMISSILERANGE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(LONGMELEERANGE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(QUARTERGRAVITY, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(FIRERESIST, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(HERETICBOUNCE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(HEXENBOUNCE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(DOOMBOUNCE, MakeVersion(4, 13, 0)),
DEFINE_DEPRECATED_FLAG(HIGHERMPROB, MakeVersion(4, 13, 0)),
// Deprecated flags with no more existing functionality.
DEFINE_DUMMY_FLAG(FASTER, true), // obsolete, replaced by 'Fast' state flag
DEFINE_DUMMY_FLAG(FASTMELEE, true), // obsolete, replaced by 'Fast' state flag
// Deprecated name as an alias
DEFINE_FLAG2_DEPRECATED(MF4_DONTHARMCLASS, DONTHURTSPECIES, AActor, flags4),
DEFINE_FLAG2_DEPRECATED(MF4_DONTHARMCLASS, DONTHURTSPECIES, AActor, flags4, 0),
// Various Skulltag flags that are quite irrelevant to ZDoom
// [BC] New DECORATE flag defines here.
@ -914,6 +920,18 @@ void SynthesizeFlagFields()
{
cls->VMType->AddNativeField(FStringf("b%s", fl.Defs[i].name), (fl.Defs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), fl.Defs[i].structoffset, fl.Defs[i].varflags, fl.Defs[i].flagbit);
}
else if (fl.Defs[i].flagbit == DEPF_MISSILEMORE || fl.Defs[i].flagbit == DEPF_MISSILEEVENMORE) // these need script side emulation because they have been around for many years.
{
auto field = cls->VMType->AddNativeField(FStringf("b%s", fl.Defs[i].name), TypeSInt32, 0, VARF_Native);
if (field)
{
// these are deprecated so flag accordingly with a proper message.
field->DeprecationMessage = "Use missilechancemult property instead";
field->mVersion = MakeVersion(4, 13, 0);
field->Flags |= VARF_Deprecated;
field->BitValue = fl.Defs[i].flagbit + 64;
}
}
}
}
}

View file

@ -176,7 +176,7 @@ bool ModActorFlag(AActor *actor, const FString &flagname, bool set, bool printer
if (fd->structoffset == -1)
{
HandleDeprecatedFlags(actor, cls, set, fd->flagbit);
HandleDeprecatedFlags(actor, set, fd->flagbit);
}
else
{
@ -214,7 +214,7 @@ INTBOOL CheckActorFlag(AActor *owner, FFlagDef *fd)
{
if (fd->structoffset == -1)
{
return CheckDeprecatedFlags(owner, owner->GetClass(), fd->flagbit);
return CheckDeprecatedFlags(owner, fd->flagbit);
}
else
#ifdef __BIG_ENDIAN__
@ -270,70 +270,106 @@ INTBOOL CheckActorFlag(AActor *owner, const char *flagname, bool printerror)
// Handles the deprecated flags and sets the respective properties
// to appropriate values. This is solely intended for backwards
// compatibility so mixing this with code that is aware of the real
// properties is not recommended
// properties is not recommended and may not do what is expected
//
//===========================================================================
void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int index)
void HandleDeprecatedFlags(AActor *actor, int set, int index)
{
switch (index)
{
case DEPF_FIREDAMAGE:
defaults->DamageType = set? NAME_Fire : NAME_None;
actor->DamageType = set? NAME_Fire : NAME_None;
break;
case DEPF_ICEDAMAGE:
defaults->DamageType = set? NAME_Ice : NAME_None;
actor->DamageType = set? NAME_Ice : NAME_None;
break;
case DEPF_LOWGRAVITY:
defaults->Gravity = set ? 1. / 8 : 1.;
actor->Gravity = set ? 1. / 8 : 1.;
break;
case DEPF_SHORTMISSILERANGE:
defaults->maxtargetrange = set? 896. : 0.;
actor->maxtargetrange = set? 896. : 0.;
break;
case DEPF_LONGMELEERANGE:
defaults->meleethreshold = set? 196. : 0.;
actor->meleethreshold = set? 196. : 0.;
break;
case DEPF_QUARTERGRAVITY:
defaults->Gravity = set ? 1. / 4 : 1.;
actor->Gravity = set ? 1. / 4 : 1.;
break;
case DEPF_FIRERESIST:
info->SetDamageFactor(NAME_Fire, set ? 0.5 : 1.);
actor->GetClass()->SetDamageFactor(NAME_Fire, set ? 0.5 : 1.);
break;
// the bounce flags will set the compatibility bounce modes to remain compatible
case DEPF_HERETICBOUNCE:
defaults->BounceFlags &= ~(BOUNCE_TypeMask|BOUNCE_UseSeeSound);
if (set) defaults->BounceFlags |= BOUNCE_HereticCompat;
actor->BounceFlags &= ~(BOUNCE_TypeMask|BOUNCE_UseSeeSound);
if (set) actor->BounceFlags |= BOUNCE_HereticCompat;
break;
case DEPF_HEXENBOUNCE:
defaults->BounceFlags &= ~(BOUNCE_TypeMask|BOUNCE_UseSeeSound);
if (set) defaults->BounceFlags |= BOUNCE_HexenCompat;
actor->BounceFlags &= ~(BOUNCE_TypeMask|BOUNCE_UseSeeSound);
if (set) actor->BounceFlags |= BOUNCE_HexenCompat;
break;
case DEPF_DOOMBOUNCE:
defaults->BounceFlags &= ~(BOUNCE_TypeMask|BOUNCE_UseSeeSound);
if (set) defaults->BounceFlags |= BOUNCE_DoomCompat;
actor->BounceFlags &= ~(BOUNCE_TypeMask|BOUNCE_UseSeeSound);
if (set) actor->BounceFlags |= BOUNCE_DoomCompat;
break;
case DEPF_PICKUPFLASH:
if (set)
{
defaults->PointerVar<PClass>(NAME_PickupFlash) = FindClassTentative("PickupFlash", RUNTIME_CLASS(AActor));
actor->PointerVar<PClass>(NAME_PickupFlash) = FindClassTentative("PickupFlash", RUNTIME_CLASS(AActor));
}
else
{
defaults->PointerVar<PClass>(NAME_PickupFlash) = nullptr;
actor->PointerVar<PClass>(NAME_PickupFlash) = nullptr;
}
break;
case DEPF_INTERHUBSTRIP: // Old system was 0 or 1, so if the flag is cleared, assume 1.
defaults->IntVar(NAME_InterHubAmount) = set ? 0 : 1;
actor->IntVar(NAME_InterHubAmount) = set ? 0 : 1;
break;
case DEPF_HIGHERMPROB:
defaults->MinMissileChance = set ? 160 : 200;
actor->MinMissileChance = set ? 160 : 200;
break;
case DEPF_MISSILEMORE:
if (set)
{
if (actor->missilechancemult == 0.125) actor->missilechancemult = 0.0625;
else actor->missilechancemult = 0.5;
}
else
{
if (actor->missilechancemult == 0.0625) actor->missilechancemult = 0.125;
else actor->missilechancemult = 1;
}
break;
case DEPF_MISSILEEVENMORE:
if (set)
{
if (actor->missilechancemult == 0.5) actor->missilechancemult = 0.0625;
else actor->missilechancemult = 0.125;
}
else
{
if (actor->missilechancemult == 0.0625) actor->missilechancemult = 0.5;
else actor->missilechancemult = 1;
}
break;
default:
break; // silence GCC
}
}
// the interface here works on object, but currently all deprecated flags affect subclasses of Actor only
DEFINE_ACTION_FUNCTION_NATIVE(DObject, HandleDeprecatedFlags, HandleDeprecatedFlags)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(set);
PARAM_INT(index);
HandleDeprecatedFlags(self, set, index);
return 0;
}
//===========================================================================
//
// CheckDeprecatedFlags
@ -344,7 +380,7 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in
//
//===========================================================================
bool CheckDeprecatedFlags(AActor *actor, PClassActor *info, int index)
int CheckDeprecatedFlags(AActor *actor, int index)
{
// A deprecated flag is false if
// a) it hasn't been added here
@ -368,7 +404,7 @@ bool CheckDeprecatedFlags(AActor *actor, PClassActor *info, int index)
case DEPF_QUARTERGRAVITY:
return actor->Gravity == 1./4;
case DEPF_FIRERESIST:
for (auto &df : info->ActorInfo()->DamageFactors)
for (auto &df : actor->GetClass()->ActorInfo()->DamageFactors)
{
if (df.first == NAME_Fire) return df.second == 0.5;
}
@ -387,15 +423,28 @@ bool CheckDeprecatedFlags(AActor *actor, PClassActor *info, int index)
return actor->PointerVar<PClass>(NAME_PickupFlash) == PClass::FindClass(NAME_PickupFlash);
case DEPF_INTERHUBSTRIP:
return !(actor->IntVar(NAME_InterHubAmount));
return actor->IntVar(NAME_InterHubAmount) == 0;
case DEPF_HIGHERMPROB:
return actor->MinMissileChance <= 160;
case DEPF_MISSILEMORE:
return actor->missilechancemult == 0.5 || actor->missilechancemult == 0.0625;
case DEPF_MISSILEEVENMORE:
return actor->missilechancemult == 0.125 || actor->missilechancemult == 0.0625;
}
return false; // Any entirely unknown flag is not set
}
DEFINE_ACTION_FUNCTION_NATIVE(DObject, CheckDeprecatedFlags, CheckDeprecatedFlags)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(index);
ACTION_RETURN_INT(CheckDeprecatedFlags(self, index));
}
//==========================================================================
//
//
@ -642,6 +691,16 @@ DEFINE_PROPERTY(floatbobstrength, F, Actor)
defaults->FloatBobStrength = id;
}
//==========================================================================
//
//==========================================================================
DEFINE_PROPERTY(floatbobfactor, F, Actor)
{
PROP_DOUBLE_PARM(id, 0);
if (id <= 0) I_Error ("FloatBobFactor must be above 0.0");
defaults->FloatBobFactor = id;
}
//==========================================================================
//
//==========================================================================

View file

@ -2250,7 +2250,12 @@ void FormatMapName(FLevelLocals *self, int cr, FString *result)
bool ishub = (cluster != nullptr && (cluster->flags & CLUSTER_HUB));
*result = "";
if (am_showmaplabel == 1 || (am_showmaplabel == 2 && !ishub))
// If a label is specified, use it uncontitionally here.
if (self->info->MapLabel.IsNotEmpty())
{
*result << self->info->MapLabel << ": ";
}
else if (am_showmaplabel == 1 || (am_showmaplabel == 2 && !ishub))
{
*result << self->MapName << ": ";
}
@ -2755,6 +2760,7 @@ DEFINE_FIELD_X(LevelInfo, level_info_t, Music)
DEFINE_FIELD_X(LevelInfo, level_info_t, LightningSound)
DEFINE_FIELD_X(LevelInfo, level_info_t, LevelName)
DEFINE_FIELD_X(LevelInfo, level_info_t, AuthorName)
DEFINE_FIELD_X(LevelInfo, level_info_t, MapLabel)
DEFINE_FIELD_X(LevelInfo, level_info_t, musicorder)
DEFINE_FIELD_X(LevelInfo, level_info_t, skyspeed1)
DEFINE_FIELD_X(LevelInfo, level_info_t, skyspeed2)

View file

@ -2005,6 +2005,7 @@ DEFINE_FIELD(AActor, strafecount)
DEFINE_FIELD(AActor, target)
DEFINE_FIELD(AActor, master)
DEFINE_FIELD(AActor, tracer)
DEFINE_FIELD(AActor, damagesource)
DEFINE_FIELD(AActor, LastHeard)
DEFINE_FIELD(AActor, lastenemy)
DEFINE_FIELD(AActor, LastLookActor)
@ -2035,6 +2036,7 @@ DEFINE_FIELD(AActor, DamageType)
DEFINE_FIELD(AActor, DamageTypeReceived)
DEFINE_FIELD(AActor, FloatBobPhase)
DEFINE_FIELD(AActor, FloatBobStrength)
DEFINE_FIELD(AActor, FloatBobFactor)
DEFINE_FIELD(AActor, RipperLevel)
DEFINE_FIELD(AActor, RipLevelMin)
DEFINE_FIELD(AActor, RipLevelMax)
@ -2042,6 +2044,7 @@ DEFINE_FIELD(AActor, Species)
DEFINE_FIELD(AActor, alternative)
DEFINE_FIELD(AActor, goal)
DEFINE_FIELD(AActor, MinMissileChance)
DEFINE_FIELD(AActor, missilechancemult)
DEFINE_FIELD(AActor, LastLookPlayerNumber)
DEFINE_FIELD(AActor, SpawnFlags)
DEFINE_FIELD(AActor, meleethreshold)

View file

@ -795,13 +795,14 @@ void ZCCDoomCompiler::ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg)
auto fd = FindFlag(cls, n1, n2, true);
if (fd != nullptr)
{
if (fd->varflags & VARF_Deprecated)
if ((fd->varflags & VARF_Deprecated) && fd->deprecationVersion <= this->mVersion)
{
Warn(flg, "Deprecated flag '%s%s%s' used", n1, n2 ? "." : "", n2 ? n2 : "");
Warn(flg, "Deprecated flag '%s%s%s' used, deprecated since %d.%d.%d", n1, n2 ? "." : "", n2 ? n2 : "",
fd->deprecationVersion.major, fd->deprecationVersion.minor, fd->deprecationVersion.revision);
}
if (fd->structoffset == -1)
{
HandleDeprecatedFlags((AActor*)cls->Defaults, cls, flg->set, fd->flagbit);
HandleDeprecatedFlags((AActor*)cls->Defaults, flg->set, fd->flagbit);
}
else
{

View file

@ -57,6 +57,9 @@
#include "texturemanager.h"
#include "v_draw.h"
#include "serializer.h"
#include "s_music.h"
CVAR(Bool, wi_percents, true, CVAR_ARCHIVE)
CVAR(Bool, wi_showtotaltime, true, CVAR_ARCHIVE)
CVAR(Bool, wi_noautostartmap, false, CVAR_USERINFO | CVAR_ARCHIVE)
@ -109,6 +112,8 @@ class DInterBackground : public DObject
{
ANIM_ALWAYS, // determined by patch entry
ANIM_PIC, // continuous
ANIM_FRAME, // determined by frame properties
ANIM_NONE, // frozen (used for infinite duration frames)
// condition bitflags
ANIM_IFVISITED = 8,
@ -137,13 +142,41 @@ class DInterBackground : public DObject
FString Level;
};
struct frame_t
{
FTextureID image;
uint32_t type;
// In tics!
int duration;
int max_duration;
};
enum ECondition
{
COND_NONE = 0, // None.
COND_GREATER = 1, // Parameter is greater than current map number.
COND_EQUAL = 2, // Parameter is equal to current map number.
COND_VISITED = 3, // Map number corresponding to parameter is visited.
COND_NOTSECRET = 4, // Current map is not a secret one.
COND_SECRETVISTED = 5, // Any secret map was visited.
COND_TALLY = 6, // Tally screen.
COND_ENTERING = 7, // "Entering" screen.
};
struct condition_t
{
ECondition condition;
int param;
};
struct in_anim_t
{
int type; // Made an int so I can use '|'
int period; // period in tics between animations
yahpt_t loc; // location of animation
int data; // ALWAYS: n/a, RANDOM: period deviation (<256)
TArray<FTextureID> frames; // actual graphics for frames of animations
TArray<frame_t> frames; // actual graphics for frames of animations
TArray<condition_t> conditions; // conditions to display the animation
// following must be initialized to zero before use!
int nexttic; // next value of bcnt (used in conjunction with period)
@ -162,9 +195,15 @@ class DInterBackground : public DObject
}
};
struct in_layer_t
{
TArray<in_anim_t> anims;
TArray<condition_t> conditions; // conditions to display the animations.
};
private:
TArray<lnode_t> lnodes;
TArray<in_anim_t> anims;
TArray<in_layer_t> layers;
int bcnt = 0; // used for timing of background animation
TArray<FTextureID> yah; // You Are Here graphic
FTextureID splat{}; // splat
@ -174,12 +213,14 @@ private:
int bgwidth = -1;
int bgheight = -1;
bool tilebackground = false;
FString muslump;
public:
DInterBackground(wbstartstruct_t *wbst);
bool LoadBackground(bool isenterpic);
bool IsUsingMusic();
void updateAnimatedBack();
void drawBackground(int state, bool drawsplat, bool snl_pointeron);
@ -242,6 +283,70 @@ private:
}
}
}
//====================================================================
//
// Draws the splats and the 'You are here' arrows
//
//====================================================================
bool ConditionsMet(int state, TArray<condition_t>& conditions)
{
for (auto& condition : conditions)
{
switch (condition.condition)
{
case ECondition::COND_NONE:
break;
case ECondition::COND_EQUAL:
{
auto* li = FindLevelInfo(state != StatCount ? wbs->next.GetChars() : wbs->current.GetChars());
if (!li)
return false;
if (li->broken_id24_levelnum != condition.param)
return false;
break;
}
case ECondition::COND_GREATER:
{
auto* li = FindLevelInfo(state != StatCount ? wbs->next.GetChars() : wbs->current.GetChars());
if (!li)
return false;
if (li->broken_id24_levelnum <= condition.param)
return false;
break;
}
case ECondition::COND_VISITED:
{
auto* li = FindLevelByNum(condition.param);
if (li == NULL || !(li->flags & LEVEL_VISITED))
return false;
break;
}
case ECondition::COND_NOTSECRET:
{
auto* li = FindLevelInfo(state != StatCount ? wbs->next.GetChars() : wbs->current.GetChars());
if (!li)
return false;
if (li->flags3 & LEVEL3_SECRET)
return false;
break;
}
case ECondition::COND_SECRETVISTED:
if (!SecretLevelVisited())
return false;
break;
case ECondition::COND_TALLY:
if (state != StatCount)
return false;
break;
case ECondition::COND_ENTERING:
if (state == StatCount)
return false;
break;
}
}
return true;
}
};
DInterBackground:: DInterBackground(wbstartstruct_t *wbst)
@ -270,11 +375,13 @@ bool DInterBackground::LoadBackground(bool isenterpic)
{
const char* lumpname = nullptr;
const char* exitpic = nullptr;
const char* exitanim = nullptr;
char buffer[10];
in_anim_t an;
lnode_t pt;
FTextureID texture;
bool noautostartmap = false;
bool id24anim = false;
bcnt = 0;
@ -284,8 +391,17 @@ bool DInterBackground::LoadBackground(bool isenterpic)
level_info_t* li = FindLevelInfo(wbs->current.GetChars());
if (li != nullptr)
{
exitpic = li->ExitPic.GetChars();
if (li->ExitPic.IsNotEmpty()) tilebackground = false;
if (li->ExitAnim.IsNotEmpty())
{
id24anim = true;
exitpic = li->ExitAnim.GetChars();
tilebackground = false;
}
else
{
exitpic = li->ExitPic.GetChars();
if (li->ExitPic.IsNotEmpty()) tilebackground = false;
}
}
lumpname = exitpic;
@ -294,8 +410,17 @@ bool DInterBackground::LoadBackground(bool isenterpic)
level_info_t* li = FindLevelInfo(wbs->next.GetChars());
if (li != NULL)
{
lumpname = li->EnterPic.GetChars();
if (li->EnterPic.IsNotEmpty()) tilebackground = false;
if (li->EnterAnim.IsNotEmpty())
{
id24anim = true;
lumpname = li->EnterAnim.GetChars();
tilebackground = false;
}
else
{
lumpname = li->EnterPic.GetChars();
if (li->EnterPic.IsNotEmpty()) tilebackground = false;
}
}
}
@ -382,12 +507,251 @@ bool DInterBackground::LoadBackground(bool isenterpic)
}
lnodes.Clear();
anims.Clear();
layers.Clear();
yah.Clear();
splat.SetInvalid();
if (!id24anim)
layers.Resize(1);
if (id24anim)
{
try
{
int lumpnum = fileSystem.CheckNumForFullName(lumpname, true);
if (lumpnum == -1)
{
I_Error("Intermission animation lump %s not found!", lumpname);
}
auto data = fileSystem.ReadFile(lumpnum);
FSerializer jsonReader;
jsonReader.mLumpName = fileSystem.GetFileFullPath(lumpnum);
lumpname = jsonReader.mLumpName.GetChars();
if (jsonReader.OpenReader(data.string(), data.size()))
{
FString type = jsonReader.GetString("type");
if (type.Compare("interlevel"))
{
I_Error("Interlevel lump %s is not interlevel!", lumpname);
}
if (jsonReader.BeginObject("data"))
{
FString music = jsonReader.GetString("music");
if (music.IsEmpty())
{
I_Error("No music lump specified for intermission animation %s!", lumpname);
}
muslump = music;
if (!MusicExists(muslump.GetChars()))
{
I_Error("Music lump %s not found!", muslump.GetChars());
}
// Check for background lump.
FString backgroundimage = jsonReader.GetString("backgroundimage");
texture = TexMan.CheckForTexture(backgroundimage.GetChars(), ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
if (!texture.isValid())
{
if (backgroundimage.IsEmpty())
{
I_Error("No background image specified for intermission animation %s!", lumpname);
}
else
{
I_Error("Texture %s not found!", backgroundimage.GetChars());
}
}
if (!jsonReader.IsKeyNull("layers") && jsonReader.BeginArray("layers"))
{
int size = jsonReader.ArraySize();
if (size == 0)
I_Error("Zero-length 'layers' array in %s", lumpname);
for (int i = 0; i < size; i++)
{
if (jsonReader.BeginObject(nullptr))
{
layers.Reserve(1);
auto& layer = layers.back();
if (!jsonReader.IsKeyNull("conditions") && jsonReader.BeginArray("conditions"))
{
int condition_size = jsonReader.ArraySize();
if (condition_size == 0)
{
I_Error("Condition array empty and not null for layer %d in lump %s", layers.Size(), lumpname);
}
for (int j = 0; j < condition_size; j++)
{
if (jsonReader.BeginObject(nullptr))
{
ECondition cond = COND_NONE;
int param = 0;
::Serialize(jsonReader, "condition", (int&)cond, nullptr);
::Serialize(jsonReader, "param", param, nullptr);
layer.conditions.Push(condition_t{ cond, param });
jsonReader.EndObject();
}
}
jsonReader.EndArray();
}
// Read anims.
if (jsonReader.BeginArray("anims"))
{
int anim_size = jsonReader.ArraySize();
if (anim_size == 0)
{
I_Error("No animations defined for layer %d in lump %s", layers.Size(), lumpname);
}
for (int j = 0; j < anim_size; j++)
{
if (jsonReader.BeginObject(nullptr))
{
layer.anims.Reserve(1);
auto& anim = layer.anims.back();
anim.Reset();
anim.type = ANIM_FRAME;
anim.ctr = 0;
anim.data = 0;
::Serialize(jsonReader, "x", anim.loc.x, nullptr);
::Serialize(jsonReader, "y", anim.loc.y, nullptr);
if (!jsonReader.IsKeyNull("conditions") && jsonReader.BeginArray("conditions"))
{
int condition_size = jsonReader.ArraySize();
if (condition_size == 0)
{
I_Error("Condition array empty and not null for anim %d, layer %d in lump %s", layer.anims.Size(), layers.Size(), lumpname);
}
for (int k = 0; k < condition_size; k++)
{
if (jsonReader.BeginObject(nullptr))
{
ECondition cond = COND_NONE;
int param = 0;
::Serialize(jsonReader, "condition", (int&)cond, nullptr);
::Serialize(jsonReader, "param", param, nullptr);
anim.conditions.Push(condition_t{ cond, param });
jsonReader.EndObject();
}
}
jsonReader.EndArray();
}
if (jsonReader.BeginArray("frames"))
{
int frames = jsonReader.ArraySize();
if (frames == 0)
{
I_Error("No frames defined for anim %d, layer %d in lump %s", layer.anims.Size(), layers.Size(), lumpname);
}
for (int k = 0; k < frames; k++)
{
if (jsonReader.BeginObject(nullptr))
{
double duration = 0.0;
double maxduration = 0.0;
anim.frames.Reserve(1);
auto& frame = anim.frames.back();
::Serialize(jsonReader, "duration", duration, nullptr);
::Serialize(jsonReader, "maxduration", maxduration, nullptr);
::Serialize(jsonReader, "type", frame.type, nullptr);
FString image = jsonReader.GetString("image");
if (image.IsEmpty())
{
I_Error("No image defined for frame %d, anim %d, layer %d in lump %s", anim.frames.Size(), layer.anims.Size(), layers.Size(), lumpname);
}
frame.image = TexMan.CheckForTexture(image.GetChars(), ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
if (!frame.image.isValid())
{
I_Error("Texture '%s' not found!", image.GetChars());
}
frame.duration = round(duration * (double)TICRATE);
frame.max_duration = round(maxduration * (double)TICRATE);
if (frame.duration <= 0 && duration > 0.0)
{
frame.duration = 1;
}
if (frame.max_duration <= 0 && maxduration > 0.0)
{
frame.max_duration = 1;
}
if (anim.frames.Size() == 1)
{
if (frame.type & 0x1000)
{
frame.type &= ~0x1000;
anim.nexttic = 1 + (M_Random() % frame.duration);
}
else
{
switch (frame.type)
{
case 0x1:
anim.type = ANIM_NONE;
break;
case 0x4:
anim.nexttic = 1 + M_Random(frame.max_duration - frame.duration + 1) + frame.duration;
break;
case 0x2:
anim.nexttic = 1 + frame.duration;
break;
}
}
}
jsonReader.EndObject();
}
}
jsonReader.EndArray();
}
jsonReader.EndObject();
}
}
jsonReader.EndArray();
}
jsonReader.EndObject();
}
}
jsonReader.EndArray();
}
jsonReader.EndObject();
}
jsonReader.Close();
}
}
// This is deliberate. Errors coming from here shouldn't cause a console abort.
catch (CRecoverableError& error)
{
Printf(TEXTCOLOR_RED "Failed to parse intermission anim definition %s: %s.\nFalling back to non-anim definitions.\n", lumpname, error.GetMessage());
if (isenterpic)
li->EnterAnim = "";
else
li->ExitAnim = "";
return LoadBackground(isenterpic);
}
}
// a name with a starting '$' indicates an intermission script
if (*lumpname != '$')
else if (*lumpname != '$')
{
texture = TexMan.CheckForTexture(lumpname, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
}
@ -520,18 +884,20 @@ bool DInterBackground::LoadBackground(bool isenterpic)
if (!sc.CheckString("{"))
{
sc.MustGetString();
an.frames.Push(TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny));
auto textureId = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
an.frames.Push(frame_t{ textureId, 0, 0, 0 });
}
else
{
while (!sc.CheckString("}"))
{
sc.MustGetString();
an.frames.Push(TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny));
auto textureId = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
an.frames.Push(frame_t{ textureId, 0, 0, 0 });
}
}
an.ctr = -1;
anims.Push(an);
layers[0].anims.Push(an);
break;
case 13: // Pic
@ -542,8 +908,8 @@ bool DInterBackground::LoadBackground(bool isenterpic)
an.loc.y = sc.Number;
sc.MustGetString();
an.frames.Reserve(1); // allocate exactly one element
an.frames[0] = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
anims.Push(an);
an.frames[0].image = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny);
layers[0].anims.Push(an);
break;
default:
@ -554,6 +920,7 @@ bool DInterBackground::LoadBackground(bool isenterpic)
}
else
{
Printf("Intermission script %s not found!\n", lumpname + 1);
texture = TexMan.GetTextureID("INTERPIC", ETextureType::MiscPatch);
}
@ -585,27 +952,59 @@ void DInterBackground::updateAnimatedBack()
unsigned int i;
bcnt++;
for (i = 0; i<anims.Size(); i++)
if (bcnt == 1 && muslump.IsNotEmpty())
{
in_anim_t * a = &anims[i];
switch (a->type & ANIM_TYPE)
S_ChangeMusic(muslump.GetChars());
}
for (auto& layer : layers)
{
auto& anims = layer.anims;
for (i = 0; i < anims.Size(); i++)
{
case ANIM_ALWAYS:
if (bcnt >= a->nexttic)
in_anim_t* a = &anims[i];
switch (a->type & ANIM_TYPE)
{
if (++a->ctr >= (int)a->frames.Size())
case ANIM_NONE:
break;
case ANIM_FRAME:
if (bcnt >= a->nexttic)
{
if (a->data == 0) a->ctr = 0;
else a->ctr--;
if (++a->ctr >= (int)a->frames.Size())
a->ctr = 0;
switch (a->frames[a->ctr].type & 0x7) {
case 0x1:
a->type &= ANIM_CONDITION;
a->type = ANIM_NONE;
break;
case 0x4:
a->nexttic = bcnt + M_Random(a->frames[a->ctr].max_duration - a->frames[a->ctr].duration + 1) + a->frames[a->ctr].duration;
break;
case 0x2:
a->nexttic = bcnt + a->frames[a->ctr].duration;
break;
}
}
a->nexttic = bcnt + a->period;
break;
case ANIM_ALWAYS:
if (bcnt >= a->nexttic)
{
if (++a->ctr >= (int)a->frames.Size())
{
if (a->data == 0) a->ctr = 0;
else a->ctr--;
}
a->nexttic = bcnt + a->period;
}
break;
case ANIM_PIC:
a->ctr = 0;
break;
}
break;
case ANIM_PIC:
a->ctr = 0;
break;
}
}
}
@ -656,51 +1055,59 @@ void DInterBackground::drawBackground(int state, bool drawsplat, bool snl_pointe
ClearRect(twod, 0, 0, twod->GetWidth(), twod->GetHeight(), 0, 0);
}
for (i = 0; i<anims.Size(); i++)
for (auto& layer : layers)
{
in_anim_t * a = &anims[i];
level_info_t *li;
auto& anims = layer.anims;
switch (a->type & ANIM_CONDITION)
if (!ConditionsMet(state, layer.conditions))
continue;
for (i = 0; i < anims.Size(); i++)
{
case ANIM_IFVISITED:
li = FindLevelInfo(a->LevelName.GetChars());
if (li == NULL || !(li->flags & LEVEL_VISITED)) continue;
break;
in_anim_t* a = &anims[i];
level_info_t* li;
case ANIM_IFNOTVISITED:
li = FindLevelInfo(a->LevelName.GetChars());
if (li == NULL || (li->flags & LEVEL_VISITED)) continue;
break;
switch (a->type & ANIM_CONDITION)
{
case ANIM_IFVISITED:
li = FindLevelInfo(a->LevelName.GetChars());
if (li == NULL || !(li->flags & LEVEL_VISITED)) continue;
break;
// StatCount means 'leaving' - everything else means 'entering'!
case ANIM_IFENTERING:
if (state == StatCount || a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
case ANIM_IFNOTVISITED:
li = FindLevelInfo(a->LevelName.GetChars());
if (li == NULL || (li->flags & LEVEL_VISITED)) continue;
break;
case ANIM_IFNOTENTERING:
if (state != StatCount && !a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
// StatCount means 'leaving' - everything else means 'entering'!
case ANIM_IFENTERING:
if (state == StatCount || a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
case ANIM_IFLEAVING:
if (state != StatCount || a->LevelName.CompareNoCase(wbs->current, 8)) continue;
break;
case ANIM_IFNOTENTERING:
if (state != StatCount && !a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
case ANIM_IFNOTLEAVING:
if (state == StatCount && !a->LevelName.CompareNoCase(wbs->current, 8)) continue;
break;
case ANIM_IFLEAVING:
if (state != StatCount || a->LevelName.CompareNoCase(wbs->current, 8)) continue;
break;
case ANIM_IFTRAVELLING:
if (a->LevelName2.CompareNoCase(wbs->current, 8) || a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
case ANIM_IFNOTLEAVING:
if (state == StatCount && !a->LevelName.CompareNoCase(wbs->current, 8)) continue;
break;
case ANIM_IFNOTTRAVELLING:
if (!a->LevelName2.CompareNoCase(wbs->current, 8) && !a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
case ANIM_IFTRAVELLING:
if (a->LevelName2.CompareNoCase(wbs->current, 8) || a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
case ANIM_IFNOTTRAVELLING:
if (!a->LevelName2.CompareNoCase(wbs->current, 8) && !a->LevelName.CompareNoCase(wbs->next, 8)) continue;
break;
}
if (a->ctr >= 0 && ConditionsMet(state, a->conditions))
DrawTexture(twod, a->frames[a->ctr].image, false, a->loc.x, a->loc.y,
DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, DTA_FullscreenScale, FSMode_ScaleToFit43, TAG_DONE);
}
if (a->ctr >= 0)
DrawTexture(twod, a->frames[a->ctr], false, a->loc.x, a->loc.y,
DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, DTA_FullscreenScale, FSMode_ScaleToFit43, TAG_DONE);
}
if (drawsplat)
@ -731,6 +1138,23 @@ DEFINE_ACTION_FUNCTION(DInterBackground, drawBackground)
return 0;
}
//====================================================================
//
//
//
//====================================================================
bool DInterBackground::IsUsingMusic()
{
return muslump.IsNotEmpty();
}
DEFINE_ACTION_FUNCTION(DInterBackground, IsUsingMusic)
{
PARAM_SELF_PROLOGUE(DInterBackground);
ACTION_RETURN_BOOL(self->IsUsingMusic());
}
IMPLEMENT_CLASS(DInterBackground, true, false)
//====================================================================

View file

@ -2516,6 +2516,7 @@ OptionValue "SpriteclipModes"
1, "$OPTVAL_SMART"
2, "$OPTVAL_ALWAYS"
3, "$OPTVAL_SMARTER"
-1, "$OPTVAL_ANAMORPHIC"
}
OptionValue "EnhancedStealth"

View file

@ -148,6 +148,7 @@ class Actor : Thinker native
native Actor Target;
native Actor Master;
native Actor Tracer;
native readonly Actor DamageSource;
native Actor LastHeard;
native Actor LastEnemy;
native Actor LastLookActor;
@ -178,6 +179,7 @@ class Actor : Thinker native
native name DamageTypeReceived;
native uint8 FloatBobPhase;
native double FloatBobStrength;
native double FloatBobFactor;
native int RipperLevel;
native int RipLevelMin;
native int RipLevelMax;
@ -185,6 +187,7 @@ class Actor : Thinker native
native Actor Alternative;
native Actor goal;
native uint8 MinMissileChance;
native double MissileChanceMult;
native int8 LastLookPlayerNumber;
native uint SpawnFlags;
native double meleethreshold;
@ -337,6 +340,7 @@ class Actor : Thinker native
property WeaveIndexXY: WeaveIndexXY;
property WeaveIndexZ: WeaveIndexZ;
property MinMissileChance: MinMissileChance;
property MissileChanceMult: MissileChanceMult;
property MaxStepHeight: MaxStepHeight;
property MaxDropoffHeight: MaxDropoffHeight;
property MaxSlopeSteepness: MaxSlopeSteepness;
@ -403,6 +407,7 @@ class Actor : Thinker native
RenderStyle 'Normal';
Alpha 1;
MinMissileChance 200;
MissileChanceMult 1.0;
MeleeRange 64 - MELEEDELTA;
MaxDropoffHeight 24;
MaxStepHeight 24;
@ -413,6 +418,7 @@ class Actor : Thinker native
FloatSpeed 4;
FloatBobPhase -1; // randomly initialize by default
FloatBobStrength 1.0;
FloatBobFactor 1.0;
Gravity 1;
Friction 1;
DamageFactor 1.0; // damage multiplier as target of damage.

View file

@ -250,7 +250,7 @@ extend class Actor
else if (originator.player)
{
// A player always spawns a monster friendly to him
mo.bFriendly = true;
mo.A_SetFriendly(true);
mo.SetFriendPlayer(originator.player);
Actor attacker=originator.player.attacker;
@ -609,6 +609,7 @@ extend class Actor
if (flags & XF_THRUSTLESS) pflags |= RADF_THRUSTLESS;
if (flags & XF_NOALLIES) pflags |= RADF_NOALLIES;
if (flags & XF_CIRCULAR) pflags |= RADF_CIRCULAR;
if (flags & XF_CIRCULARTHRUST) pflags |= RADF_CIRCULARTHRUST;
int count = RadiusAttack (target, damage, distance, damagetype, pflags, fulldamagedistance);
if (!(flags & XF_NOSPLASH)) CheckSplash(distance);

View file

@ -16,8 +16,8 @@ class Cyberdemon : Actor
PainChance 20;
Monster;
MinMissileChance 160;
MissileChanceMult 0.5;
+BOSS
+MISSILEMORE
+FLOORCLIP
+NORADIUSDMG
+DONTMORPH

View file

@ -14,8 +14,9 @@ class LostSoul : Actor
Speed 8;
Damage 3;
PainChance 256;
MissileChanceMult 0.5;
Monster;
+FLOAT +NOGRAVITY +MISSILEMORE +DONTFALL +NOICEDEATH +ZDOOMTRANS +RETARGETAFTERSLAM
+FLOAT +NOGRAVITY +DONTFALL +NOICEDEATH +ZDOOMTRANS +RETARGETAFTERSLAM
AttackSound "skull/melee";
PainSound "skull/pain";
DeathSound "skull/death";

View file

@ -218,7 +218,7 @@ extend class Actor
{
if (target && IsFriend(target))
{ // And I thought you were my friend!
bFriendly = false;
A_SetFriendly(false);
}
A_NoBlocking();
A_PainShootSkull(spawntype, angle + 90);

View file

@ -342,12 +342,16 @@ extend class Actor
void A_CPosRefire()
{
if (HitFriend())
{
SetState(SeeState);
return;
}
// keep firing unless target got out of sight
A_FaceTarget();
if (Random[CPosRefire](0, 255) >= 40)
{
if (!target
|| HitFriend()
|| target.health <= 0
|| !CheckSight(target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES))
{

View file

@ -15,7 +15,7 @@ class Revenant : Actor
PainChance 100;
Monster;
MeleeThreshold 196;
+MISSILEMORE
MissileChanceMult 0.5;
+FLOORCLIP
SeeSound "skeleton/sight";
PainSound "skeleton/pain";

View file

@ -13,9 +13,9 @@ class SpiderMastermind : Actor
Mass 1000;
Speed 12;
PainChance 40;
MissileChanceMult 0.5;
Monster;
+BOSS
+MISSILEMORE
+FLOORCLIP
+NORADIUSDMG
+DONTMORPH
@ -73,12 +73,16 @@ extend class Actor
{
void A_SpidRefire()
{
if (HitFriend())
{
SetState(SeeState);
return;
}
// keep firing unless target got out of sight
A_FaceTarget();
if (Random[CPosRefire](0, 255) >= 10)
{
if (!target
|| HitFriend()
|| target.health <= 0
|| !CheckSight(target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES))
{

View file

@ -13,12 +13,12 @@ class HereticImp : Actor
Mass 50;
Speed 10;
Painchance 200;
MissileChanceMult 0.5;
Monster;
+FLOAT
+NOGRAVITY
+SPAWNFLOAT
+DONTOVERLAP
+MISSILEMORE
SeeSound "himp/sight";
AttackSound "himp/attack";
PainSound "himp/pain";
@ -159,7 +159,7 @@ class HereticImpLeader : HereticImp
{
Species "HereticImpLeader";
Health 80;
-MISSILEMORE
MissileChanceMult 1;
AttackSound "himp/leaderattack";
}
States

View file

@ -2867,6 +2867,7 @@ struct PlayerInfo native play // self is what internally is known as player_t
native clearscope int GetColorSet() const;
native clearscope int GetPlayerClassNum() const;
native clearscope int GetSkin() const;
native clearscope int GetSkinCount() const;
native clearscope bool GetNeverSwitch() const;
native clearscope int GetGender() const;
native clearscope int GetTeam() const;
@ -2878,6 +2879,7 @@ struct PlayerInfo native play // self is what internally is known as player_t
native bool GetFViewBob() const;
native double GetStillBob() const;
native void SetFOV(float fov);
native int SetSkin(int skinIndex);
native clearscope bool GetClassicFlight() const;
native void SendPitchLimits();
native clearscope bool HasWeaponsInSlot(int slot) const;

View file

@ -118,7 +118,7 @@ class Acolyte : StrifeHumanoid
void A_BeShadowyFoe()
{
A_SetRenderStyle(HR_SHADOW, STYLE_Translucent);
bFriendly = false;
A_SetFriendly(false);
}
//============================================================================
@ -155,7 +155,7 @@ class AcolyteTan : Acolyte
{
Default
{
+MISSILEMORE +MISSILEEVENMORE
MissileChanceMult 0.0625;
DropItem "ClipOfBullets";
}
}
@ -166,7 +166,7 @@ class AcolyteRed : Acolyte
{
Default
{
+MISSILEMORE +MISSILEEVENMORE
MissileChanceMult 0.0625;
Translation 0;
}
}
@ -177,7 +177,7 @@ class AcolyteRust : Acolyte
{
Default
{
+MISSILEMORE +MISSILEEVENMORE
MissileChanceMult 0.0625;
Translation 1;
}
}
@ -188,7 +188,7 @@ class AcolyteGray : Acolyte
{
Default
{
+MISSILEMORE +MISSILEEVENMORE
MissileChanceMult 0.0625;
Translation 2;
}
}
@ -199,7 +199,7 @@ class AcolyteDGreen : Acolyte
{
Default
{
+MISSILEMORE +MISSILEEVENMORE
MissileChanceMult 0.0625;
Translation 3;
}
}
@ -210,7 +210,7 @@ class AcolyteGold : Acolyte
{
Default
{
+MISSILEMORE +MISSILEEVENMORE
MissileChanceMult 0.0625;
Translation 4;
}
}
@ -243,7 +243,7 @@ class AcolyteShadow : Acolyte
{
Default
{
+MISSILEMORE
MissileChanceMult 0.5;
DropItem "ClipOfBullets";
}
States

View file

@ -11,10 +11,10 @@ class Crusader : Actor
Mass 400;
Health 400;
Painchance 128;
MissileChanceMult 0.5;
Monster;
+FLOORCLIP
+DONTMORPH
+MISSILEMORE
+INCOMBAT
+NOICEDEATH
+NOBLOOD

View file

@ -11,6 +11,7 @@ class Sentinel : Actor
Radius 23;
Height 53;
Mass 300;
MissileChanceMult 0.5;
Monster;
+SPAWNCEILING
+NOGRAVITY
@ -18,7 +19,6 @@ class Sentinel : Actor
+NOBLOOD
+NOBLOCKMONST
+INCOMBAT
+MISSILEMORE
+LOOKALLAROUND
+NEVERRESPAWN
MinMissileChance 150;
@ -164,13 +164,17 @@ extend class Actor
void A_SentinelRefire()
{
A_FaceTarget ();
if (HitFriend())
{
SetState(SeeState);
return;
}
if (random[SentinelRefire]() >= 30)
{
if (target == NULL ||
target.health <= 0 ||
!CheckSight (target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ||
HitFriend() ||
(MissileState == NULL && !CheckMeleeRange()) ||
random[SentinelRefire]() < 40)
{

View file

@ -267,6 +267,7 @@ enum EExplodeFlags
XF_THRUSTLESS = 64,
XF_NOALLIES = 128,
XF_CIRCULAR = 256,
XF_CIRCULARTHRUST = 512,
};
// Flags for A_RadiusThrust
@ -276,6 +277,7 @@ enum ERadiusThrustFlags
RTF_NOIMPACTDAMAGE = 2,
RTF_NOTMISSILE = 4,
RTF_THRUSTZ = 16,
RTF_CIRCULARTHRUST = 512,
};
// Flags for A_RadiusDamageSelf
@ -1254,7 +1256,8 @@ enum RadiusDamageFlags
RADF_OLDRADIUSDAMAGE = 32,
RADF_THRUSTLESS = 64,
RADF_NOALLIES = 128,
RADF_CIRCULAR = 256
RADF_CIRCULAR = 256,
RADF_CIRCULARTHRUST = 512,
};
enum IntermissionSequenceType

View file

@ -346,6 +346,7 @@ struct LevelInfo native
native readonly String LightningSound;
native readonly String Music;
native readonly String LevelName;
native readonly String MapLabel;
native readonly String AuthorName;
native readonly int musicorder;
native readonly float skyspeed1;

View file

@ -143,6 +143,15 @@ enum EPrintLevel
PRINT_NOLOG = 2048, // Flag - do not print to log file
};
enum EDebugLevel
{
DMSG_OFF, // no developer messages.
DMSG_ERROR, // general notification messages
DMSG_WARNING, // warnings
DMSG_NOTIFY, // general notification messages
DMSG_SPAMMY, // for those who want to see everything, regardless of its usefulness.
};
enum EConsoleState
{
c_up = 0,
@ -665,6 +674,7 @@ struct Console native
native static void HideConsole();
native static vararg void Printf(string fmt, ...);
native static vararg void PrintfEx(int printlevel, string fmt, ...);
native static vararg void DebugPrintf(int debuglevel, string fmt, ...);
}
struct CVar native
@ -750,6 +760,8 @@ class Object native
private native static Class<Object> BuiltinNameToClass(Name nm, Class<Object> filter);
private native static Object BuiltinClassCast(Object inptr, Class<Object> test);
private native static Function<void> BuiltinFunctionPtrCast(Function<void> inptr, voidptr newtype);
private native static void HandleDeprecatedFlags(Object obj, bool set, int index);
private native static bool CheckDeprecatedFlags(Object obj, int index);
native static uint MSTime();
native static double MSTimeF();

View file

@ -435,6 +435,11 @@ struct Sector native play
SECMF_UNDERWATERMASK = 32+64,
SECMF_DRAWN = 128, // sector has been drawn at least once
SECMF_HIDDEN = 256, // Do not draw on textured automap
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
SECMF_LIFT = 2048, // For MBF monster AI
SECMF_HURTMONSTERS = 4096, // Monsters in this sector are hurt like players.
SECMF_HARMINAIR = 8192, // Actors in this sector are also hurt mid-air.
}
native uint16 MoreFlags;
@ -452,6 +457,9 @@ struct Sector native play
SECF_ENDLEVEL = 512, // ends level when health goes below 10
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling.
SECF_NOATTACK = 2048, // monsters cannot start attacks in this sector.
SECF_EXIT1 = 4096,
SECF_EXIT2 = 8192,
SECF_KILLMONSTERS = 16384,// Monsters in this sector are instantly killed.
SECF_WASSECRET = 1 << 30, // a secret that was discovered
SECF_SECRET = 1 << 31, // a secret sector

View file

@ -6,6 +6,7 @@ class InterBackground native ui version("2.5")
native virtual bool LoadBackground(bool isenterpic);
native virtual void updateAnimatedBack();
native virtual void drawBackground(int CurState, bool drawsplat, bool snl_pointeron);
native virtual bool IsUsingMusic();
}
// This is obsolete. Hopefully this was never used...
@ -825,7 +826,8 @@ class StatusScreen : ScreenJob abstract version("2.5")
virtual void StartMusic()
{
Level.SetInterMusic(wbs.next);
if (!bg.IsUsingMusic())
Level.SetInterMusic(wbs.next);
}
//====================================================================