diff --git a/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp b/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp index 23bd073c6..d2eb62f0a 100644 --- a/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp +++ b/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp @@ -19,6 +19,8 @@ LineEdit::LineEdit(Widget* parent) : Widget(parent) LineEdit::~LineEdit() { + delete timer; + delete scroll_timer; } bool LineEdit::IsReadOnly() const diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 75ae3c474..9a219fd41 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -114,6 +114,7 @@ Note: All fields default to false unless mentioned otherwise. // texture to behave like an impassable line (projectiles // pass through it). checkswitchrange = ; // Switches can only be activated when vertically reachable. + walking = ; // Crossing activation checks if creature is on the ground, and fails if they are not. blockprojectiles = ; // Line blocks all projectiles blockuse = ; // Line blocks all use actions blocksight = ; // Line blocks monster line of sight @@ -306,6 +307,8 @@ Note: All fields default to false unless mentioned otherwise. leakiness = ; // Probability of leaking through radiation suit (0 = never, 256 = always), default = 0. damageterraineffect = ; // Will spawn a terrain splash when damage is inflicted. Default = false. damagehazard = ; // Changes damage model to Strife's delayed damage for the given sector. Default = false. + hurtmonsters = ; // Non-players like monsters and decorations are hurt by this sector in the same manner as player. Doesn't work with damagehazard. + harminair = ; // Actors in this sector are harmed by the damage effects of the floor even if they aren't touching it. floorterrain = ; // Sets the terrain for the sector's floor. Default = 'use the flat texture's terrain definition.' ceilingterrain = ; // Sets the terrain for the sector's ceiling. Default = 'use the flat texture's terrain definition.' floor_reflect = ; // reflectiveness of floor (OpenGL only, not functional on sloped sectors) diff --git a/src/common/2d/v_2ddrawer.cpp b/src/common/2d/v_2ddrawer.cpp index ffb753491..10db8fafa 100644 --- a/src/common/2d/v_2ddrawer.cpp +++ b/src/common/2d/v_2ddrawer.cpp @@ -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()); diff --git a/src/common/audio/music/music.cpp b/src/common/audio/music/music.cpp index 13837e93e..01b3a5da3 100644 --- a/src/common/audio/music/music.cpp +++ b/src/common/audio/music/music.cpp @@ -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; diff --git a/src/common/audio/music/s_music.h b/src/common/audio/music/s_music.h index cddfd36d6..629f6f95d 100644 --- a/src/common/audio/music/s_music.h +++ b/src/common/audio/music/s_music.h @@ -45,6 +45,9 @@ bool S_StartMusic (const char *music_name); // Start music using , and set whether looping bool S_ChangeMusic (const char *music_name, int order=0, bool looping=true, bool force=false); +// Check if exists +bool MusicExists(const char* music_name); + void S_RestartMusic (); void S_MIDIDeviceChanged(int newdev); diff --git a/src/common/engine/namedef.h b/src/common/engine/namedef.h index a1f791f9d..26c7943df 100644 --- a/src/common/engine/namedef.h +++ b/src/common/engine/namedef.h @@ -278,6 +278,8 @@ xx(BuiltinNameToClass) xx(BuiltinClassCast) xx(BuiltinFunctionPtrCast) xx(BuiltinFindTranslation) +xx(HandleDeprecatedFlags) +xx(CheckDeprecatedFlags) xx(ScreenJobRunner) xx(Action) diff --git a/src/common/engine/serializer.cpp b/src/common/engine/serializer.cpp index dcf930842..97ded1adf 100644 --- a/src/common/engine/serializer.cpp +++ b/src/common/engine/serializer.cpp @@ -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()) diff --git a/src/common/engine/serializer.h b/src/common/engine/serializer.h index 99ffb72dc..40b6ab706 100644 --- a/src/common/engine/serializer.h +++ b/src/common/engine/serializer.h @@ -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); diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 5b1a975a6..b14fb267c 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -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(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(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; } diff --git a/src/common/scripting/core/types.cpp b/src/common/scripting/core/types.cpp index 8e13dcc52..b70783f3e 100644 --- a/src/common/scripting/core/types.cpp +++ b/src/common/scripting/core/types.cpp @@ -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); diff --git a/src/common/scripting/interface/vmnatives.cpp b/src/common/scripting/interface/vmnatives.cpp index 8a5674afd..2b57ceb25 100644 --- a/src/common/scripting/interface/vmnatives.cpp +++ b/src/common/scripting/interface/vmnatives.cpp @@ -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(); diff --git a/src/common/startscreen/startscreen.cpp b/src/common/startscreen/startscreen.cpp index 82fa28f02..9825b41b9 100644 --- a/src/common/startscreen/startscreen.cpp +++ b/src/common/startscreen/startscreen.cpp @@ -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 diff --git a/src/common/startscreen/startscreen.h b/src/common/startscreen/startscreen.h index afac6c759..c90d092f9 100644 --- a/src/common/startscreen/startscreen.h +++ b/src/common/startscreen/startscreen.h @@ -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); diff --git a/src/d_main.cpp b/src/d_main.cpp index 4c91a2319..d5eb38180 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -164,6 +164,7 @@ bool M_SetSpecialMenu(FName& menu, int param); // game specific checks const FIWADInfo *D_FindIWAD(TArray &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"); diff --git a/src/doomdata.h b/src/doomdata.h index 09235c03a..303e0eca7 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -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), }; diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index f8da8f096..74cc1c119 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -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 } diff --git a/src/gamedata/d_dehacked.cpp b/src/gamedata/d_dehacked.cpp index 77e9d1320..8ddcc7935 100644 --- a/src/gamedata/d_dehacked.cpp +++ b/src/gamedata/d_dehacked.cpp @@ -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; } }, diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index 4822f35a8..27609e6da 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -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 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; + } + } } diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index 1ee37ec0e..3bdaaa0a3 100644 --- a/src/gamedata/g_mapinfo.h +++ b/src/gamedata/g_mapinfo.h @@ -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 > 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(); diff --git a/src/gamedata/r_defs.h b/src/gamedata/r_defs.h index bc3f3a58a..50b491ece 100644 --- a/src/gamedata/r_defs.h +++ b/src/gamedata/r_defs.h @@ -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 diff --git a/src/gamedata/textures/animations.cpp b/src/gamedata/textures/animations.cpp index 7b7a1049b..b0dce0a55 100644 --- a/src/gamedata/textures/animations.cpp +++ b/src/gamedata/textures/animations.cpp @@ -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) { diff --git a/src/gamedata/umapinfo.cpp b/src/gamedata/umapinfo.cpp index 08e16d9f8..9ba72e61b 100644 --- a/src/gamedata/umapinfo.cpp +++ b/src/gamedata/umapinfo.cpp @@ -41,6 +41,8 @@ struct UMapEntry FString LevelName; FString InterText; FString InterTextSecret; + FString author; + FString label; TArray 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 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]) { diff --git a/src/maploader/udmf.cpp b/src/maploader/udmf.cpp index c813cc4a4..1820270d3 100644 --- a/src/maploader/udmf.cpp +++ b/src/maploader/udmf.cpp @@ -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; diff --git a/src/namedef_custom.h b/src/namedef_custom.h index 9359888e4..7f7684d3d 100644 --- a/src/namedef_custom.h +++ b/src/namedef_custom.h @@ -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) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 4e0ab378c..dcf739977 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -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 alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals. TObjPtr tracer; // Thing being chased/attacked for tracers TObjPtr master; // Thing which spawned this one (prevents mutual attacks) + TObjPtr 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(); diff --git a/src/playsim/actorinlines.h b/src/playsim/actorinlines.h index 939807c98..bbc173801 100644 --- a/src/playsim/actorinlines.h +++ b/src/playsim/actorinlines.h @@ -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 diff --git a/src/playsim/p_3dfloors.cpp b/src/playsim/p_3dfloors.cpp index dd5a2bb0a..505bfa794 100644 --- a/src/playsim/p_3dfloors.cpp +++ b/src/playsim/p_3dfloors.cpp @@ -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; } diff --git a/src/playsim/p_3dfloors.h b/src/playsim/p_3dfloors.h index 05d222137..2dba7f713 100644 --- a/src/playsim/p_3dfloors.h +++ b/src/playsim/p_3dfloors.h @@ -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); diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index cf15cbf38..55f501488 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -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)) diff --git a/src/playsim/p_local.h b/src/playsim/p_local.h index cde45f403..6d02d2a56 100644 --- a/src/playsim/p_local.h +++ b/src/playsim/p_local.h @@ -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, diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index aca24d5d9..6126a5059 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -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 + } + } } } } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 141d13e29..d75f88f35 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -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. diff --git a/src/playsim/p_spec.cpp b/src/playsim/p_spec.cpp index ba2036971..9c3fde6f4 100644 --- a/src/playsim/p_spec.cpp +++ b/src/playsim/p_spec.cpp @@ -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. diff --git a/src/playsim/p_spec.h b/src/playsim/p_spec.h index cc7d990a9..ebb5d93f9 100644 --- a/src/playsim/p_spec.h +++ b/src/playsim/p_spec.h @@ -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); diff --git a/src/playsim/p_user.cpp b/src/playsim/p_user.cpp index fefb0f943..a98a7c523 100644 --- a/src/playsim/p_user.cpp +++ b/src/playsim/p_user.cpp @@ -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) diff --git a/src/playsim/shadowinlines.h b/src/playsim/shadowinlines.h index 574abd251..b36b051ac 100644 --- a/src/playsim/shadowinlines.h +++ b/src/playsim/shadowinlines.h @@ -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; diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index 71543d30d..fdd6b9507 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -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 diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 9d405190d..e9e24192f 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -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; diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index a1cc7702e..19c8f38ab 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -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 { diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 34cd34666..f12cca99d 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -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 diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 5bbb35fae..129c06b78 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -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; + } + } } } } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index bedd66cd3..2154f4db5 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -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(NAME_PickupFlash) = FindClassTentative("PickupFlash", RUNTIME_CLASS(AActor)); + actor->PointerVar(NAME_PickupFlash) = FindClassTentative("PickupFlash", RUNTIME_CLASS(AActor)); } else { - defaults->PointerVar(NAME_PickupFlash) = nullptr; + actor->PointerVar(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(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; +} + //========================================================================== // //========================================================================== diff --git a/src/scripting/vmthunks.cpp b/src/scripting/vmthunks.cpp index 2abff431e..9229a8dc3 100644 --- a/src/scripting/vmthunks.cpp +++ b/src/scripting/vmthunks.cpp @@ -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) diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index acf253807..18cd8aa32 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -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) diff --git a/src/scripting/zscript/zcc_compile_doom.cpp b/src/scripting/zscript/zcc_compile_doom.cpp index 4becfc4fe..3563967e5 100644 --- a/src/scripting/zscript/zcc_compile_doom.cpp +++ b/src/scripting/zscript/zcc_compile_doom.cpp @@ -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 { diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index dddc37c31..3af0a7829 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -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 frames; // actual graphics for frames of animations + TArray frames; // actual graphics for frames of animations + TArray 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 anims; + TArray conditions; // conditions to display the animations. + }; + private: TArray lnodes; - TArray anims; + TArray layers; int bcnt = 0; // used for timing of background animation TArray 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& 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; itype & 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; itype & 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) //==================================================================== diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 83b74a1b7..60295433b 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2516,6 +2516,7 @@ OptionValue "SpriteclipModes" 1, "$OPTVAL_SMART" 2, "$OPTVAL_ALWAYS" 3, "$OPTVAL_SMARTER" + -1, "$OPTVAL_ANAMORPHIC" } OptionValue "EnhancedStealth" diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 2d1356048..e7cdf917b 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -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. diff --git a/wadsrc/static/zscript/actors/attacks.zs b/wadsrc/static/zscript/actors/attacks.zs index db9da6f35..c1cb445e1 100644 --- a/wadsrc/static/zscript/actors/attacks.zs +++ b/wadsrc/static/zscript/actors/attacks.zs @@ -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); diff --git a/wadsrc/static/zscript/actors/doom/cyberdemon.zs b/wadsrc/static/zscript/actors/doom/cyberdemon.zs index d795fc31a..b18861bf9 100644 --- a/wadsrc/static/zscript/actors/doom/cyberdemon.zs +++ b/wadsrc/static/zscript/actors/doom/cyberdemon.zs @@ -16,8 +16,8 @@ class Cyberdemon : Actor PainChance 20; Monster; MinMissileChance 160; + MissileChanceMult 0.5; +BOSS - +MISSILEMORE +FLOORCLIP +NORADIUSDMG +DONTMORPH diff --git a/wadsrc/static/zscript/actors/doom/lostsoul.zs b/wadsrc/static/zscript/actors/doom/lostsoul.zs index 7feb8553b..0712a6d49 100644 --- a/wadsrc/static/zscript/actors/doom/lostsoul.zs +++ b/wadsrc/static/zscript/actors/doom/lostsoul.zs @@ -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"; diff --git a/wadsrc/static/zscript/actors/doom/painelemental.zs b/wadsrc/static/zscript/actors/doom/painelemental.zs index 244261303..75f90a02a 100644 --- a/wadsrc/static/zscript/actors/doom/painelemental.zs +++ b/wadsrc/static/zscript/actors/doom/painelemental.zs @@ -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); diff --git a/wadsrc/static/zscript/actors/doom/possessed.zs b/wadsrc/static/zscript/actors/doom/possessed.zs index 4fa9e0f7e..26966e1f0 100644 --- a/wadsrc/static/zscript/actors/doom/possessed.zs +++ b/wadsrc/static/zscript/actors/doom/possessed.zs @@ -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)) { diff --git a/wadsrc/static/zscript/actors/doom/revenant.zs b/wadsrc/static/zscript/actors/doom/revenant.zs index 36599e3a9..90492727a 100644 --- a/wadsrc/static/zscript/actors/doom/revenant.zs +++ b/wadsrc/static/zscript/actors/doom/revenant.zs @@ -15,7 +15,7 @@ class Revenant : Actor PainChance 100; Monster; MeleeThreshold 196; - +MISSILEMORE + MissileChanceMult 0.5; +FLOORCLIP SeeSound "skeleton/sight"; PainSound "skeleton/pain"; diff --git a/wadsrc/static/zscript/actors/doom/spidermaster.zs b/wadsrc/static/zscript/actors/doom/spidermaster.zs index 3ad0b8846..c2685f480 100644 --- a/wadsrc/static/zscript/actors/doom/spidermaster.zs +++ b/wadsrc/static/zscript/actors/doom/spidermaster.zs @@ -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)) { diff --git a/wadsrc/static/zscript/actors/heretic/hereticimp.zs b/wadsrc/static/zscript/actors/heretic/hereticimp.zs index 3a938b4ed..5ea8b063e 100644 --- a/wadsrc/static/zscript/actors/heretic/hereticimp.zs +++ b/wadsrc/static/zscript/actors/heretic/hereticimp.zs @@ -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 diff --git a/wadsrc/static/zscript/actors/player/player.zs b/wadsrc/static/zscript/actors/player/player.zs index 69e534b70..c2da5f46c 100644 --- a/wadsrc/static/zscript/actors/player/player.zs +++ b/wadsrc/static/zscript/actors/player/player.zs @@ -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; diff --git a/wadsrc/static/zscript/actors/strife/acolyte.zs b/wadsrc/static/zscript/actors/strife/acolyte.zs index 5aeb0e63a..b982d8429 100644 --- a/wadsrc/static/zscript/actors/strife/acolyte.zs +++ b/wadsrc/static/zscript/actors/strife/acolyte.zs @@ -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 diff --git a/wadsrc/static/zscript/actors/strife/crusader.zs b/wadsrc/static/zscript/actors/strife/crusader.zs index f08d0f847..daee0d2e9 100644 --- a/wadsrc/static/zscript/actors/strife/crusader.zs +++ b/wadsrc/static/zscript/actors/strife/crusader.zs @@ -11,10 +11,10 @@ class Crusader : Actor Mass 400; Health 400; Painchance 128; + MissileChanceMult 0.5; Monster; +FLOORCLIP +DONTMORPH - +MISSILEMORE +INCOMBAT +NOICEDEATH +NOBLOOD diff --git a/wadsrc/static/zscript/actors/strife/sentinel.zs b/wadsrc/static/zscript/actors/strife/sentinel.zs index 0acc8580f..84b9c3f0c 100644 --- a/wadsrc/static/zscript/actors/strife/sentinel.zs +++ b/wadsrc/static/zscript/actors/strife/sentinel.zs @@ -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) { diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 80a4f11fc..2212af4cd 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -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 diff --git a/wadsrc/static/zscript/doombase.zs b/wadsrc/static/zscript/doombase.zs index 403763066..760cb373c 100644 --- a/wadsrc/static/zscript/doombase.zs +++ b/wadsrc/static/zscript/doombase.zs @@ -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; diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index f55919116..18fe1b363 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -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 BuiltinNameToClass(Name nm, Class filter); private native static Object BuiltinClassCast(Object inptr, Class test); private native static Function BuiltinFunctionPtrCast(Function 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(); diff --git a/wadsrc/static/zscript/mapdata.zs b/wadsrc/static/zscript/mapdata.zs index db217e7cc..080e05f32 100644 --- a/wadsrc/static/zscript/mapdata.zs +++ b/wadsrc/static/zscript/mapdata.zs @@ -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 diff --git a/wadsrc/static/zscript/ui/statscreen/statscreen.zs b/wadsrc/static/zscript/ui/statscreen/statscreen.zs index 4b50f0cf7..3ae9cf447 100644 --- a/wadsrc/static/zscript/ui/statscreen/statscreen.zs +++ b/wadsrc/static/zscript/ui/statscreen/statscreen.zs @@ -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); } //====================================================================