diff --git a/.gitignore b/.gitignore index b434b52a7..6e9e5b9de 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ /build_vc2019-64 /build_vc2019-32 /build__ +gzdoom-crash.log diff --git a/src/common/engine/serializer.cpp b/src/common/engine/serializer.cpp index 261aa464c..dcf930842 100644 --- a/src/common/engine/serializer.cpp +++ b/src/common/engine/serializer.cpp @@ -294,6 +294,21 @@ bool FSerializer::BeginObject(const char *name) // //========================================================================== +bool FSerializer::HasKey(const char* name) +{ + if (isReading()) + { + return r->FindKey(name) != nullptr; + } + return false; +} + +//========================================================================== +// +// +// +//========================================================================== + bool FSerializer::HasObject(const char* name) { if (isReading()) diff --git a/src/common/engine/serializer.h b/src/common/engine/serializer.h index 29677237b..ae25853d9 100644 --- a/src/common/engine/serializer.h +++ b/src/common/engine/serializer.h @@ -91,6 +91,7 @@ public: void ReadObjects(bool hubtravel); bool BeginObject(const char *name); void EndObject(); + bool HasKey(const char* name); bool HasObject(const char* name); bool BeginArray(const char *name); void EndArray(); @@ -245,6 +246,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString *def); FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def); FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &mo, struct ModelOverride *def); +FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverride &mo, struct AnimModelOverride *def); FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimOverride &ao, struct AnimOverride *def); FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval); @@ -259,6 +261,16 @@ FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) return arc; } +template +FSerializer &Serialize(FSerializer &arc, const char *key, std::pair &value, std::pair *def) +{ + arc.BeginObject(key); + Serialize(arc, "first", value.first, def ? &def->first : nullptr); + Serialize(arc, "second", value.second, def ? &def->second : nullptr); + arc.EndObject(); + return arc; +} + template FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value, TArray *def) { diff --git a/src/common/filesystem/source/fs_findfile.cpp b/src/common/filesystem/source/fs_findfile.cpp index 06d9b69f1..1f8824fc7 100644 --- a/src/common/filesystem/source/fs_findfile.cpp +++ b/src/common/filesystem/source/fs_findfile.cpp @@ -36,6 +36,18 @@ #include #include +#ifndef _WIN32 + +#include +#include +#include +#include +#include + +#include + +#endif + namespace FileSys { enum @@ -63,14 +75,6 @@ enum #ifndef _WIN32 -#include -#include -#include -#include -#include - -#include - struct findstate_t { std::string path; diff --git a/src/common/models/model.cpp b/src/common/models/model.cpp index a8ee8958f..0f1603415 100644 --- a/src/common/models/model.cpp +++ b/src/common/models/model.cpp @@ -43,8 +43,6 @@ #include "texturemanager.h" #include "modelrenderer.h" - -TArray savedModelFiles; TDeletingArray Models; TArray SpriteModelFrames; TMap BaseSpriteModelFrames; @@ -160,7 +158,7 @@ unsigned FindModel(const char * path, const char * modelfile, bool silent) for(unsigned i = 0; i< Models.Size(); i++) { - if (!Models[i]->mFileName.CompareNoCase(fullname)) return i; + if (Models[i]->mFileName.CompareNoCase(fullname) == 0) return i; } auto len = fileSystem.FileLength(lump); @@ -236,6 +234,7 @@ unsigned FindModel(const char * path, const char * modelfile, bool silent) } // The vertex buffer cannot be initialized here because this gets called before OpenGL is initialized model->mFileName = fullname; + model->mFilePath = {path, modelfile}; return Models.Push(model); } diff --git a/src/common/models/model.h b/src/common/models/model.h index f5bbf43db..7d5746e33 100644 --- a/src/common/models/model.h +++ b/src/common/models/model.h @@ -18,7 +18,7 @@ struct FSpriteModelFrame; FTextureID LoadSkin(const char* path, const char* fn); void FlushModels(); -extern TArray savedModelFiles; + extern TDeletingArray Models; extern TArray SpriteModelFrames; extern TMap BaseSpriteModelFrames; @@ -76,6 +76,7 @@ enum EFrameError class FModel { public: + FModel(); virtual ~FModel(); @@ -100,7 +101,9 @@ public: void DestroyVertexBuffer(); bool hasSurfaces = false; + FString mFileName; + std::pair mFilePath; FSpriteModelFrame *baseFrame; private: diff --git a/src/common/models/voxels.cpp b/src/common/models/voxels.cpp index a2e371582..4d1332353 100644 --- a/src/common/models/voxels.cpp +++ b/src/common/models/voxels.cpp @@ -164,6 +164,7 @@ FVoxel *R_LoadKVX(int lumpnum) auto lump = fileSystem.ReadFile(lumpnum); // FileData adds an extra 0 byte to the end. auto rawvoxel = lump.bytes(); int voxelsize = (int)(lump.size()); + if (voxelsize <= 768 + 4) return nullptr; // Oh, KVX, why couldn't you have a proper header? We'll just go through // and collect each MIP level, doing lots of range checking, and if the diff --git a/src/common/objects/dobject.cpp b/src/common/objects/dobject.cpp index 09bea7999..15400ce23 100644 --- a/src/common/objects/dobject.cpp +++ b/src/common/objects/dobject.cpp @@ -363,54 +363,49 @@ size_t DObject::PropagateMark() const PClass *info = GetClass(); if (!PClass::bShutdown) { - const size_t *offsets = info->FlatPointers; - if (offsets == NULL) + if (info->FlatPointers == nullptr) { - const_cast(info)->BuildFlatPointers(); - offsets = info->FlatPointers; - } - while (*offsets != ~(size_t)0) - { - GC::Mark((DObject **)((uint8_t *)this + *offsets)); - offsets++; + info->BuildFlatPointers(); + assert(info->FlatPointers); } - offsets = info->ArrayPointers; - if (offsets == NULL) + for(size_t i = 0; i < info->FlatPointersSize; i++) { - const_cast(info)->BuildArrayPointers(); - offsets = info->ArrayPointers; + GC::Mark((DObject **)((uint8_t *)this + info->FlatPointers[i].first)); } - while (*offsets != ~(size_t)0) + + if (info->ArrayPointers == nullptr) { - auto aray = (TArray*)((uint8_t *)this + *offsets); + info->BuildArrayPointers(); + assert(info->ArrayPointers); + } + + for(size_t i = 0; i < info->ArrayPointersSize; i++) + { + auto aray = (TArray*)((uint8_t *)this + info->ArrayPointers[i].first); for (auto &p : *aray) { GC::Mark(&p); } - offsets++; } + if (info->MapPointers == nullptr) { - const std::pair *maps = info->MapPointers; - if (maps == NULL) - { - const_cast(info)->BuildMapPointers(); - maps = info->MapPointers; - } - while (maps->first != ~(size_t)0) - { - if(maps->second->RegType == REGT_STRING) - { // FString,DObject* - PropagateMarkMap((ZSMap*)((uint8_t *)this + maps->first)); - } - else - { // uint32_t,DObject* - PropagateMarkMap((ZSMap*)((uint8_t *)this + maps->first)); - } - maps++; - } + info->BuildMapPointers(); + assert(info->MapPointers); + } + for(size_t i = 0; i < info->MapPointersSize; i++) + { + PMap * type = static_cast(info->MapPointers[i].second); + if(type->KeyType->RegType == REGT_STRING) + { // FString,DObject* + PropagateMarkMap((ZSMap*)((uint8_t *)this + info->MapPointers[i].first)); + } + else + { // uint32_t,DObject* + PropagateMarkMap((ZSMap*)((uint8_t *)this + info->MapPointers[i].first)); + } } return info->Size; } @@ -423,46 +418,116 @@ size_t DObject::PropagateMark() // //========================================================================== -size_t DObject::PointerSubstitution (DObject *old, DObject *notOld) +template +static void MapPointerSubstitution(M *map, size_t &changed, DObject *old, DObject *notOld, const bool shouldSwap) +{ + TMapIterator it(*map); + typename M::Pair * p; + while(it.NextPair(p)) + { + if (p->Value == old) + { + if (shouldSwap) + { + p->Value = notOld; + changed++; + } + else if (p->Value != nullptr) + { + p->Value = nullptr; + changed++; + } + } + } +} + +size_t DObject::PointerSubstitution (DObject *old, DObject *notOld, bool nullOnFail) { const PClass *info = GetClass(); - const size_t *offsets = info->FlatPointers; size_t changed = 0; - if (offsets == NULL) + if (info->FlatPointers == nullptr) { - const_cast(info)->BuildFlatPointers(); - offsets = info->FlatPointers; - } - while (*offsets != ~(size_t)0) - { - if (*(DObject **)((uint8_t *)this + *offsets) == old) - { - *(DObject **)((uint8_t *)this + *offsets) = notOld; - changed++; - } - offsets++; + info->BuildFlatPointers(); + assert(info->FlatPointers); } - offsets = info->ArrayPointers; - if (offsets == NULL) + for(size_t i = 0; i < info->FlatPointersSize; i++) { - const_cast(info)->BuildArrayPointers(); - offsets = info->ArrayPointers; + size_t offset = info->FlatPointers[i].first; + auto& obj = *(DObject**)((uint8_t*)this + offset); + + if (obj == old) + { + // If a pointer's type is null, that means it's native and anything native is safe to swap + // around due to its inherit type expansiveness. + if (info->FlatPointers[i].second == nullptr || notOld->IsKindOf(info->FlatPointers[i].second->PointedClass())) + { + obj = notOld; + changed++; + } + else if (nullOnFail && obj != nullptr) + { + obj = nullptr; + changed++; + } + } } - while (*offsets != ~(size_t)0) + + if (info->ArrayPointers == nullptr) { - auto aray = (TArray*)((uint8_t *)this + *offsets); + info->BuildArrayPointers(); + assert(info->ArrayPointers); + } + + for(size_t i = 0; i < info->ArrayPointersSize; i++) + { + const bool isType = notOld->IsKindOf(static_cast(info->ArrayPointers[i].second->ElementType)->PointedClass()); + + if (!isType && !nullOnFail) + continue; + + auto aray = (TArray*)((uint8_t*)this + info->ArrayPointers[i].first); for (auto &p : *aray) { if (p == old) { - p = notOld; - changed++; + if (isType) + { + p = notOld; + changed++; + } + else if (p != nullptr) + { + p = nullptr; + changed++; + } } } - offsets++; } + if (info->MapPointers == nullptr) + { + info->BuildMapPointers(); + assert(info->MapPointers); + } + + for(size_t i = 0; i < info->MapPointersSize; i++) + { + PMap * type = static_cast(info->MapPointers[i].second); + + const bool isType = notOld->IsKindOf(static_cast(type->ValueType)->PointedClass()); + if (!isType && !nullOnFail) + continue; + + if(type->KeyType->RegType == REGT_STRING) + { // FString,DObject* + MapPointerSubstitution((ZSMap*)((uint8_t *)this + info->MapPointers[i].first), changed, old, notOld, isType); + } + else + { // uint32_t,DObject* + MapPointerSubstitution((ZSMap*)((uint8_t *)this + info->MapPointers[i].first), changed, old, notOld, isType); + } + } return changed; } diff --git a/src/common/objects/dobject.h b/src/common/objects/dobject.h index bb295fb78..ed5fc996b 100644 --- a/src/common/objects/dobject.h +++ b/src/common/objects/dobject.h @@ -246,7 +246,7 @@ public: inline int* IntArray(FName field); // This is only needed for swapping out PlayerPawns and absolutely nothing else! - virtual size_t PointerSubstitution (DObject *old, DObject *notOld); + virtual size_t PointerSubstitution (DObject *old, DObject *notOld, bool nullOnFail); PClass *GetClass() const { diff --git a/src/common/objects/dobjtype.cpp b/src/common/objects/dobjtype.cpp index 7b4463011..0d1bfdf9a 100644 --- a/src/common/objects/dobjtype.cpp +++ b/src/common/objects/dobjtype.cpp @@ -80,9 +80,7 @@ DEFINE_GLOBAL(WP_NOCHANGE); // PRIVATE DATA DEFINITIONS ------------------------------------------------ // A harmless non-nullptr FlatPointer for classes without pointers. -static const size_t TheEnd = ~(size_t)0; - -static const std::pair TheMapEnd = {~(size_t)0 , nullptr}; +static const std::pair TheEnd = {~(size_t)0 , nullptr}; //========================================================================== // @@ -768,75 +766,81 @@ PSymbol *PClass::FindSymbol(FName symname, bool searchparents) const // //========================================================================== -void PClass::BuildFlatPointers () +void PClass::BuildFlatPointers() const { + using pairType = std::pair; + if (FlatPointers != nullptr) { // Already built: Do nothing. return; } - else if (ParentClass == nullptr) - { // No parent (i.e. DObject: FlatPointers is the same as Pointers. - if (Pointers == nullptr) - { // No pointers: Make FlatPointers a harmless non-nullptr. - FlatPointers = &TheEnd; - } - else - { - FlatPointers = Pointers; - } - } else { - ParentClass->BuildFlatPointers (); - - TArray ScriptPointers; - - // Collect all pointers in scripted fields. These are not part of the Pointers list. - for (auto field : Fields) + TArray NativePointers; + if (Pointers != nullptr) { - if (!(field->Flags & VARF_Native)) + for (size_t i = 0; Pointers[i] != ~(size_t)0; i++) { - field->Type->SetPointer(Defaults, unsigned(field->Offset), &ScriptPointers); + NativePointers.Push({Pointers[i], nullptr}); // native pointers have a null type } } - if (Pointers == nullptr && ScriptPointers.Size() == 0) - { // No new pointers: Just use the same FlatPointers as the parent. - FlatPointers = ParentClass->FlatPointers; + if (ParentClass == nullptr) + { // No parent (i.e. DObject): FlatPointers is the same as Pointers. + if (NativePointers.Size() == 0) + { // No pointers: Make FlatPointers a harmless non-nullptr. + FlatPointers = (pairType*)(&TheEnd); + FlatPointersSize = 0; + } + else + { + pairType *flat = (pairType*)ClassDataAllocator.Alloc(sizeof(pairType) * NativePointers.Size()); + memcpy(flat, NativePointers.Data(), sizeof(pairType) * NativePointers.Size()); + + FlatPointers = flat; + FlatPointersSize = NativePointers.Size(); + } } else - { // New pointers: Create a new FlatPointers array and add them. - int numPointers, numSuperPointers; + { + ParentClass->BuildFlatPointers(); - if (Pointers != nullptr) + TArray ScriptPointers; + + // Collect all pointers in scripted fields. These are not part of the Pointers list. + for (auto field : Fields) { - // Count pointers defined by this class. - for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++) + if (!(field->Flags & VARF_Native)) { + field->Type->SetPointer(Defaults, unsigned(field->Offset), &ScriptPointers); } } - else numPointers = 0; - // Count pointers defined by superclasses. - for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++) - { } + if (NativePointers.Size() == 0 && ScriptPointers.Size() == 0) + { // No new pointers: Just use the same FlatPointers as the parent. + FlatPointers = ParentClass->FlatPointers; + FlatPointersSize = ParentClass->FlatPointersSize; + } + else + { // New pointers: Create a new FlatPointers array and add them. + // Concatenate them into a new array + pairType *flat = (pairType*)ClassDataAllocator.Alloc(sizeof(pairType) * (ParentClass->FlatPointersSize + NativePointers.Size() + ScriptPointers.Size())); - // Concatenate them into a new array - size_t *flat = (size_t*)ClassDataAllocator.Alloc(sizeof(size_t) * (numPointers + numSuperPointers + ScriptPointers.Size() + 1)); - if (numSuperPointers > 0) - { - memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers); + if (ParentClass->FlatPointersSize > 0) + { + memcpy (flat, ParentClass->FlatPointers, sizeof(pairType) * ParentClass->FlatPointersSize); + } + if (NativePointers.Size() > 0) + { + memcpy(flat + ParentClass->FlatPointersSize, NativePointers.Data(), sizeof(pairType) * NativePointers.Size()); + } + if (ScriptPointers.Size() > 0) + { + memcpy(flat + ParentClass->FlatPointersSize + NativePointers.Size(), &ScriptPointers[0], sizeof(pairType) * ScriptPointers.Size()); + } + FlatPointers = flat; + FlatPointersSize = ParentClass->FlatPointersSize + NativePointers.Size() + ScriptPointers.Size(); } - if (numPointers > 0) - { - memcpy(flat + numSuperPointers, Pointers, sizeof(size_t)*numPointers); - } - if (ScriptPointers.Size() > 0) - { - memcpy(flat + numSuperPointers + numPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size()); - } - flat[numSuperPointers + numPointers + ScriptPointers.Size()] = ~(size_t)0; - FlatPointers = flat; } } } @@ -849,21 +853,24 @@ void PClass::BuildFlatPointers () // //========================================================================== -void PClass::BuildArrayPointers() +void PClass::BuildArrayPointers() const { + using pairType = std::pair; + if (ArrayPointers != nullptr) { // Already built: Do nothing. return; } else if (ParentClass == nullptr) - { // No parent (i.e. DObject: FlatPointers is the same as Pointers. - ArrayPointers = &TheEnd; + { // No parent (i.e. DObject): Make ArrayPointers a harmless non-nullptr. + ArrayPointers = (pairType*)(&TheEnd); + ArrayPointersSize = 0; } else { ParentClass->BuildArrayPointers(); - TArray ScriptPointers; + TArray ScriptPointers; // Collect all arrays to pointers in scripted fields. for (auto field : Fields) @@ -877,28 +884,24 @@ void PClass::BuildArrayPointers() if (ScriptPointers.Size() == 0) { // No new pointers: Just use the same ArrayPointers as the parent. ArrayPointers = ParentClass->ArrayPointers; + ArrayPointersSize = ParentClass->ArrayPointersSize; } else - { // New pointers: Create a new FlatPointers array and add them. - int numSuperPointers; - - // Count pointers defined by superclasses. - for (numSuperPointers = 0; ParentClass->ArrayPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++) - { - } - + { // New pointers: Create a new ArrayPointers array and add them. // Concatenate them into a new array - size_t *flat = (size_t*)ClassDataAllocator.Alloc(sizeof(size_t) * (numSuperPointers + ScriptPointers.Size() + 1)); - if (numSuperPointers > 0) + pairType *flat = (pairType*)ClassDataAllocator.Alloc(sizeof(pairType) * (ParentClass->ArrayPointersSize + ScriptPointers.Size())); + if (ParentClass->ArrayPointersSize > 0) { - memcpy(flat, ParentClass->ArrayPointers, sizeof(size_t)*numSuperPointers); + memcpy(flat, ParentClass->ArrayPointers, sizeof(pairType) * ParentClass->ArrayPointersSize); } + if (ScriptPointers.Size() > 0) { - memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size()); + memcpy(flat + ParentClass->ArrayPointersSize, ScriptPointers.Data(), sizeof(pairType) * ScriptPointers.Size()); } - flat[numSuperPointers + ScriptPointers.Size()] = ~(size_t)0; + ArrayPointers = flat; + ArrayPointersSize = ParentClass->ArrayPointersSize + ScriptPointers.Size(); } } } @@ -911,21 +914,24 @@ void PClass::BuildArrayPointers() // //========================================================================== -void PClass::BuildMapPointers() +void PClass::BuildMapPointers() const { + using pairType = std::pair; + if (MapPointers != nullptr) { // Already built: Do nothing. return; } else if (ParentClass == nullptr) - { // No parent (i.e. DObject: FlatPointers is the same as Pointers. - MapPointers = &TheMapEnd; + { // No parent (i.e. DObject): Make MapPointers a harmless non-nullptr. + MapPointers = (pairType*)(&TheEnd); + MapPointersSize = 0; } else { ParentClass->BuildMapPointers(); - TArray> ScriptPointers; + TArray ScriptPointers; // Collect all arrays to pointers in scripted fields. for (auto field : Fields) @@ -939,28 +945,23 @@ void PClass::BuildMapPointers() if (ScriptPointers.Size() == 0) { // No new pointers: Just use the same ArrayPointers as the parent. MapPointers = ParentClass->MapPointers; + MapPointersSize = ParentClass->MapPointersSize; } else - { // New pointers: Create a new FlatPointers array and add them. - int numSuperPointers; - - // Count pointers defined by superclasses. - for (numSuperPointers = 0; ParentClass->MapPointers[numSuperPointers].first != ~(size_t)0; numSuperPointers++) - { - } - + { // New pointers: Create a new FlatPointers array and add them. // Concatenate them into a new array - std::pair *flat = (std::pair*)ClassDataAllocator.Alloc(sizeof(std::pair) * (numSuperPointers + ScriptPointers.Size() + 1)); - if (numSuperPointers > 0) + pairType *flat = (pairType*)ClassDataAllocator.Alloc(sizeof(pairType) * (ParentClass->MapPointersSize + ScriptPointers.Size())); + if (ParentClass->MapPointersSize > 0) { - memcpy(flat, ParentClass->MapPointers, sizeof(std::pair)*numSuperPointers); + memcpy(flat, ParentClass->MapPointers, sizeof(pairType) * ParentClass->MapPointersSize); } + if (ScriptPointers.Size() > 0) { - memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(std::pair) * ScriptPointers.Size()); + memcpy(flat + ParentClass->MapPointersSize, ScriptPointers.Data(), sizeof(pairType) * ScriptPointers.Size()); } - flat[numSuperPointers + ScriptPointers.Size()] = TheMapEnd; MapPointers = flat; + MapPointersSize = ParentClass->MapPointersSize + ScriptPointers.Size(); } } } diff --git a/src/common/objects/dobjtype.h b/src/common/objects/dobjtype.h index 7e698a6bf..62e6f5ccc 100644 --- a/src/common/objects/dobjtype.h +++ b/src/common/objects/dobjtype.h @@ -27,6 +27,9 @@ class PClassType; struct FNamespaceManager; class PSymbol; class PField; +class PObjectPointer; +class PDynArray; +class PMap; enum { @@ -53,10 +56,15 @@ public: // Per-class information ------------------------------------- PClass *ParentClass = nullptr; // the class this class derives from - const size_t *Pointers = nullptr; // object pointers defined by this class *only* - const size_t *FlatPointers = nullptr; // object pointers defined by this class and all its superclasses; not initialized by default - const size_t *ArrayPointers = nullptr; // dynamic arrays containing object pointers. - const std::pair *MapPointers = nullptr; // maps containing object pointers. + const size_t * Pointers = nullptr; // native object pointers defined by this class *only* + + mutable size_t FlatPointersSize = 0; + mutable const std::pair * FlatPointers = nullptr; // object pointers defined by this class and all its superclasses; not initialized by default. + mutable size_t ArrayPointersSize = 0; + mutable const std::pair * ArrayPointers = nullptr; // dynamic arrays containing object pointers. + mutable size_t MapPointersSize = 0; + mutable const std::pair * MapPointers = nullptr; // maps containing object pointers. + uint8_t *Defaults = nullptr; uint8_t *Meta = nullptr; // Per-class static script data unsigned Size = sizeof(DObject); @@ -86,9 +94,11 @@ public: PClass *CreateDerivedClass(FName name, unsigned int size, bool *newlycreated = nullptr, int fileno = 0); void InitializeActorInfo(); - void BuildFlatPointers(); - void BuildArrayPointers(); - void BuildMapPointers(); + + void BuildFlatPointers() const; + void BuildArrayPointers() const; + void BuildMapPointers() const; + void DestroySpecials(void *addr); void DestroyMeta(void *addr); const PClass *NativeClass() const; diff --git a/src/common/platform/posix/cocoa/i_system.mm b/src/common/platform/posix/cocoa/i_system.mm index 3338f54db..cef2d177b 100644 --- a/src/common/platform/posix/cocoa/i_system.mm +++ b/src/common/platform/posix/cocoa/i_system.mm @@ -32,6 +32,7 @@ */ #include "i_common.h" +#include "c_cvars.h" #include #include @@ -40,7 +41,7 @@ #include "st_console.h" #include "v_text.h" - +EXTERN_CVAR(Bool, longsavemessages) double PerfToSec, PerfToMillisec; void CalculateCPUSpeed() @@ -188,7 +189,8 @@ void I_OpenShellFolder(const char* folder) NSString *currentpath = [filemgr currentDirectoryPath]; [filemgr changeCurrentDirectoryPath:[NSString stringWithUTF8String:folder]]; - Printf("Opening folder: %s\n", folder); + if (longsavemessages) + Printf("Opening folder: %s\n", folder); std::system("open ."); [filemgr changeCurrentDirectoryPath:currentpath]; } diff --git a/src/common/platform/posix/sdl/i_system.cpp b/src/common/platform/posix/sdl/i_system.cpp index c6d3df291..7fa41b2bc 100644 --- a/src/common/platform/posix/sdl/i_system.cpp +++ b/src/common/platform/posix/sdl/i_system.cpp @@ -77,6 +77,7 @@ int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad double PerfToSec, PerfToMillisec; CVAR(Bool, con_printansi, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE); CVAR(Bool, con_4bitansi, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE); +EXTERN_CVAR(Bool, longsavemessages) extern FStartupScreen *StartWindow; @@ -372,13 +373,17 @@ void I_OpenShellFolder(const char* infolder) if (!chdir(infolder)) { - Printf("Opening folder: %s\n", infolder); + if (longsavemessages) + Printf("Opening folder: %s\n", infolder); std::system("xdg-open ."); chdir(curdir); } else { - Printf("Unable to open directory '%s\n", infolder); + if (longsavemessages) + Printf("Unable to open directory '%s\n", infolder); + else + Printf("Unable to open requested directory\n"); } free(curdir); } diff --git a/src/common/platform/win32/i_input.cpp b/src/common/platform/win32/i_input.cpp index 054c99590..cb945d61e 100644 --- a/src/common/platform/win32/i_input.cpp +++ b/src/common/platform/win32/i_input.cpp @@ -110,6 +110,8 @@ EXTERN_CVAR (Bool, use_mouse) static int WheelDelta; extern bool CursorState; +void SetCursorState(bool visible); + extern BOOL paused; static bool noidle = false; @@ -415,7 +417,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_SETCURSOR: if (!CursorState) { - SetCursor(NULL); // turn off window cursor + SetCursorState(false); // turn off window cursor return TRUE; // Prevent Windows from setting cursor to window class cursor } else diff --git a/src/common/platform/win32/i_mouse.cpp b/src/common/platform/win32/i_mouse.cpp index 37ee35709..72dc3f335 100644 --- a/src/common/platform/win32/i_mouse.cpp +++ b/src/common/platform/win32/i_mouse.cpp @@ -131,7 +131,7 @@ enum EMouseMode // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- -static void SetCursorState(bool visible); +void SetCursorState(bool visible); static FMouse *CreateWin32Mouse(); static FMouse *CreateDInputMouse(); static FMouse *CreateRawMouse(); @@ -191,7 +191,7 @@ CUSTOM_CVAR (Int, in_mouse, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) static bool mouse_shown = true; -static void SetCursorState(bool visible) +void SetCursorState(bool visible) { CursorState = visible || !m_hidepointer; if (GetForegroundWindow() == mainwindow.GetHandle()) @@ -297,6 +297,12 @@ void I_CheckNativeMouse(bool preferNative, bool eventhandlerresult) { BlockMouseMove = 3; Mouse->Ungrab(); + + if(!mouse_shown) + { + ShowCursor(true); + mouse_shown = true; + } } else { diff --git a/src/common/platform/win32/i_system.cpp b/src/common/platform/win32/i_system.cpp index f23b0e958..0b8ea53ac 100644 --- a/src/common/platform/win32/i_system.cpp +++ b/src/common/platform/win32/i_system.cpp @@ -113,6 +113,7 @@ static HCURSOR CreateBitmapCursor(int xhot, int yhot, HBITMAP and_mask, HBITMAP EXTERN_CVAR (Bool, queryiwad); // Used on welcome/IWAD screen. +EXTERN_CVAR(Bool, longsavemessages) extern HANDLE StdOut; extern bool FancyStdOut; @@ -823,13 +824,17 @@ void I_OpenShellFolder(const char* infolder) } else if (SetCurrentDirectoryW(WideString(infolder).c_str())) { - Printf("Opening folder: %s\n", infolder); + if (longsavemessages) + Printf("Opening folder: %s\n", infolder); ShellExecuteW(NULL, L"open", L"explorer.exe", L".", NULL, SW_SHOWNORMAL); SetCurrentDirectoryW(curdir.Data()); } else { - Printf("Unable to open directory '%s\n", infolder); + if (longsavemessages) + Printf("Unable to open directory '%s\n", infolder); + else + Printf("Unable to open requested directory\n"); } } diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 0504fc160..28f27f850 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -9542,6 +9542,8 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) auto &argflags = Function->Variants[0].ArgFlags; auto *defaults = FnPtrCall ? nullptr : &Function->Variants[0].Implementation->DefaultArgs; + if(FnPtrCall) static_cast(ctx.Function->Variants[0].Implementation)->blockJit = true; + int implicit = Function->GetImplicitArgs(); if (!CheckAccessibility(ctx.Version)) diff --git a/src/common/scripting/core/types.cpp b/src/common/scripting/core/types.cpp index 0873125f8..8e13dcc52 100644 --- a/src/common/scripting/core/types.cpp +++ b/src/common/scripting/core/types.cpp @@ -196,15 +196,15 @@ void PType::SetDefaultValue(void *base, unsigned offset, TArray // //========================================================================== -void PType::SetPointer(void *base, unsigned offset, TArray *stroffs) +void PType::SetPointer(void *base, unsigned offset, TArray> *stroffs) { } -void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) +void PType::SetPointerArray(void *base, unsigned offset, TArray> *stroffs) { } -void PType::SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) +void PType::SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) { } @@ -1575,10 +1575,10 @@ PObjectPointer::PObjectPointer(PClass *cls, bool isconst) // //========================================================================== -void PObjectPointer::SetPointer(void *base, unsigned offset, TArray *special) +void PObjectPointer::SetPointer(void *base, unsigned offset, TArray> *special) { // Add to the list of pointers for this class. - special->Push(offset); + special->Push({offset, this}); } //========================================================================== @@ -1706,7 +1706,7 @@ bool PClassPointer::isCompatible(PType *type) // //========================================================================== -void PClassPointer::SetPointer(void *base, unsigned offset, TArray *special) +void PClassPointer::SetPointer(void *base, unsigned offset, TArray> *special) { } @@ -1908,7 +1908,7 @@ void PArray::SetDefaultValue(void *base, unsigned offset, TArray // //========================================================================== -void PArray::SetPointer(void *base, unsigned offset, TArray *special) +void PArray::SetPointer(void *base, unsigned offset, TArray> *special) { for (unsigned i = 0; i < ElementCount; ++i) { @@ -1922,7 +1922,7 @@ void PArray::SetPointer(void *base, unsigned offset, TArray *special) // //========================================================================== -void PArray::SetPointerArray(void *base, unsigned offset, TArray *special) +void PArray::SetPointerArray(void *base, unsigned offset, TArray> *special) { if (ElementType->isStruct() || ElementType->isDynArray()) { @@ -1939,7 +1939,7 @@ void PArray::SetPointerArray(void *base, unsigned offset, TArray *specia // //========================================================================== -void PArray::SetPointerMap(void *base, unsigned offset, TArray> *special) +void PArray::SetPointerMap(void *base, unsigned offset, TArray> *special) { if(ElementType->isStruct() || ElementType->isMap()) { @@ -2254,12 +2254,12 @@ void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray *special) +void PDynArray::SetPointerArray(void *base, unsigned offset, TArray> *special) { if (ElementType->isObjectPointer()) { // Add to the list of pointer arrays for this class. - special->Push(offset); + special->Push({offset, this}); } } @@ -2524,12 +2524,12 @@ void PMap::SetDefaultValue(void *base, unsigned offset, TArray * // //========================================================================== -void PMap::SetPointerMap(void *base, unsigned offset, TArray> *special) +void PMap::SetPointerMap(void *base, unsigned offset, TArray> *special) { if (ValueType->isObjectPointer()) { // Add to the list of pointer arrays for this class. - special->Push(std::make_pair(offset,KeyType)); + special->Push(std::make_pair(offset, this)); } } @@ -3264,7 +3264,7 @@ void PStruct::SetDefaultValue(void *base, unsigned offset, TArray *special) +void PStruct::SetPointer(void *base, unsigned offset, TArray> *special) { auto it = Symbols.GetIterator(); PSymbolTable::MapType::Pair *pair; @@ -3284,7 +3284,7 @@ void PStruct::SetPointer(void *base, unsigned offset, TArray *special) // //========================================================================== -void PStruct::SetPointerArray(void *base, unsigned offset, TArray *special) +void PStruct::SetPointerArray(void *base, unsigned offset, TArray> *special) { auto it = Symbols.GetIterator(); PSymbolTable::MapType::Pair *pair; @@ -3304,7 +3304,7 @@ void PStruct::SetPointerArray(void *base, unsigned offset, TArray *speci // //========================================================================== -void PStruct::SetPointerMap(void *base, unsigned offset, TArray> *special) +void PStruct::SetPointerMap(void *base, unsigned offset, TArray> *special) { auto it = Symbols.GetIterator(); PSymbolTable::MapType::Pair *pair; diff --git a/src/common/scripting/core/types.h b/src/common/scripting/core/types.h index e892a8b12..a092247de 100644 --- a/src/common/scripting/core/types.h +++ b/src/common/scripting/core/types.h @@ -68,8 +68,11 @@ class PPointer; class PClassPointer; class PFunctionPointer; class PArray; +class PDynArray; +class PMap; class PStruct; class PClassType; +class PObjectPointer; struct ZCC_ExprConstant; class PType : public PTypeBase @@ -129,9 +132,9 @@ public: // initialization when the object is created and destruction when the // object is destroyed. virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL); - virtual void SetPointer(void *base, unsigned offset, TArray *ptrofs = NULL); - virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL); - virtual void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL); + virtual void SetPointer(void *base, unsigned offset, TArray> *ptrofs = NULL); + virtual void SetPointerArray(void *base, unsigned offset, TArray> *ptrofs = NULL); + virtual void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL); // Initialize the value, if needed (e.g. strings) virtual void InitializeValue(void *addr, const void *def) const; @@ -455,7 +458,7 @@ public: void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; - void SetPointer(void *base, unsigned offset, TArray *special = NULL) override; + void SetPointer(void *base, unsigned offset, TArray> *special = NULL) override; PClass *PointedClass() const; }; @@ -471,7 +474,7 @@ public: void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; - void SetPointer(void *base, unsigned offset, TArray *special = NULL) override; + void SetPointer(void *base, unsigned offset, TArray> *special = NULL) override; bool IsMatch(intptr_t id1, intptr_t id2) const override; void GetTypeIDs(intptr_t &id1, intptr_t &id2) const override; }; @@ -503,9 +506,9 @@ public: bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; void SetDefaultValue(void *base, unsigned offset, TArray *special) override; - void SetPointer(void *base, unsigned offset, TArray *special) override; - void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) override; - void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL) override; + void SetPointer(void *base, unsigned offset, TArray> *special) override; + void SetPointerArray(void *base, unsigned offset, TArray> *ptrofs = NULL) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL) override; }; class PStaticArray : public PArray @@ -535,7 +538,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; void InitializeValue(void *addr, const void *def) const override; void DestroyValue(void *addr) const override; - void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) override; + void SetPointerArray(void *base, unsigned offset, TArray> *ptrofs = NULL) override; }; class PMap : public PCompoundType @@ -579,7 +582,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; void InitializeValue(void *addr, const void *def) const override; void DestroyValue(void *addr) const override; - void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; }; @@ -653,9 +656,9 @@ public: void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; - void SetPointer(void *base, unsigned offset, TArray *specials) override; - void SetPointerArray(void *base, unsigned offset, TArray *special) override; - void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; + void SetPointer(void *base, unsigned offset, TArray> *specials) override; + void SetPointerArray(void *base, unsigned offset, TArray> *special) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; }; class PPrototype : public PCompoundType diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index 14a0fd5fa..d109717a3 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -43,6 +43,11 @@ FSharedStringArena VMStringConstants; +static bool ShouldWrapPointer(PType * type) +{ + return ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray() || type->isMap() || type->isMapIterator()); +} + int GetIntConst(FxExpression *ex, FCompileContext &ctx) { ex = new FxIntCast(ex, false); @@ -2049,8 +2054,17 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n } while( (t = (ZCC_Type *)t->SiblingNext) != fn->RetType); if(auto *t = fn->Params; t != nullptr) do { - args.Push(DetermineType(outertype, field, name, t->Type, false, false)); - argflags.Push(t->Flags == ZCC_Out ? VARF_Out : 0); + PType * tt = DetermineType(outertype, field, name, t->Type, false, false); + int flags = 0; + + if (ShouldWrapPointer(tt)) + { + tt = NewPointer(tt); + flags = VARF_Ref; + } + + args.Push(tt); + argflags.Push(t->Flags == ZCC_Out ? VARF_Out|flags : flags); } while( (t = (ZCC_FuncPtrParamDecl *) t->SiblingNext) != fn->Params); auto proto = NewPrototype(returns,args); @@ -2550,7 +2564,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); int flags = 0; - if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray() || type->isMap() || type->isMapIterator()) + if (ShouldWrapPointer(type)) { // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly. type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); diff --git a/src/common/scripting/vm/vmframe.cpp b/src/common/scripting/vm/vmframe.cpp index 635bace63..f69b8ea61 100644 --- a/src/common/scripting/vm/vmframe.cpp +++ b/src/common/scripting/vm/vmframe.cpp @@ -279,6 +279,8 @@ static bool CanJit(VMScriptFunction *func) // Asmjit has a 256 register limit. Stay safely away from it as the jit compiler uses a few for temporaries as well. // Any function exceeding the limit will use the VM - a fair punishment to someone for writing a function so bloated ;) + if(func->blockJit) return false; + int maxregs = 200; if (func->NumRegA + func->NumRegD + func->NumRegF + func->NumRegS < maxregs) return true; diff --git a/src/common/scripting/vm/vmintern.h b/src/common/scripting/vm/vmintern.h index 44fb53463..b35eefc1e 100644 --- a/src/common/scripting/vm/vmintern.h +++ b/src/common/scripting/vm/vmintern.h @@ -474,6 +474,8 @@ public: VM_UBYTE NumArgs; // Number of arguments this function takes TArray SpecialInits; // list of all contents on the extra stack which require construction and destruction + bool blockJit = false; // function triggers Jit bugs, block compilation until bugs are fixed + void InitExtra(void *addr); void DestroyExtra(void *addr); int AllocExtraStack(PType *type); diff --git a/src/common/startscreen/startscreen.cpp b/src/common/startscreen/startscreen.cpp index abfac343f..1c38766f1 100644 --- a/src/common/startscreen/startscreen.cpp +++ b/src/common/startscreen/startscreen.cpp @@ -654,13 +654,11 @@ void FStartScreen::NetProgress(int count) void FStartScreen::Render(bool force) { + static uint64_t minwaittime = 30; + auto nowtime = I_msTime(); // Do not refresh too often. This function gets called a lot more frequently than the screen can update. -#ifdef _DEBUG - if (nowtime - screen->FrameTime > 3000 || force) -#else - if (nowtime - screen->FrameTime > 30 || force) -#endif + if (nowtime - screen->FrameTime > minwaittime || force) { screen->FrameTime = nowtime; screen->FrameTimeNS = I_nsTime(); @@ -694,6 +692,9 @@ void FStartScreen::Render(bool force) screen->Update(); twod->OnFrameDone(); } + auto newtime = I_msTime(); + if ((newtime - nowtime) * 2.0 > minwaittime) // slow down drawing the start screen if we're on a slow GPU! + minwaittime = (newtime - nowtime) * 2.0; } FImageSource* CreateStartScreenTexture(FBitmap& srcdata); diff --git a/src/common/utility/tarray.h b/src/common/utility/tarray.h index b642f3c87..bce3ba3ea 100644 --- a/src/common/utility/tarray.h +++ b/src/common/utility/tarray.h @@ -55,6 +55,7 @@ #include #include #include +#include #if !defined(_WIN32) #include // for intptr_t @@ -366,11 +367,11 @@ public: } template - bool IsSorted(Func lt) + bool IsSorted(Func &<) { for(unsigned i = 1; i < Count; i++) { - if(lt(Array[i], Array[i-1])) return false; + if(std::invoke(lt, Array[i], Array[i-1])) return false; } return true; } @@ -418,7 +419,7 @@ public: // // exact = false returns the closest match, to be used for, ex., insertions, exact = true returns Size() when no match, like Find does template - unsigned int SortedFind(const T& item, Func lt, bool exact = true) const + unsigned int SortedFind(const T& item, Func &<, bool exact = true) const { if(Count == 0) return 0; if(Count == 1) return lt(item, Array[0]) ? 0 : 1; @@ -430,11 +431,11 @@ public: { int mid = lo + ((hi - lo) / 2); - if(lt(Array[mid], item)) + if(std::invoke(lt, Array[mid], item)) { lo = mid + 1; } - else if(lt(item, Array[mid])) + else if(std::invoke(lt, item, Array[mid])) { if(mid == 0) break; // prevent negative overflow due to unsigned numbers hi = mid - 1; @@ -450,7 +451,7 @@ public: } else { - return (lo == Count || lt(item, Array[lo])) ? lo : lo + 1; + return (lo == Count || std::invoke(lt, item, Array[lo])) ? lo : lo + 1; } } @@ -466,12 +467,24 @@ public: } template - unsigned int FindEx(Func compare) const + bool Contains(const T& item, Func &&compare) const + { + unsigned int i; + for(i = 0;i < Count;++i) + { + if(std::invoke(compare, Array[i], item)) + return true; + } + return false; + } + + template + unsigned int FindEx(Func &&compare) const { unsigned int i; for (i = 0; i < Count; ++i) { - if (compare(Array[i])) + if (std::invoke(compare, Array[i])) break; } return i; @@ -569,9 +582,9 @@ public: } template - unsigned SortedAddUnique(const T& obj, Func lt) + unsigned SortedAddUnique(const T& obj, Func &<) { - auto f = SortedFind(obj, lt, true); + auto f = SortedFind(obj, std::forward(lt), true); if (f == Size()) Push(obj); return f; } @@ -591,9 +604,9 @@ public: } template - bool SortedDelete(const T& obj, Func lt) + bool SortedDelete(const T& obj, Func &<) { - auto f = SortedFind(obj, lt, true); + auto f = SortedFind(obj, std::forward(lt), true); if (f == Size()) { Delete(f); @@ -691,9 +704,9 @@ public: } template - void SortedInsert (const T &item, Func lt) + void SortedInsert (const T &item, Func &<) { - Insert (SortedFind (item, lt, false), item); + Insert (SortedFind (item, std::forward(lt), false), item); } void ShrinkToFit () diff --git a/src/d_main.cpp b/src/d_main.cpp index 52cb16c92..a39d27738 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -579,6 +579,7 @@ CVAR(Flag, sv_localitems, dmflags3, DF3_LOCAL_ITEMS); CVAR(Flag, sv_nolocaldrops, dmflags3, DF3_NO_LOCAL_DROPS); CVAR(Flag, sv_nocoopitems, dmflags3, DF3_NO_COOP_ONLY_ITEMS); CVAR(Flag, sv_nocoopthings, dmflags3, DF3_NO_COOP_ONLY_THINGS); +CVAR(Flag, sv_rememberlastweapon, dmflags3, DF3_REMEMBER_LAST_WEAP); //========================================================================== // diff --git a/src/doomdef.h b/src/doomdef.h index 668059909..70759a406 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -185,6 +185,7 @@ enum : unsigned DF3_NO_LOCAL_DROPS = 1 << 3, // Drops from Actors aren't picked up locally DF3_NO_COOP_ONLY_ITEMS = 1 << 4, // Items that only appear in co-op are disabled DF3_NO_COOP_ONLY_THINGS = 1 << 5, // Any Actor that only appears in co-op is disabled + DF3_REMEMBER_LAST_WEAP = 1 << 6, // When respawning in co-op, keep the last used weapon out instead of switching to the best new one. }; // [RH] Compatibility flags. diff --git a/src/g_game.cpp b/src/g_game.cpp index 4596212c0..ab3759478 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1606,10 +1606,20 @@ void FLevelLocals::DeathMatchSpawnPlayer (int playernum) if (selections < 1) I_Error ("No deathmatch starts"); + bool hasSpawned = false; + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (PlayerInGame(i) && Players[i]->mo != nullptr && Players[i]->health > 0) + { + hasSpawned = true; + break; + } + } + // At level start, none of the players have mobjs attached to them, // so we always use the random deathmatch spawn. During the game, // though, we use whatever dmflags specifies. - if ((dmflags & DF_SPAWN_FARTHEST) && players[playernum].mo) + if ((dmflags & DF_SPAWN_FARTHEST) && hasSpawned) spot = SelectFarthestDeathmatchSpot (selections); else spot = SelectRandomDeathmatchSpot (playernum, selections); @@ -2148,14 +2158,6 @@ void G_DoLoadGame () BackupSaveName = savename; - //Push any added models from A_ChangeModel - for (auto& smf : savedModelFiles) - { - FString modelFilePath = smf.Left(smf.LastIndexOf("/")+1); - FString modelFileName = smf.Right(smf.Len() - smf.Left(smf.LastIndexOf("/") + 1).Len()); - FindModel(modelFilePath.GetChars(), modelFileName.GetChars()); - } - // At this point, the GC threshold is likely a lot higher than the // amount of memory in use, so bring it down now by starting a // collection. diff --git a/src/g_level.cpp b/src/g_level.cpp index f4452bfbd..fa7ac53ae 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1715,7 +1715,7 @@ int FLevelLocals::FinishTravel () pawn->flags2 &= ~MF2_BLASTED; if (oldpawn != nullptr) { - PlayerPointerSubstitution (oldpawn, pawn); + PlayerPointerSubstitution (oldpawn, pawn, true); oldpawn->Destroy(); } if (pawndup != NULL) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index d05140ee8..6c72d3a52 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -1014,8 +1014,7 @@ void FLevelLocals::Serialize(FSerializer &arc, bool hubload) ("scrolls", Scrolls) ("automap", automap) ("interpolator", interpolator) - ("frozenstate", frozenstate) - ("savedModelFiles", savedModelFiles); + ("frozenstate", frozenstate); // Hub transitions must keep the current total time diff --git a/src/p_states.cpp b/src/p_states.cpp index da8ef7f96..841bb4925 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -506,8 +506,10 @@ int FStateDefinitions::GetStateLabelIndex (FName statename) { return -1; } - assert((size_t)std->State <= StateArray.Size() + 1); - return (int)((ptrdiff_t)std->State - 1); + if ((size_t)std->State <= StateArray.Size() + 1) + return (int)((ptrdiff_t)std->State - 1); + else + return -1; } //========================================================================== diff --git a/src/playsim/a_sharedglobal.h b/src/playsim/a_sharedglobal.h index ecaa74b52..4e4a9403e 100644 --- a/src/playsim/a_sharedglobal.h +++ b/src/playsim/a_sharedglobal.h @@ -139,8 +139,8 @@ class DEarthquake : public DThinker public: static const int DEFAULT_STAT = STAT_EARTHQUAKE; void Construct(AActor *center, double intensityX, double intensityY, double intensityZ, int duration, - int damrad, int tremrad, FSoundID quakesfx, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, double rollIntensity, double rollWave, double damageMultiplier, double thrustMultiplier, int damage); + double damrad, double tremrad, FSoundID quakesfx, int flags, + double waveSpeedX, double waveSpeedY, double waveSpeedZ, double falloff, int highpoint, double rollIntensity, double rollWave, double damageMultiplier, double thrustMultiplier, int damage); void Serialize(FSerializer &arc); void Tick (); diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 14e5ee906..4cb0cd60b 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -709,7 +709,7 @@ struct AnimOverride double startFrame; int flags = ANIMOVERRIDE_NONE; float framerate; - double startTic; // when the animation starts if interpolating from previous animation + double startTic; // when the current animation started (changing framerates counts as restarting) (or when animation starts if interpolating from previous animation) double switchTic; // when the animation was changed -- where to interpolate the switch from }; @@ -719,6 +719,16 @@ struct ModelOverride TArray surfaceSkinIDs; }; +struct AnimModelOverride +{ + int id; + + AnimModelOverride() = default; + + AnimModelOverride(int i) : id(i) {} + operator int() { return id; } +}; + enum EModelDataFlags { MODELDATA_HADMODEL = 1 << 0, @@ -729,14 +739,14 @@ class DActorModelData : public DObject { DECLARE_CLASS(DActorModelData, DObject); public: - PClass * modelDef; - TArray models; - TArray skinIDs; - TArray animationIDs; - TArray modelFrameGenerators; - int flags; - int overrideFlagsSet; - int overrideFlagsClear; + PClass * modelDef; + TArray models; + TArray skinIDs; + TArray animationIDs; + TArray modelFrameGenerators; + int flags; + int overrideFlagsSet; + int overrideFlagsClear; AnimOverride curAnim; AnimOverride prevAnim; // used for interpolation when switching anims @@ -1745,7 +1755,7 @@ struct FTranslatedLineTarget bool unlinked; // found by a trace that went through an unlinked portal. }; -void PlayerPointerSubstitution(AActor* oldPlayer, AActor* newPlayer); +void PlayerPointerSubstitution(AActor* oldPlayer, AActor* newPlayer, bool removeOld); int MorphPointerSubstitution(AActor* from, AActor* to); #define S_FREETARGMOBJ 1 diff --git a/src/playsim/d_player.h b/src/playsim/d_player.h index 49d8d54a1..34ac55da6 100644 --- a/src/playsim/d_player.h +++ b/src/playsim/d_player.h @@ -125,6 +125,7 @@ typedef enum CF_INTERPVIEWANGLES = 1 << 15, // [MR] flag for interpolating view angles without interpolating the entire frame CF_NOFOVINTERP = 1 << 16, // [B] Disable FOV interpolation when instantly zooming CF_SCALEDNOLERP = 1 << 17, // [MR] flag for applying angles changes in the ticrate without interpolating the frame + CF_NOVIEWPOSINTERP = 1 << 18, // Disable view position interpolation. CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths. CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either. CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either. diff --git a/src/playsim/fragglescript/t_script.cpp b/src/playsim/fragglescript/t_script.cpp index 2eb3ef229..7af1424ab 100644 --- a/src/playsim/fragglescript/t_script.cpp +++ b/src/playsim/fragglescript/t_script.cpp @@ -549,9 +549,9 @@ size_t DFraggleThinker::PropagateMark() // //========================================================================== -size_t DFraggleThinker::PointerSubstitution (DObject *old, DObject *notOld) +size_t DFraggleThinker::PointerSubstitution (DObject *old, DObject *notOld, bool nullOnFail) { - size_t changed = Super::PointerSubstitution(old, notOld); + size_t changed = Super::PointerSubstitution(old, notOld, nullOnFail); for(unsigned i=0;i(old)) diff --git a/src/playsim/fragglescript/t_script.h b/src/playsim/fragglescript/t_script.h index 767778628..2dc79a95e 100644 --- a/src/playsim/fragglescript/t_script.h +++ b/src/playsim/fragglescript/t_script.h @@ -701,7 +701,7 @@ public: void Tick(); void InitFunctions(); size_t PropagateMark(); - size_t PointerSubstitution (DObject *old, DObject *notOld); + size_t PointerSubstitution (DObject *old, DObject *notOld, bool nullOnFail); bool wait_finished(DRunningScript *script); void AddRunningScript(DRunningScript *runscr); diff --git a/src/playsim/mapthinkers/a_quake.cpp b/src/playsim/mapthinkers/a_quake.cpp index aae1061bf..b21951562 100644 --- a/src/playsim/mapthinkers/a_quake.cpp +++ b/src/playsim/mapthinkers/a_quake.cpp @@ -51,8 +51,8 @@ IMPLEMENT_POINTERS_END //========================================================================== void DEarthquake::Construct(AActor *center, double intensityX, double intensityY, double intensityZ, int duration, - int damrad, int tremrad, FSoundID quakesound, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, + double damrad, double tremrad, FSoundID quakesound, int flags, + double waveSpeedX, double waveSpeedY, double waveSpeedZ, double falloff, int highpoint, double rollIntensity, double rollWave, double damageMultiplier, double thrustMultiplier, int damage) { m_QuakeSFX = quakesound; @@ -185,7 +185,7 @@ void DEarthquake::DoQuakeDamage(DEarthquake *quake, AActor *victim, bool falloff if (!quake || !victim) return; - dist = quake->m_Spot->Distance2D(victim, true); + dist = quake->m_Spot->Distance2D(victim); thrustfalloff = falloff ? GetFalloff(dist, m_DamageRadius) : 1.0; // Check if in damage radius if (dist < m_DamageRadius && victim->Z() <= victim->floorz) @@ -355,8 +355,8 @@ int DEarthquake::StaticGetQuakeIntensities(double ticFrac, AActor *victim, FQuak { double dist; - if (quake->m_Flags & QF_3D) dist = quake->m_Spot->Distance3D(victim, true); - else dist = quake->m_Spot->Distance2D(victim, true); + if (quake->m_Flags & QF_3D) dist = quake->m_Spot->Distance3D(victim); + else dist = quake->m_Spot->Distance2D(victim); if (dist < quake->m_TremorRadius) { @@ -429,8 +429,8 @@ int DEarthquake::StaticGetQuakeIntensities(double ticFrac, AActor *victim, FQuak //========================================================================== bool P_StartQuakeXYZ(FLevelLocals *Level, AActor *activator, int tid, double intensityX, double intensityY, double intensityZ, int duration, - int damrad, int tremrad, FSoundID quakesfx, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, + double damrad, double tremrad, FSoundID quakesfx, int flags, + double waveSpeedX, double waveSpeedY, double waveSpeedZ, double falloff, int highpoint, double rollIntensity, double rollWave, double damageMultiplier, double thrustMultiplier, int damage) { AActor *center; @@ -463,7 +463,7 @@ bool P_StartQuakeXYZ(FLevelLocals *Level, AActor *activator, int tid, double int return res; } -bool P_StartQuake(FLevelLocals * Level, AActor * activator, int tid, double intensity, int duration, int damrad, int tremrad, FSoundID quakesfx) +bool P_StartQuake(FLevelLocals * Level, AActor * activator, int tid, double intensity, int duration, double damrad, double tremrad, FSoundID quakesfx) { //Maintains original behavior by passing 0 to intensityZ, flags, and everything else after QSFX. return P_StartQuakeXYZ(Level, activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0.5, 0); } diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 410ac6a53..3c48725bb 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -3322,8 +3322,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_Quake) PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT (intensity); PARAM_INT (duration); - PARAM_INT (damrad); - PARAM_INT (tremrad); + PARAM_FLOAT (damrad); + PARAM_FLOAT (tremrad); PARAM_SOUND (sound); P_StartQuake(self->Level, self, 0, intensity, duration, damrad, tremrad, sound); @@ -3345,14 +3345,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_QuakeEx) PARAM_FLOAT(intensityY); PARAM_FLOAT(intensityZ); PARAM_INT(duration); - PARAM_INT(damrad); - PARAM_INT(tremrad); + PARAM_FLOAT(damrad); + PARAM_FLOAT(tremrad); PARAM_SOUND(sound); PARAM_INT(flags); PARAM_FLOAT(mulWaveX); PARAM_FLOAT(mulWaveY); PARAM_FLOAT(mulWaveZ); - PARAM_INT(falloff); + PARAM_FLOAT(falloff); PARAM_INT(highpoint); PARAM_FLOAT(rollIntensity); PARAM_FLOAT(rollWave); @@ -5125,11 +5125,10 @@ enum ESetAnimationFlags { SAF_INSTANT = 1 << 0, SAF_LOOP = 1 << 1, - SAF_USEACTORROLL = 1 << 2, - SAF_USEACTORPITCH = 1 << 3, + SAF_NOOVERRIDE = 1 << 2, }; -void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int interpolateTics, int flags, double ticFrac) +void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags, double ticFrac) { if(!self) ThrowAbortException(X_READ_NIL, "In function parameter self"); @@ -5138,6 +5137,11 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s ThrowAbortException(X_OTHER, "Cannot set animation for non-decoupled actors"); } + if(!BaseSpriteModelFrames.CheckKey(self->GetClass())) + { + ThrowAbortException(X_OTHER, "Actor class is missing a MODELDEF definition or a MODELDEF BaseFrame"); + } + if(interpolateTics <= 0) interpolateTics = 1; EnsureModelData(self); @@ -5168,6 +5172,13 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s Printf("Could not find animation %s\n", animName.GetChars()); return; } + + if((flags & SAF_NOOVERRIDE) && self->modelData->curAnim.flags != ANIMOVERRIDE_NONE && self->modelData->curAnim.firstFrame == animStart) + { + //same animation as current, skip setting it + return; + } + int animEnd = mdl->FindLastFrame(animName); if(framerate < 0) @@ -5180,18 +5191,24 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s if(startFrame >= len) { self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; - Printf("frame %d is past the end of animation %s\n", startFrame, animName.GetChars()); + Printf("frame %d (startFrame) is past the end of animation %s\n", startFrame, animName.GetChars()); return; } else if(loopFrame >= len) { self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; - Printf("frame %d is past the end of animation %s\n", startFrame, animName.GetChars()); + Printf("frame %d (loopFrame) is past the end of animation %s\n", startFrame, animName.GetChars()); + return; + } + else if(endFrame >= len) + { + self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; + Printf("frame %d (endFrame) is past the end of animation %s\n", endFrame, animName.GetChars()); return; } self->modelData->curAnim.firstFrame = animStart; - self->modelData->curAnim.lastFrame = animEnd - 1; + self->modelData->curAnim.lastFrame = endFrame < 0 ? animEnd - 1 : animStart + endFrame; self->modelData->curAnim.startFrame = startFrame < 0 ? animStart : animStart + startFrame; self->modelData->curAnim.loopFrame = loopFrame < 0 ? animStart : animStart + loopFrame; self->modelData->curAnim.flags = (flags&SAF_LOOP) ? ANIMOVERRIDE_LOOP : 0; @@ -5208,14 +5225,14 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s } } -void SetAnimationNative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int interpolateTics, int flags) +void SetAnimationNative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags) { - SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, interpolateTics, flags, 1); + SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, 1); } -void SetAnimationUINative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int interpolateTics, int flags) +void SetAnimationUINative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags) { - SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, interpolateTics, flags, I_GetTimeFrac()); + SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, I_GetTimeFrac()); } extern double getCurrentFrame(const AnimOverride &anim, double tic); @@ -5438,57 +5455,6 @@ void ChangeModelNative( } } - //[SM] - We need to serialize file paths and model names so that they are pushed on loading save files. Likewise, let's not include models that were already parsed when initialized. - if (queryModel >= 0) - { - FString fullName; - fullName.Format("%s%s", modelpath.GetChars(), model.GetChars()); - bool found = false; - - for (auto &m : savedModelFiles) - { - if(m.CompareNoCase(fullName) == 0) - { - found = true; - break; - } - } - if(!found) for (auto &m : Models) - { - if (m->mFileName.CompareNoCase(fullName) == 0) - { - found = true; - break; - } - } - if(!found) savedModelFiles.Push(fullName); - } - //Same for animations - if (queryAnimation >= 0) - { - FString fullName; - fullName.Format("%s%s", animationpath.GetChars(), animation.GetChars()); - bool found = false; - - for (auto &m : savedModelFiles) - { - if(m.CompareNoCase(fullName) == 0) - { - found = true; - break; - } - } - if(!found) for (auto &m : Models) - { - if (m->mFileName.CompareNoCase(fullName) == 0) - { - found = true; - break; - } - } - if(!found) savedModelFiles.Push(fullName); - } - CleanupModelData(mobj); return; @@ -5522,10 +5488,11 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative) PARAM_FLOAT(framerate); PARAM_INT(startFrame); PARAM_INT(loopFrame); + PARAM_INT(endFrame); PARAM_INT(interpolateTics); PARAM_INT(flags); - SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, interpolateTics, flags, 1); + SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, 1); return 0; } @@ -5537,10 +5504,11 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative) PARAM_FLOAT(framerate); PARAM_INT(startFrame); PARAM_INT(loopFrame); + PARAM_INT(endFrame); PARAM_INT(interpolateTics); PARAM_INT(flags); - SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, interpolateTics, flags, I_GetTimeFrac()); + SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, I_GetTimeFrac()); return 0; } diff --git a/src/playsim/p_destructible.cpp b/src/playsim/p_destructible.cpp index a40970c76..8e8b94f18 100644 --- a/src/playsim/p_destructible.cpp +++ b/src/playsim/p_destructible.cpp @@ -448,11 +448,11 @@ static void PGRA_InsertIfCloser(TMap& damageGroupPos, int grou EXTERN_CVAR(Float, splashfactor); -void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance) +void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, double bombdistance, FName damagetype, double fulldamagedistance) { TMap damageGroupPos; - double bombdistancefloat = 1. / (double)(bombdistance - fulldamagedistance); + double bombdistancefloat = 1.0 / (bombdistance - fulldamagedistance); // now, this is not entirely correct... but sector actions still _do_ require a valid source actor to trigger anything if (!bombspot) @@ -588,8 +588,8 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage int damage = 0; if (dst < bombdistance) { - dst = clamp(dst - (double)fulldamagedistance, 0, dst); - damage = (int)((double)bombdamage * (1. - dst * bombdistancefloat)); + dst = clamp(dst - fulldamagedistance, 0.0, dst); + damage = (int)((double)bombdamage * (1.0 - dst * bombdistancefloat)); if (bombsource == bombspot) damage = (int)(damage * splashfactor); } @@ -661,8 +661,8 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage int damage = 0; if (dst < bombdistance) { - dst = clamp(dst - (double)fulldamagedistance, 0, dst); - damage = (int)((double)bombdamage * (1. - dst * bombdistancefloat)); + dst = clamp(dst - fulldamagedistance, 0.0, dst); + damage = (int)((double)bombdamage * (1.0 - dst * bombdistancefloat)); if (bombsource == bombspot) damage = (int)(damage * splashfactor); } @@ -950,9 +950,9 @@ DEFINE_ACTION_FUNCTION(FDestructible, GeometryRadiusAttack) PARAM_OBJECT(bombspot, AActor); PARAM_OBJECT(bombsource, AActor); PARAM_INT(bombdamage); - PARAM_INT(bombdistance); + PARAM_FLOAT(bombdistance); PARAM_NAME(damagetype); - PARAM_INT(fulldamagedistance); + PARAM_FLOAT(fulldamagedistance); P_GeometryRadiusAttack(bombspot, bombsource, bombdamage, bombdistance, damagetype, fulldamagedistance); return 0; } diff --git a/src/playsim/p_destructible.h b/src/playsim/p_destructible.h index 24a95b6a4..c4e6e9d23 100644 --- a/src/playsim/p_destructible.h +++ b/src/playsim/p_destructible.h @@ -34,7 +34,7 @@ void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagety void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius, bool dogroups); void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType); -void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance); +void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, double bombdistance, FName damagetype, double fulldamagedistance); bool P_ProjectileHitLinedef(AActor* projectile, line_t* line); bool P_ProjectileHitPlane(AActor* projectile, int part); diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index f63d00588..50ed1a74d 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -1271,7 +1271,7 @@ int P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams { maxdist = params->maxDist; mindist = params->minDist; - fov = params->Fov; + fov = allaround ? DAngle::fromDeg(0.) : params->Fov; // [RK] Account for LOOKALLAROUND flag. } else { @@ -2086,7 +2086,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LookEx) { // If we find a valid target here, the wandering logic should *not* // be activated! If would cause the seestate to be set twice. - if (P_LookForPlayers(self, true, ¶ms)) + if (P_LookForPlayers(self, (self->flags4 & MF4_LOOKALLAROUND), ¶ms)) // [RK] Passing true for allround should only occur if the flag is actually set. goto seeyou; } @@ -2899,13 +2899,8 @@ void A_Chase(AActor *self) A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); } -DEFINE_ACTION_FUNCTION(AActor, A_Chase) +void A_ChaseNative(AActor * self, int meleelabel, int missilelabel, int flags) { - PARAM_SELF_PROLOGUE(AActor); - PARAM_STATELABEL(meleelabel); - PARAM_STATELABEL(missilelabel); - PARAM_INT(flags); - FName meleename = ENamedName(meleelabel - 0x10000000); FName missilename = ENamedName(missilelabel - 0x10000000); if (meleename != NAME__a_chase_default || missilename != NAME__a_chase_default) @@ -2913,7 +2908,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Chase) FState *melee = StateLabels.GetState(meleelabel, self->GetClass()); FState *missile = StateLabels.GetState(missilelabel, self->GetClass()); if ((flags & CHF_RESURRECT) && P_CheckForResurrection(self, false)) - return 0; + return; A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), !!(flags&CHF_NIGHTMAREFAST), !!(flags&CHF_DONTMOVE), flags & 0x3fffffff); @@ -2922,6 +2917,36 @@ DEFINE_ACTION_FUNCTION(AActor, A_Chase) { A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); } +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_Chase, A_ChaseNative) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_STATELABEL(meleelabel); + PARAM_STATELABEL(missilelabel); + PARAM_INT(flags); + + A_ChaseNative(self, meleelabel, missilelabel, flags); + + return 0; +} + +void A_DoChaseNative(AActor * self, FState *melee, FState *missile, int flags) +{ + if ((flags & CHF_RESURRECT) && P_CheckForResurrection(self, false)) + return; + A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), !!(flags&CHF_NIGHTMAREFAST), !!(flags&CHF_DONTMOVE), flags & 0x3fffffff); +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_DoChase, A_DoChaseNative) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_STATE(melee); + PARAM_STATE(missile); + PARAM_INT(flags); + + A_DoChaseNative(self, melee, missile, flags); + return 0; } diff --git a/src/playsim/p_local.h b/src/playsim/p_local.h index ea40d386a..cde45f403 100644 --- a/src/playsim/p_local.h +++ b/src/playsim/p_local.h @@ -410,9 +410,9 @@ enum RADF_NOALLIES = 128, RADF_CIRCULAR = 256 }; -int P_GetRadiusDamage(AActor *self, AActor *thing, int damage, int distance, int fulldmgdistance, bool oldradiusdmg, bool circular); -int P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, - FName damageType, int flags, int fulldamagedistance=0, FName species = NAME_None); +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, + FName damageType, int flags, double fulldamagedistance=0.0, FName species = NAME_None); void P_DelSeclist(msecnode_t *, msecnode_t *sector_t::*seclisthead); void P_DelSeclist(portnode_t *, portnode_t *FLinePortal::*seclisthead); diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index d6a828024..53329b96f 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -5949,7 +5949,7 @@ CUSTOM_CVAR(Float, splashfactor, 1.f, CVAR_SERVERINFO) // Used by anything without OLDRADIUSDMG flag //========================================================================== -static double GetRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, int bombdamage, int bombdistance, int fulldamagedistance, bool thingbombsource, bool round) +static double GetRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, int bombdamage, double bombdistance, double fulldamagedistance, bool thingbombsource, bool round) { // [RH] New code. The bounding box only covers the // height of the thing and not the height of the map. @@ -5958,7 +5958,7 @@ static double GetRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, double dx, dy; double boxradius; - double bombdistancefloat = 1. / (double)(bombdistance - fulldamagedistance); + double bombdistancefloat = 1.0 / (bombdistance - fulldamagedistance); double bombdamagefloat = (double)bombdamage; if (!round) @@ -6005,8 +6005,8 @@ static double GetRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, { len = bombspot->Distance3D (thing); } - len = clamp(len - (double)fulldamagedistance, 0, len); - points = bombdamagefloat * (1. - len * bombdistancefloat); + len = clamp(len - fulldamagedistance, 0.0, len); + points = bombdamagefloat * (1.0 - len * bombdistancefloat); // Calculate the splash and radius damage factor if called by P_RadiusAttack. // Otherwise, just get the raw damage. This allows modders to manipulate it @@ -6034,7 +6034,7 @@ static double GetRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, // based on XY distance. //========================================================================== -static int GetOldRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, int bombdamage, int bombdistance, int fulldamagedistance) +static int GetOldRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, int bombdamage, double bombdistance, double fulldamagedistance) { const int ret = fromaction ? 0 : -1; // -1 is specifically for P_RadiusAttack; continue onto another actor. double dx, dy, dist; @@ -6055,8 +6055,8 @@ static int GetOldRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, // When called from the action function, ignore the sight check. if (fromaction || P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { - dist = clamp(dist - fulldamagedistance, 0, dist); - int damage = Scale(bombdamage, bombdistance - int(dist), bombdistance); + dist = clamp(dist - fulldamagedistance, 0.0, dist); + int damage = (int)Scale((double)bombdamage, bombdistance - dist, bombdistance); if (!fromaction) { @@ -6078,7 +6078,7 @@ static int GetOldRadiusDamage(bool fromaction, AActor *bombspot, AActor *thing, // damage and not taking into account any damage reduction. //========================================================================== -int P_GetRadiusDamage(AActor *self, AActor *thing, int damage, int distance, int fulldmgdistance, bool oldradiusdmg, bool circular) +int P_GetRadiusDamage(AActor *self, AActor *thing, int damage, double distance, double fulldmgdistance, bool oldradiusdmg, bool circular) { if (!thing) @@ -6090,10 +6090,10 @@ int P_GetRadiusDamage(AActor *self, AActor *thing, int damage, int distance, int return damage; } - fulldmgdistance = clamp(fulldmgdistance, 0, distance - 1); + fulldmgdistance = clamp(fulldmgdistance, 0.0, distance - 1.0); // Mirroring A_Explode's behavior. - if (distance <= 0) + if (distance <= 0.0) distance = damage; const int newdam = oldradiusdmg @@ -6110,15 +6110,15 @@ int P_GetRadiusDamage(AActor *self, AActor *thing, int damage, int distance, int // //========================================================================== -int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bombdistance, FName bombmod, - int flags, int fulldamagedistance, FName species) +int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, double bombdistance, FName bombmod, + int flags, double fulldamagedistance, FName species) { - if (bombdistance <= 0) + if (bombdistance <= 0.0) return 0; - fulldamagedistance = clamp(fulldamagedistance, 0, bombdistance - 1); + fulldamagedistance = clamp(fulldamagedistance, 0.0, bombdistance - 1.0); FPortalGroupArray grouplist(FPortalGroupArray::PGA_Full3d); - FMultiBlockThingsIterator it(grouplist, bombspot->Level, bombspot->X(), bombspot->Y(), bombspot->Z() - bombdistance, bombspot->Height + bombdistance*2, bombdistance, false, bombspot->Sector); + FMultiBlockThingsIterator it(grouplist, bombspot->Level, bombspot->X(), bombspot->Y(), bombspot->Z() - bombdistance, bombspot->Height + bombdistance*2.0, bombdistance, false, bombspot->Sector); FMultiBlockThingsIterator::CheckResult cres; if (flags & RADF_SOURCEISSPOT) diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index c4e6ab69e..0005f01f9 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -101,6 +101,7 @@ #include "fragglescript/t_fs.h" #include "shadowinlines.h" #include "d_net.h" +#include "model.h" // MACROS ------------------------------------------------------------------ @@ -1387,18 +1388,57 @@ bool AActor::Massacre () // //---------------------------------------------------------------------------- +void SerializeModelID(FSerializer &arc, const char *key, int &id) +{ // TODO: make it a proper serializable type (FModelID) instead of an int + if(arc.isWriting()) + { + if(id >= 0) + { + arc(key, Models[id]->mFilePath); + } + } + else + { + if(arc.HasKey(key)) + { + std::pair modelFile; + arc(key, modelFile); + + id = FindModel(modelFile.first.GetChars(), modelFile.second.GetChars(), true); + } + else + { + id = -1; + } + } +} + FSerializer &Serialize(FSerializer &arc, const char *key, ModelOverride &mo, ModelOverride *def) { arc.BeginObject(key); - arc("modelID", mo.modelID); + SerializeModelID(arc, "model", mo.modelID); arc("surfaceSkinIDs", mo.surfaceSkinIDs); arc.EndObject(); return arc; } +FSerializer &Serialize(FSerializer &arc, const char *key, AnimModelOverride &amo, AnimModelOverride *def) +{ + int ok = arc.BeginObject(key); + if(arc.isReading() && !ok) + { + amo.id = -1; + } + else if(ok) + { + SerializeModelID(arc, "model", amo.id); + arc.EndObject(); + } + return arc; +} + FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimOverride &ao, struct AnimOverride *def) { - //TODO arc.BeginObject(key); arc("firstFrame", ao.firstFrame); arc("lastFrame", ao.lastFrame); @@ -5208,15 +5248,14 @@ extern bool demonew; //========================================================================== // -// This function is dangerous and only designed for swapping player pawns +// This function is only designed for swapping player pawns // over to their new ones upon changing levels or respawning. It SHOULD NOT be // used for anything else! Do not export this functionality as it's -// meant strictly for internal usage. Only swap pointers if the thing being swapped -// to is a type of the thing being swapped from. +// meant strictly for internal usage. // //========================================================================== -void PlayerPointerSubstitution(AActor* oldPlayer, AActor* newPlayer) +void PlayerPointerSubstitution(AActor* oldPlayer, AActor* newPlayer, bool removeOld) { if (oldPlayer == nullptr || newPlayer == nullptr || oldPlayer == newPlayer || !oldPlayer->IsKindOf(NAME_PlayerPawn) || !newPlayer->IsKindOf(NAME_PlayerPawn)) @@ -5253,20 +5292,16 @@ void PlayerPointerSubstitution(AActor* oldPlayer, AActor* newPlayer) sec.SoundTarget = newPlayer; } - // Update all the remaining object pointers. This is dangerous but needed to ensure - // everything functions correctly when respawning or changing levels. + // Update all the remaining object pointers. for (DObject* probe = GC::Root; probe != nullptr; probe = probe->ObjNext) - probe->PointerSubstitution(oldPlayer, newPlayer); + probe->PointerSubstitution(oldPlayer, newPlayer, removeOld); } //========================================================================== // -// This function is much safer than PlayerPointerSubstition as it only truly -// swaps a few safe pointers. This has some extra barriers to it to allow +// This has some extra barriers compared to PlayerPointerSubstitution to allow // Actors to freely morph into other Actors which is its main usage. -// Previously this used raw pointer substitutions but that's far too -// volatile to use with modder-provided information. It also allows morphing -// to be more extendable from ZScript. +// It also allows morphing to be more extendable from ZScript. // //========================================================================== @@ -5311,30 +5346,6 @@ int MorphPointerSubstitution(AActor* from, AActor* to) VMCall(func, params, 2, nullptr, 0); } - // Only change some gameplay-related pointers that we know we can safely swap to whatever - // new Actor class is present. - AActor* mo = nullptr; - auto it = from->Level->GetThinkerIterator(); - while ((mo = it.Next()) != nullptr) - { - if (mo->target == from) - mo->target = to; - if (mo->tracer == from) - mo->tracer = to; - if (mo->master == from) - mo->master = to; - if (mo->goal == from) - mo->goal = to; - if (mo->lastenemy == from) - mo->lastenemy = to; - if (mo->LastHeard == from) - mo->LastHeard = to; - if (mo->LastLookActor == from) - mo->LastLookActor = to; - if (mo->Poisoner == from) - mo->Poisoner = to; - } - // Go through player infos. for (int i = 0; i < MAXPLAYERS; ++i) { @@ -5364,6 +5375,10 @@ int MorphPointerSubstitution(AActor* from, AActor* to) sec.SoundTarget = to; } + // Replace any object pointers that are safe to swap around. + for (DObject* probe = GC::Root; probe != nullptr; probe = probe->ObjNext) + probe->PointerSubstitution(from, to, false); + // Remaining maintenance related to morphing. if (from->player != nullptr) { @@ -5493,6 +5508,7 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag p->mo = mobj; mobj->player = p; state = p->playerstate; + const auto heldWeap = state == PST_REBORN && (dmflags3 & DF3_REMEMBER_LAST_WEAP) ? p->ReadyWeapon : nullptr; if (state == PST_REBORN || state == PST_ENTER) { PlayerReborn (playernum); @@ -5593,7 +5609,7 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag { // Special inventory handling for respawning in coop IFVM(PlayerPawn, FilterCoopRespawnInventory) { - VMValue params[] = { p->mo, oldactor }; + VMValue params[] = { p->mo, oldactor, ((heldWeap == nullptr || (heldWeap->ObjectFlags & OF_EuthanizeMe)) ? nullptr : heldWeap) }; VMCall(func, params, 2, nullptr, 0); } } @@ -5656,7 +5672,7 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag if (sec.SoundTarget == oldactor) sec.SoundTarget = nullptr; } - PlayerPointerSubstitution (oldactor, p->mo); + PlayerPointerSubstitution (oldactor, p->mo, false); localEventManager->PlayerRespawned(PlayerNum(p)); Behaviors.StartTypedScripts (SCRIPT_Respawn, p->mo, true); diff --git a/src/playsim/p_spec.h b/src/playsim/p_spec.h index ff0095eea..5d602eb1d 100644 --- a/src/playsim/p_spec.h +++ b/src/playsim/p_spec.h @@ -164,7 +164,7 @@ void P_TerminateScript (FLevelLocals *Level, int script, const char *map); // // [RH] p_quake.c // -bool P_StartQuakeXYZ(FLevelLocals *Level, AActor *activator, int tid, double intensityX, double intensityY, double intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, double rollIntensity, double rollWave, double damageMultiplier, double thrustMultiplier, int damage); -bool P_StartQuake(FLevelLocals *Level, AActor *activator, int tid, double intensity, int duration, int damrad, int tremrad, FSoundID quakesfx); +bool P_StartQuakeXYZ(FLevelLocals *Level, AActor *activator, int tid, double intensityX, double intensityY, double intensityZ, int duration, double damrad, double tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, double falloff, int highpoint, double rollIntensity, double rollWave, double damageMultiplier, double thrustMultiplier, int damage); +bool P_StartQuake(FLevelLocals *Level, AActor *activator, int tid, double intensity, int duration, double damrad, double tremrad, FSoundID quakesfx); #endif diff --git a/src/playsim/p_user.cpp b/src/playsim/p_user.cpp index a1888d5c1..6839b412a 100644 --- a/src/playsim/p_user.cpp +++ b/src/playsim/p_user.cpp @@ -1278,6 +1278,7 @@ void P_PlayerThink (player_t *player) player->cheats &= ~CF_INTERPVIEWANGLES; player->cheats &= ~CF_SCALEDNOLERP; player->cheats &= ~CF_NOFOVINTERP; + player->cheats &= ~CF_NOVIEWPOSINTERP; player->mo->FloatVar("prevBob") = player->bob; IFVIRTUALPTRNAME(player->mo, NAME_PlayerPawn, PlayerThink) diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index ec9cd502f..fc5407a67 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -278,12 +278,19 @@ double getCurrentFrame(const AnimOverride &anim, double tic) { if(anim.framerate <= 0) return anim.startFrame; - double duration = double(anim.lastFrame - anim.firstFrame) / double(anim.framerate); // duration in seconds - double startPos = double(anim.startFrame - anim.firstFrame) / double(anim.framerate); + double frame = ((tic - anim.startTic) / GameTicRate) * anim.framerate; // position in frames - double pos = startPos + ((tic - anim.startTic) / GameTicRate); // position in seconds + double duration = double(anim.lastFrame) - anim.startFrame; - return (((anim.flags & ANIMOVERRIDE_LOOP) ? fmod(pos, duration) : min(pos, duration)) * anim.framerate) + anim.firstFrame; + if((anim.flags & ANIMOVERRIDE_LOOP) && frame >= duration) + { + frame = frame - duration; + return fmod(frame, anim.lastFrame - anim.loopFrame) + anim.loopFrame; + } + else + { + return min(frame, duration) + anim.startFrame; + } } static void calcFrame(const AnimOverride &anim, double tic, double &inter, int &prev, int &next) @@ -294,22 +301,7 @@ static void calcFrame(const AnimOverride &anim, double tic, double &inter, int & inter = frame - prev; - if(frame > anim.lastFrame) - { - if(anim.flags & ANIMOVERRIDE_LOOP) - { - next = anim.loopFrame + (prev - anim.lastFrame); - } - else - { - inter = 0; - prev = next = anim.lastFrame; - } - } - else - { - next = int(ceil(frame)); - } + next = int(ceil(frame)); } void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor) @@ -523,7 +515,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr skinid = smf->skinIDs[i]; } - if (modelid >= 0) + if (modelid >= 0 && modelid < Models.size()) { FModel * mdl = Models[modelid]; auto tex = skinid.isValid() ? TexMan.GetGameTexture(skinid, true) : nullptr; diff --git a/src/rendering/hwrenderer/scene/hw_walls.cpp b/src/rendering/hwrenderer/scene/hw_walls.cpp index d16f6b3f0..fa4c0adfa 100644 --- a/src/rendering/hwrenderer/scene/hw_walls.cpp +++ b/src/rendering/hwrenderer/scene/hw_walls.cpp @@ -2367,6 +2367,7 @@ void HWWall::ProcessLowerMiniseg(HWWallDispatcher *di, FRenderState& state, seg_ this->frontsector = frontsector; this->backsector = backsector; this->sub = NULL; + //this->lightmap = nullptr; // this came from gzdoom commit f796e55c0d313e1c48c3472a7334de37cc4ae775 vertex_t * v1 = seg->v1; vertex_t * v2 = seg->v2; diff --git a/src/rendering/r_utility.cpp b/src/rendering/r_utility.cpp index fbb785d0b..b78cf955d 100644 --- a/src/rendering/r_utility.cpp +++ b/src/rendering/r_utility.cpp @@ -78,7 +78,7 @@ struct InterpolationViewer { struct instance { - DVector3 Pos; + DVector3 Pos, ViewPos; DRotator Angles; DRotator ViewAngles; }; @@ -605,6 +605,22 @@ void R_InterpolateView(FRenderViewpoint& viewPoint, const player_t* const player // Now that the base position and angles are set, add offsets. + const DViewPosition* const vPos = iView->ViewActor->ViewPos; + if (vPos != nullptr && !(vPos->Flags & VPSF_ABSOLUTEPOS) + && (player == nullptr || gamestate == GS_TITLELEVEL || (!(player->cheats & CF_CHASECAM) && (!r_deathcamera || !(iView->ViewActor->flags6 & MF6_KILLED))))) + { + DVector3 vOfs = {}; + if (player == nullptr || !(player->cheats & CF_NOVIEWPOSINTERP)) + vOfs = iView->Old.ViewPos * inverseTicFrac + iView->New.ViewPos * ticFrac; + else + vOfs = iView->New.ViewPos; + + if (vPos->Flags & VPSF_ABSOLUTEOFFSET) + iView->ViewOffset += vOfs; + else + iView->RelativeViewOffset += vOfs; + } + DVector3 posOfs = iView->ViewOffset; if (!iView->RelativeViewOffset.isZero()) posOfs += DQuaternion::FromAngles(viewPoint.Angles.Yaw, viewPoint.Angles.Pitch, viewPoint.Angles.Roll) * iView->RelativeViewOffset; @@ -916,8 +932,8 @@ void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AA viewPoint.showviewer = false; viewPoint.bForceNoViewer = matchPlayer; - if (gamestate != GS_TITLELEVEL && - ((player != nullptr && (player->cheats & CF_CHASECAM)) || (r_deathcamera && (viewPoint.camera->flags6 & MF6_KILLED)))) + if (player != nullptr && gamestate != GS_TITLELEVEL + && ((player->cheats & CF_CHASECAM) || (r_deathcamera && (viewPoint.camera->flags6 & MF6_KILLED)))) { // The cam Actor should probably be visible in third person. viewPoint.showviewer = true; @@ -925,7 +941,8 @@ void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AA iView->ViewOffset.Z = clamp(chase_height, -1000.0, 1000.0); iView->RelativeViewOffset.X = -clamp(chase_dist, 0.0, 30000.0); } - else if (viewOffset != nullptr) + + if (viewOffset != nullptr) { // No chase/death cam, so use the view offset. if (!viewPoint.bForceNoViewer) @@ -933,16 +950,13 @@ void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AA if (viewOffset->Flags & VPSF_ABSOLUTEPOS) { + iView->New.ViewPos.Zero(); if (!matchPlayer) camPos = viewOffset->Offset; } - else if (viewOffset->Flags & VPSF_ABSOLUTEOFFSET) - { - iView->ViewOffset += viewOffset->Offset; - } else { - iView->RelativeViewOffset = viewOffset->Offset; + iView->New.ViewPos = viewOffset->Offset; } } diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index c10212098..7094212e0 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1317,14 +1317,14 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetRadiusDamage, P_GetRadiusDamage) PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT(thing, AActor); PARAM_INT(damage); - PARAM_INT(distance); - PARAM_INT(fulldmgdistance); + PARAM_FLOAT(distance); + PARAM_FLOAT(fulldmgdistance); PARAM_BOOL(oldradiusdmg); PARAM_BOOL(circular); ACTION_RETURN_INT(P_GetRadiusDamage(self, thing, damage, distance, fulldmgdistance, oldradiusdmg, circular)); } -static int RadiusAttack(AActor *self, AActor *bombsource, int bombdamage, int bombdistance, int damagetype, int flags, int fulldamagedistance, int species) +static int RadiusAttack(AActor *self, AActor *bombsource, int bombdamage, double bombdistance, int damagetype, int flags, double fulldamagedistance, int species) { return P_RadiusAttack(self, bombsource, bombdamage, bombdistance, ENamedName(damagetype), flags, fulldamagedistance, ENamedName(species)); } @@ -1334,10 +1334,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, RadiusAttack, RadiusAttack) PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT(bombsource, AActor); PARAM_INT(bombdamage); - PARAM_INT(bombdistance); + PARAM_FLOAT(bombdistance); PARAM_INT(damagetype); PARAM_INT(flags); - PARAM_INT(fulldamagedistance); + PARAM_FLOAT(fulldamagedistance); PARAM_INT(species); ACTION_RETURN_INT(RadiusAttack(self, bombsource, bombdamage, bombdistance, damagetype, flags, fulldamagedistance, species)); } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index e20a53ff3..41ee94ec9 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1833,6 +1833,7 @@ OptionMenu CoopOptions protected Option "$GMPLYMNU_SHAREKEYS", "sv_coopsharekeys", "YesNo" Option "$GMPLYMNU_LOCALITEMS", "sv_localitems", "YesNo" Option "$GMPLYMNU_NOLOCALDROP", "sv_nolocaldrops", "YesNo" + Option "$GMPLYMNU_REMEMBERWEAP", "sv_rememberlastweapon", "YesNo" Class "GameplayMenu" } diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index c1fe424de..72f441b10 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -273,7 +273,7 @@ class Actor : Thinker native meta Name BloodType2; // Bloopsplatter replacement type meta Name BloodType3; // AxeBlood replacement type meta bool DontHurtShooter; - meta int ExplosionRadius; + meta double ExplosionRadius; meta int ExplosionDamage; meta int MeleeDamage; meta Sound MeleeSound; @@ -432,7 +432,7 @@ class Actor : Thinker native DefThreshold 100; BloodType "Blood", "BloodSplatter", "AxeBlood"; ExplosionDamage 128; - ExplosionRadius -1; // i.e. use ExplosionDamage value + ExplosionRadius -1.0; // i.e. use ExplosionDamage value MissileHeight 32; SpriteAngle 0; SpriteRotation 0; @@ -1145,6 +1145,7 @@ class Actor : Thinker native void A_Fall() { A_NoBlocking(); } native void A_Look(); native void A_Chase(statelabel melee = '_a_chase_default', statelabel missile = '_a_chase_default', int flags = 0); + native void A_DoChase(State melee, State missile, int flags = 0); native void A_VileChase(); native bool A_CheckForResurrection(State state = null, Sound snd = 0); native void A_BossDeath(); @@ -1225,9 +1226,9 @@ class Actor : Thinker native native void A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true); native void A_CustomComboAttack(class missiletype, double spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); native void A_Burst(class chunktype); - native void A_RadiusDamageSelf(int damage = 128, double distance = 128, int flags = 0, class flashtype = null); - native int GetRadiusDamage(Actor thing, int damage, int distance, int fulldmgdistance = 0, bool oldradiusdmg = false, bool circular = false); - native int RadiusAttack(Actor bombsource, int bombdamage, int bombdistance, Name bombmod = 'none', int flags = RADF_HURTSOURCE, int fulldamagedistance = 0, name species = "None"); + native void A_RadiusDamageSelf(int damage = 128, double distance = 128.0, int flags = 0, class flashtype = null); + native int GetRadiusDamage(Actor thing, int damage, double distance, double fulldmgdistance = 0.0, bool oldradiusdmg = false, bool circular = false); + native int RadiusAttack(Actor bombsource, int bombdamage, double bombdistance, Name bombmod = 'none', int flags = RADF_HURTSOURCE, double fulldamagedistance = 0.0, name species = "None"); native void A_Respawn(int flags = 1); native void A_RestoreSpecialPosition(); @@ -1247,8 +1248,8 @@ class Actor : Thinker native deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserArray(name varname, int index, int value); deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserVarFloat(name varname, double value); deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserArrayFloat(name varname, int index, double value); - native void A_Quake(double intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake"); - native void A_QuakeEx(double intensityX, double intensityY, double intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, double mulWaveX = 1, double mulWaveY = 1, double mulWaveZ = 1, int falloff = 0, int highpoint = 0, double rollIntensity = 0, double rollWave = 0, double damageMultiplier = 1, double thrustMultiplier = 0.5, int damage = 0); + native void A_Quake(double intensity, int duration, double damrad, double tremrad, sound sfx = "world/quake"); + native void A_QuakeEx(double intensityX, double intensityY, double intensityZ, int duration, double damrad, double tremrad, sound sfx = "world/quake", int flags = 0, double mulWaveX = 1, double mulWaveY = 1, double mulWaveZ = 1, double falloff = 0, int highpoint = 0, double rollIntensity = 0, double rollWave = 0, double damageMultiplier = 1, double thrustMultiplier = 0.5, int damage = 0); action native void A_SetTics(int tics); native void A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = null, name species = "None", int src = AAPTR_DEFAULT, int inflict = AAPTR_DEFAULT); native void A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = null, name species = "None", int src = AAPTR_DEFAULT, int inflict = AAPTR_DEFAULT); @@ -1304,8 +1305,8 @@ class Actor : Thinker native native bool A_AttachLight(Name lightid, int type, Color lightcolor, int radius1, int radius2, int flags = 0, Vector3 ofs = (0,0,0), double param = 0, double spoti = 10, double spoto = 25, double spotp = 0); native bool A_RemoveLight(Name lightid); - native version("4.12") void SetAnimation(Name animName, double framerate = -1, int startFrame = -1, int loopFrame= -1, int interpolateTics = -1, int flags = 0); - native version("4.12") ui void SetAnimationUI(Name animName, double framerate = -1, int startFrame = -1, int loopFrame = -1, int interpolateTics = -1, int flags = 0); + native version("4.12") void SetAnimation(Name animName, double framerate = -1, int startFrame = -1, int loopFrame= -1, int endFrame = -1, int interpolateTics = -1, int flags = 0); + native version("4.12") ui void SetAnimationUI(Name animName, double framerate = -1, int startFrame = -1, int loopFrame = -1, int endFrame = -1, int interpolateTics = -1, int flags = 0); native version("4.12") void SetAnimationFrameRate(double framerate); native version("4.12") ui void SetAnimationFrameRateUI(double framerate); diff --git a/wadsrc/static/zscript/actors/attacks.zs b/wadsrc/static/zscript/actors/attacks.zs index ac754d1e1..188a9c064 100644 --- a/wadsrc/static/zscript/actors/attacks.zs +++ b/wadsrc/static/zscript/actors/attacks.zs @@ -569,7 +569,7 @@ extend class Actor // //========================================================================== - int A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff", name damagetype = "none") + int A_Explode(int damage = -1, double distance = -1.0, int flags = XF_HURTSOURCE, bool alert = false, double fulldamagedistance = 0.0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff", name damagetype = "none") { if (damage < 0) // get parameters from metadata @@ -626,7 +626,7 @@ extend class Actor } deprecated("2.3", "For Dehacked use only") - void A_RadiusDamage(int dam, int dist) + void A_RadiusDamage(int dam, double dist) { A_Explode(dam, dist); } @@ -637,10 +637,10 @@ extend class Actor // //========================================================================== - void A_RadiusThrust(int force = 128, int distance = -1, int flags = RTF_AFFECTSOURCE, int fullthrustdistance = 0, name species = "None") + void A_RadiusThrust(int force = 128, double distance = -1.0, int flags = RTF_AFFECTSOURCE, double fullthrustdistance = 0.0, name species = "None") { if (force == 0) force = 128; - if (distance <= 0) distance = abs(force); + if (distance <= 0.0) distance = abs(force); bool nothrust = false; if (target) diff --git a/wadsrc/static/zscript/actors/inventory/inventory.zs b/wadsrc/static/zscript/actors/inventory/inventory.zs index 4108650a4..c9890c699 100644 --- a/wadsrc/static/zscript/actors/inventory/inventory.zs +++ b/wadsrc/static/zscript/actors/inventory/inventory.zs @@ -12,6 +12,7 @@ class Inventory : Actor private bool bSharingItem; // Currently being shared (avoid infinite recursions). private bool pickedUp[MAXPLAYERS]; // If items are set to local, track who already picked it up. + private bool bCreatingCopy; // Tells GoAway that it needs to return true so a new copy of the item is spawned. deprecated("3.7") private int ItemFlags; Actor Owner; // Who owns this item? NULL if it's still a pickup. @@ -835,9 +836,11 @@ class Inventory : Actor Inventory give = self; if (localPickUp) { - give = Inventory(Spawn(GetClass())); + give = CreateLocalCopy(toucher); if (!give) return; + + localPickUp = give != self; } bool res; @@ -1043,6 +1046,9 @@ class Inventory : Actor protected bool GoAway () { + if (bCreatingCopy) + return true; + // Dropped items never stick around if (bDropped) { @@ -1113,6 +1119,17 @@ class Inventory : Actor pickedUp[pNum] = true; DisableLocalRendering(pNum, true); } + + // Force spawn a new version of the item. This needs to use CreateCopy so that + // any transferrable properties on the item get correctly set. + Inventory CreateLocalCopy(Actor client) + { + bCreatingCopy = true; + let item = CreateCopy(client); + bCreatingCopy = false; + + return item; + } //=========================================================================== // diff --git a/wadsrc/static/zscript/actors/player/player.zs b/wadsrc/static/zscript/actors/player/player.zs index d87fc1706..e6c66fa44 100644 --- a/wadsrc/static/zscript/actors/player/player.zs +++ b/wadsrc/static/zscript/actors/player/player.zs @@ -867,7 +867,7 @@ class PlayerPawn : Actor // //=========================================================================== - void FilterCoopRespawnInventory (PlayerPawn oldplayer) + void FilterCoopRespawnInventory (PlayerPawn oldplayer, Weapon curHeldWeapon = null) { // If we're losing everything, this is really simple. if (sv_cooploseinventory) @@ -876,6 +876,10 @@ class PlayerPawn : Actor return; } + // Make sure to get the real held weapon before messing with the inventory. + if (curHeldWeapon && curHeldWeapon.bPowered_Up) + curHeldWeapon = curHeldWeapon.SisterWeapon; + // Walk through the old player's inventory and destroy or modify // according to dmflags. Inventory next; @@ -957,7 +961,15 @@ class PlayerPawn : Actor ObtainInventory (oldplayer); player.ReadyWeapon = NULL; - PickNewWeapon (NULL); + if (curHeldWeapon && curHeldWeapon.owner == self && curHeldWeapon.CheckAmmo(Weapon.EitherFire, false)) + { + player.PendingWeapon = curHeldWeapon; + BringUpWeapon(); + } + else + { + PickNewWeapon (NULL); + } } @@ -1251,6 +1263,12 @@ class PlayerPawn : Actor return forward, side; } + virtual void ApplyAirControl(out double movefactor, out double bobfactor) + { + movefactor *= level.aircontrol; + bobfactor *= level.aircontrol; + } + //---------------------------------------------------------------------------- // // PROC P_MovePlayer @@ -1294,8 +1312,8 @@ class PlayerPawn : Actor if (!player.onground && !bNoGravity && !waterlevel) { // [RH] allow very limited movement if not on ground. - movefactor *= level.aircontrol; - bobfactor*= level.aircontrol; + // [AA] but also allow authors to override it. + ApplyAirControl(movefactor, bobfactor); } fm = cmd.forwardmove; diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index b991f7a79..cb416b3c5 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -375,6 +375,7 @@ enum ESetAnimationFlags { SAF_INSTANT = 1 << 0, SAF_LOOP = 1 << 1, + SAF_NOOVERRIDE = 1 << 2, }; // Change model flags @@ -1161,6 +1162,7 @@ enum EPlayerCheats CF_INTERPVIEWANGLES = 1 << 15, // [MR] flag for interpolating view angles without interpolating the entire frame CF_NOFOVINTERP = 1 << 16, // [B] Disable FOV interpolation when instantly zooming CF_SCALEDNOLERP = 1 << 17, // [MR] flag for applying angles changes in the ticrate without interpolating the frame + CF_NOVIEWPOSINTERP = 1 << 18, // Disable view position interpolation. CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths. diff --git a/wadsrc/static/zscript/destructible.zs b/wadsrc/static/zscript/destructible.zs index a7e0dc982..84861a45a 100644 --- a/wadsrc/static/zscript/destructible.zs +++ b/wadsrc/static/zscript/destructible.zs @@ -28,7 +28,7 @@ struct Destructible native play static native void DamageLinedef(Line def, Actor source, int damage, Name damagetype, int side, vector3 position, bool isradius); static native void GeometryLineAttack(TraceResults trace, Actor thing, int damage, Name damagetype); - static native void GeometryRadiusAttack(Actor bombspot, Actor bombsource, int bombdamage, int bombdistance, Name damagetype, int fulldamagedistance); + static native void GeometryRadiusAttack(Actor bombspot, Actor bombsource, int bombdamage, double bombdistance, Name damagetype, double fulldamagedistance); static native bool ProjectileHitLinedef(Actor projectile, Line def); static native bool ProjectileHitPlane(Actor projectile, SectorPart part);