diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc42f81e14..e0a12da252 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1197,7 +1197,9 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE p_plats.cpp p_portals.cpp p_pspr.cpp + p_pusher.cpp p_saveg.cpp + p_scroll.cpp p_sectors.cpp p_setup.cpp p_sight.cpp diff --git a/src/dobject.cpp b/src/dobject.cpp index efb4370728..511f3e5f01 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -257,46 +257,50 @@ DObject::DObject (PClass *inClass) DObject::~DObject () { - if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown) + if (!PClass::bShutdown) { - DObject **probe; PClass *type = GetClass(); - - if (!(ObjectFlags & OF_YesReallyDelete)) + if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown) { - Printf ("Warning: '%s' is freed outside the GC process.\n", - type != NULL ? type->TypeName.GetChars() : "==some object=="); - } + DObject **probe; - // Find all pointers that reference this object and NULL them. - StaticPointerSubstitution(this, NULL); - - // Now unlink this object from the GC list. - for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext)) - { - if (*probe == this) + if (!(ObjectFlags & OF_YesReallyDelete)) { - *probe = ObjNext; - if (&ObjNext == GC::SweepPos) - { - GC::SweepPos = probe; - } - break; + Printf("Warning: '%s' is freed outside the GC process.\n", + type != NULL ? type->TypeName.GetChars() : "==some object=="); } - } - // If it's gray, also unlink it from the gray list. - if (this->IsGray()) - { - for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext)) + // Find all pointers that reference this object and NULL them. + StaticPointerSubstitution(this, NULL); + + // Now unlink this object from the GC list. + for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext)) { if (*probe == this) { - *probe = GCNext; + *probe = ObjNext; + if (&ObjNext == GC::SweepPos) + { + GC::SweepPos = probe; + } break; } } + + // If it's gray, also unlink it from the gray list. + if (this->IsGray()) + { + for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext)) + { + if (*probe == this) + { + *probe = GCNext; + break; + } + } + } } + type->DestroySpecials(this); } } @@ -417,46 +421,15 @@ void DObject::SerializeUserVars(FArchive &arc) if (arc.IsStoring()) { - // Write all user variables. - for (; symt != NULL; symt = symt->ParentSymbolTable) - { - PSymbolTable::MapType::Iterator it(symt->Symbols); - PSymbolTable::MapType::Pair *pair; - - while (it.NextPair(pair)) - { - PField *var = dyn_cast(pair->Value); - if (var != NULL && !(var->Flags & VARF_Native)) - { - PType *type = var->Type; - PArray *arraytype = dyn_cast(type); - if (arraytype == NULL) - { - count = 1; - } - else - { - count = arraytype->ElementCount; - type = arraytype->ElementType; - } - assert(type == TypeSInt32); - varloc = (int *)(reinterpret_cast(this) + var->Offset); - - arc << var->SymbolName; - arc.WriteCount(count); - for (j = 0; j < count; ++j) - { - arc << varloc[j]; - } - } - } - } - // Write terminator. - varname = NAME_None; - arc << varname; + // Write all fields that aren't serialized by native code. + GetClass()->WriteValue(arc, this); + } + else if (SaveVersion >= 4535) + { + GetClass()->ReadValue(arc, this); } else - { + { // Old version that only deals with ints // Read user variables until 'None' is encountered. arc << varname; while (varname != NAME_None) diff --git a/src/dobject.h b/src/dobject.h index 22e6127234..682e9bb082 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -42,6 +42,7 @@ class PClass; class FArchive; class DObject; +/* class DArgs; class DCanvas; class DConsoleCommand; @@ -77,6 +78,7 @@ class DFloor; class DFloorWaggle; class DPlat; class DPillar; +*/ class PClassActor; diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 609ec17211..fbc3ceb730 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -39,6 +39,7 @@ #include "dobject.h" #include "i_system.h" +#include "farchive.h" #include "actor.h" #include "templates.h" #include "autosegs.h" @@ -384,6 +385,151 @@ bool PType::VisitedNodeSet::Check(const PType *node) return false; } +//========================================================================== +// +// PType :: WriteValue +// +//========================================================================== + +void PType::WriteValue(FArchive &ar, const void *addr) const +{ + assert(0 && "Cannot write value for this type"); +} + +//========================================================================== +// +// PType :: ReadValue +// +//========================================================================== + +bool PType::ReadValue(FArchive &ar, void *addr) const +{ + assert(0 && "Cannot read value for this type"); + SkipValue(ar); + return false; +} + +//========================================================================== +// +// PType :: SkipValue STATIC +// +//========================================================================== + +void PType::SkipValue(FArchive &ar) +{ + BYTE tag; + ar << tag; + SkipValue(ar, tag); +} + +void PType::SkipValue(FArchive &ar, int tag) +{ + assert(ar.IsLoading() && "SkipValue passed an archive that is writing"); + BYTE buff[8]; + + switch (tag) + { + case VAL_Zero: case VAL_One: + break; + + case VAL_Int8: case VAL_UInt8: + ar.Read(buff, 1); + break; + + case VAL_Int16: case VAL_UInt16: + ar.Read(buff, 2); + break; + + case VAL_Int32: case VAL_UInt32: case VAL_Float32: case VAL_Fixed: case VAL_BAM: + ar.Read(buff, 4); + break; + + case VAL_Int64: case VAL_UInt64: case VAL_Float64: + ar.Read(buff, 8); + break; + + case VAL_Name: + ar.ReadName(); + break; + + case VAL_Object: + { + DObject *skipper; + ar << skipper; + break; + } + case VAL_State: + { + FState *skipper; + ar << skipper; + break; + } + case VAL_String: + { + FString skipper; + ar << skipper; + break; + } + case VAL_Array: + { + DWORD count = ar.ReadCount(); + while (count-- > 0) + { + SkipValue(ar); + } + break; + } + case VAL_Struct: + { + const char *label; + for (label = ar.ReadName(); label != NULL; label = ar.ReadName()) + { + SkipValue(ar); + } + break; + } + case VAL_Class: + { + PClass *type; + for (ar.UserReadClass(type); type != NULL; ar.UserReadClass(type)) + { + SkipValue(ar, VAL_Struct); + } + break; + } + } +} + +//========================================================================== +// +// PType :: SetDefaultValue +// +//========================================================================== + +void PType::SetDefaultValue(void *base, unsigned offset, TArray *stroffs) const +{ +} + +//========================================================================== +// +// PType :: InitializeValue +// +//========================================================================== + +void PType::InitializeValue(void *addr, const void *def) const +{ +} + +//========================================================================== +// +// PType :: DestroyValue +// +//========================================================================== + +void PType::DestroyValue(void *addr) const +{ +} + //========================================================================== // // PType :: SetValue @@ -392,7 +538,12 @@ bool PType::VisitedNodeSet::Check(const PType *node) void PType::SetValue(void *addr, int val) { - assert(0 && "Cannot set value for this type"); + assert(0 && "Cannot set int value for this type"); +} + +void PType::SetValue(void *addr, double val) +{ + assert(0 && "Cannot set float value for this type"); } //========================================================================== @@ -407,6 +558,12 @@ int PType::GetValueInt(void *addr) const return 0; } +double PType::GetValueFloat(void *addr) const +{ + assert(0 && "Cannot get value for this type"); + return 0; +} + //========================================================================== // // PType :: GetStoreOp @@ -651,6 +808,177 @@ PInt::PInt(unsigned int size, bool unsign) } } +//========================================================================== +// +// PInt :: WriteValue +// +// Write the value using the minimum byte size needed to represent it. This +// means that the value as written is not necessarily of the same type as +// stored, but the signedness information is preserved. +// +//========================================================================== + +void PInt::WriteValue(FArchive &ar, const void *addr) const +{ + BYTE bval; + + // The process for bytes is the same whether signed or unsigned, since + // they can't be compacted into a representation with fewer bytes. + if (Size == 1) + { + bval = *(BYTE *)addr; + } + else if (Unsigned) + { + unsigned val; + if (Size == 8) + { + QWORD qval = *(QWORD *)addr; + if (qval & 0xFFFFFFFF00000000llu) + { // Value needs 64 bits + ar.WriteByte(VAL_UInt64); + ar.WriteInt64(qval); + return; + } + // Value can fit in 32 bits or less + val = (unsigned)qval; + goto check_u32; + } + else if (Size == 4) + { + val = *(DWORD *)addr; +check_u32: if (val & 0xFFFF0000u) + { // Value needs 32 bits + ar.WriteByte(VAL_UInt32); + ar.WriteInt32(val); + return; + } + // Value can fit in 16 bits or less + goto check_u16; + } + else// if (Size == 2) + { + val = *(WORD *)addr; +check_u16: if (val & 0xFFFFFF00u) + { // Value needs 16 bits + ar.WriteByte(VAL_UInt16); + ar.WriteInt16(val); + return; + } + // Value can fit in 8 bits + bval = (BYTE)val; + } + } + else // Signed + { + int val; + if (Size == 8) + { + SQWORD qval = *(SQWORD *)addr; + INT_MIN; + if (qval < (-0x7FFFFFFF - 1) || qval > 0x7FFFFFFF) + { // Value needs 64 bits + ar.WriteByte(VAL_Int64); + ar.WriteInt64(qval); + return; + } + // Value can fit in 32 bits or less + val = (int)qval; + goto check_s32; + } + else if (Size == 4) + { + val = *(SDWORD *)addr; +check_s32: if (val < -0x8000 || val > 0x7FFF) + { // Value needs 32 bits + ar.WriteByte(VAL_Int32); + ar.WriteInt32(val); + return; + } + // Value can fit in 16 bits or less + goto check_s16; + } + else// if (Size == 2) + { + val = *(SWORD *)addr; +check_s16: if (val < -0x80 || val > 0x7F) + { // Value needs 16 bits + ar.WriteByte(VAL_Int16); + ar.WriteInt16(val); + return; + } + // Value can fit in 8 bits + bval = (BYTE)val; + } + } + // If we get here, the value fits in a byte. Values of 0 and 1 are + // optimized away into the tag so they don't require any extra space + // to store. + if (bval & 0xFE) + { + BYTE out[2] = { Unsigned ? VAL_UInt8 : VAL_Int8, bval }; + ar.Write(out, 2); + } + else + { + ar.WriteByte(VAL_Zero + bval); + } +} + +//========================================================================== +// +// PInt :: ReadValue +// +//========================================================================== + +bool PInt::ReadValue(FArchive &ar, void *addr) const +{ + union + { + QWORD uval; + SQWORD sval; + }; + BYTE tag; + union + { + BYTE val8; + WORD val16; + DWORD val32; + fixed_t fix; + float single; + double dbl; + angle_t ang; + }; + + ar << tag; + switch (tag) + { + case VAL_Zero: uval = 0; break; + case VAL_One: uval = 1; break; + case VAL_Int8: ar << val8; sval = (SBYTE)val8; break; + case VAL_UInt8: ar << val8; uval = val8; break; + case VAL_Int16: ar << val16; sval = (SWORD)val16; break; + case VAL_UInt16: ar << val16; uval = val16; break; + case VAL_Int32: ar << val32; sval = (SDWORD)val32; break; + case VAL_UInt32: ar << val32; uval = val32; break; + case VAL_Int64: ar << sval; break; + case VAL_UInt64: ar << uval; break; + case VAL_Fixed: ar << fix; sval = fix >> FRACBITS; break; // fixed -> int + case VAL_BAM: ar << ang; uval = ang / ANGLE_1; break; // BAM -> degrees + case VAL_Float32: ar << single; sval = (SQWORD)single; break; + case VAL_Float64: ar << dbl; sval = (SQWORD)dbl; break; + default: SkipValue(ar, tag); return false; // Incompatible type + } + switch (Size) + { + case 1: *(BYTE *)addr = (BYTE)uval; break; + case 2: *(WORD *)addr = (WORD)uval; break; + case 4: *(DWORD *)addr = (DWORD)uval; break; + case 8: *(QWORD *)addr = uval; break; + } + return true; +} + //========================================================================== // // PInt :: SetValue @@ -682,6 +1010,11 @@ void PInt::SetValue(void *addr, int val) } } +void PInt::SetValue(void *addr, double val) +{ + SetValue(addr, (int)val); +} + //========================================================================== // // PInt :: GetValueInt @@ -714,6 +1047,17 @@ int PInt::GetValueInt(void *addr) const } } +//========================================================================== +// +// PInt :: GetValueFloat +// +//========================================================================== + +double PInt::GetValueFloat(void *addr) const +{ + return GetValueInt(addr); +} + //========================================================================== // // PInt :: GetStoreOp @@ -920,6 +1264,97 @@ void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) } } +//========================================================================== +// +// PFloat :: WriteValue +// +//========================================================================== + +void PFloat::WriteValue(FArchive &ar, const void *addr) const +{ + float singleprecision; + if (Size == 8) + { + // If it can be written as single precision without information + // loss, then prefer that over writing a full-sized double. + double doubleprecision = *(double *)addr; + singleprecision = (float)doubleprecision; + if (singleprecision != doubleprecision) + { + ar.WriteByte(VAL_Float64); + ar << doubleprecision; + } + } + else + { + singleprecision = *(float *)addr; + } + ar.WriteByte(VAL_Float32); + ar << singleprecision; +} + +//========================================================================== +// +// PFloat :: ReadValue +// +//========================================================================== + +static bool ReadValueDbl(FArchive &ar, double *addr, unsigned tag) +{ + double val; + union + { + BYTE val8; + WORD val16; + DWORD val32; + QWORD val64; + fixed_t fix; + float single; + angle_t ang; + }; + + switch (tag) + { + case VAL_Zero: val = 0; break; + case VAL_One: val = 1; break; + case VAL_Int8: ar << val8; val = (SBYTE)val8; break; + case VAL_UInt8: ar << val8; val = val8; break; + case VAL_Int16: ar << val16; val = (SWORD)val16; break; + case VAL_UInt16: ar << val16; val = val16; break; + case VAL_Int32: ar << val32; val = (SDWORD)val32; break; + case VAL_UInt32: ar << val32; val = val32; break; + case VAL_Int64: ar << val64; val = (double)(SQWORD)val64; break; + case VAL_UInt64: ar << val64; val = (double)val64; break; + case VAL_Fixed: ar << fix; val = FIXED2DBL(fix); break; + case VAL_BAM: ar << ang; val = ang * (90.0 / ANGLE_90); break; // BAM -> degrees + case VAL_Float32: ar << single; val = single; break; + case VAL_Float64: ar << val; break; + default: PType::SkipValue(ar, tag); return false; // Incompatible type + } + *(double *)addr = val; + return true; +} + +bool PFloat::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + double val; + if (ReadValueDbl(ar, &val, tag)) + { + if (Size == 4) + { + *(float *)addr = (float)val; + } + else + { + *(double *)addr = val; + } + return true; + } + return false; +} + //========================================================================== // // PFloat :: SetValue @@ -927,6 +1362,11 @@ void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) //========================================================================== void PFloat::SetValue(void *addr, int val) +{ + return SetValue(addr, (double)val); +} + +void PFloat::SetValue(void *addr, double val) { assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); if (Size == 4) @@ -947,16 +1387,27 @@ void PFloat::SetValue(void *addr, int val) //========================================================================== int PFloat::GetValueInt(void *addr) const +{ + return xs_ToInt(GetValueFloat(addr)); +} + +//========================================================================== +// +// PFloat :: GetValueFloat +// +//========================================================================== + +double PFloat::GetValueFloat(void *addr) const { assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); if (Size == 4) { - return xs_ToInt(*(float *)addr); + return *(float *)addr; } else { assert(Size == 8); - return xs_ToInt(*(double *)addr); + return *(double *)addr; } } @@ -1037,6 +1488,82 @@ int PString::GetRegType() const return REGT_STRING; } +//========================================================================== +// +// PString :: WriteValue +// +//========================================================================== + +void PString::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_String); + ar.WriteString(*(const FString *)addr); +} + +//========================================================================== +// +// PString :: ReadValue +// +//========================================================================== + +bool PString::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_String) + { + ar << *(FString *)addr; + } + else if (tag == VAL_Name) + { + const char *str = ar.ReadName(); + *(FString *)addr = str; + } + else + { + SkipValue(ar, tag); + return false; + } + return true; +} + +//========================================================================== +// +// PString :: SetDefaultValue +// +//========================================================================== + +void PString::SetDefaultValue(void *base, unsigned offset, TArray *special) const +{ + new((BYTE *)base + offset) FString; + if (special != NULL) + { + special->Push(std::make_pair(this, offset)); + } +} + +//========================================================================== +// +// PString :: InitializeValue +// +//========================================================================== + +void PString::InitializeValue(void *addr, const void *def) const +{ + new(addr) FString(*(FString *)def); +} + +//========================================================================== +// +// PString :: DestroyValue +// +//========================================================================== + +void PString::DestroyValue(void *addr) const +{ + ((FString *)addr)->~FString(); +} + /* PName ******************************************************************/ IMPLEMENT_CLASS(PName) @@ -1053,6 +1580,46 @@ PName::PName() assert(sizeof(FName) == __alignof(FName)); } +//========================================================================== +// +// PName :: WriteValue +// +//========================================================================== + +void PName::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Name); + ar.WriteName(((const FName *)addr)->GetChars()); +} + +//========================================================================== +// +// PName :: ReadValue +// +//========================================================================== + +bool PName::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Name) + { + *(FName *)addr = FName(ar.ReadName()); + } + else if (tag == VAL_String) + { + FString str; + ar << str; + *(FName *)addr = FName(str); + } + else + { + SkipValue(ar, tag); + return false; + } + return true; +} + /* PSound *****************************************************************/ IMPLEMENT_CLASS(PSound) @@ -1069,6 +1636,48 @@ PSound::PSound() assert(sizeof(FSoundID) == __alignof(FSoundID)); } +//========================================================================== +// +// PSound :: WriteValue +// +//========================================================================== + +void PSound::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Name); + ar.WriteName(*(const FSoundID *)addr); +} + +//========================================================================== +// +// PSound :: ReadValue +// +//========================================================================== + +bool PSound::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + + ar << tag; + if (tag == VAL_Name) + { + const char *str = ar.ReadName(); + *(FSoundID *)addr = FSoundID(str); + } + else if (tag == VAL_String) + { + FString str; + ar << str; + *(FSoundID *)addr = FSoundID(str); + } + else + { + SkipValue(ar, tag); + return false; + } + return true; +} + /* PColor *****************************************************************/ IMPLEMENT_CLASS(PColor) @@ -1100,6 +1709,45 @@ PFixed::PFixed() { } +//========================================================================== +// +// PFixed :: WriteValue +// +//========================================================================== + +void PFixed::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Fixed); + ar << *(fixed_t *)addr; +} + +//========================================================================== +// +// PFixed :: ReadValue +// +//========================================================================== + +bool PFixed::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Fixed) + { + ar << *(fixed_t *)addr; + return true; + } + else + { + double val; + if (ReadValueDbl(ar, &val, tag)) + { + *(fixed_t *)addr = FLOAT2FIXED(val); + return true; + } + return false; + } +} + //========================================================================== // // PFixed :: SetValue @@ -1112,6 +1760,12 @@ void PFixed::SetValue(void *addr, int val) *(fixed_t *)addr = val << FRACBITS; } +void PFixed::SetValue(void *addr, double val) +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + *(fixed_t *)addr = FLOAT2FIXED(val); +} + //========================================================================== // // PFixed :: GetValueInt @@ -1124,6 +1778,18 @@ int PFixed::GetValueInt(void *addr) const return *(fixed_t *)addr >> FRACBITS; } +//========================================================================== +// +// PFixed :: GetValueFloat +// +//========================================================================== + +double PFixed::GetValueFloat(void *addr) const +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + return FIXED2DBL(*(fixed_t *)addr); +} + //========================================================================== // // PFixed :: GetStoreOp @@ -1161,6 +1827,45 @@ PAngle::PAngle() { } +//========================================================================== +// +// PAngle :: WriteValue +// +//========================================================================== + +void PAngle::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_BAM); + ar.WriteInt32(*(angle_t *)addr); +} + +//========================================================================== +// +// PAngle :: ReadValue +// +//========================================================================== + +bool PAngle::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_BAM) + { + ar << *(angle_t *)addr; + return true; + } + else + { + double val; + if (ReadValueDbl(ar, &val, tag)) + { + *(angle_t *)addr = FLOAT2ANGLE(val); + return true; + } + return false; + } +} + //========================================================================== // // PAngle :: SetValue @@ -1173,6 +1878,12 @@ void PAngle::SetValue(void *addr, int val) *(angle_t *)addr = Scale(val, ANGLE_90, 90); } +void PAngle::SetValue(void *addr, double val) +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + *(angle_t *)addr = (angle_t)(val * ANGLE_90 / 90); +} + //========================================================================== // // PAngle :: GetValueInt @@ -1185,6 +1896,18 @@ int PAngle::GetValueInt(void *addr) const return *(angle_t *)addr / ANGLE_1; } +//========================================================================== +// +// PAngle :: GetValueFloat +// +//========================================================================== + +double PAngle::GetValueFloat(void *addr) const +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + return (double)(*(angle_t *)addr) / ANGLE_1; +} + //========================================================================== // // PAngle :: GetStoreOp @@ -1255,6 +1978,37 @@ int PStatePointer::GetRegType() const return REGT_POINTER; } +//========================================================================== +// +// PStatePointer :: WriteValue +// +//========================================================================== + +void PStatePointer::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_State); + ar << *(FState **)addr; +} + +//========================================================================== +// +// PStatePointer :: ReadValue +// +//========================================================================== + +bool PStatePointer::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_State) + { + ar << *(FState **)addr; + return true; + } + SkipValue(ar, tag); + return false; +} + /* PPointer ***************************************************************/ IMPLEMENT_POINTY_CLASS(PPointer) @@ -1342,6 +2096,45 @@ void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const id2 = 0; } +//========================================================================== +// +// PPointer :: WriteValue +// +//========================================================================== + +void PPointer::WriteValue(FArchive &ar, const void *addr) const +{ + if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) + { + ar.WriteByte(VAL_Object); + ar << *(DObject **)addr; + } + else + { + assert(0 && "Pointer points to a type we don't handle"); + I_Error("Attempt to save pointer to unhandled type"); + } +} + +//========================================================================== +// +// PPointer :: ReadValue +// +//========================================================================== + +bool PPointer::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Object && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) + { + ar << *(DObject **)addr; + return true; + } + SkipValue(ar, tag); + return false; +} + //========================================================================== // // NewPointer @@ -1547,6 +2340,75 @@ void PArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const id2 = ElementCount; } +//========================================================================== +// +// PArray :: WriteValue +// +//========================================================================== + +void PArray::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Array); + ar.WriteCount(ElementCount); + const BYTE *addrb = (const BYTE *)addr; + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->WriteValue(ar, addrb); + addrb += ElementSize; + } +} + +//========================================================================== +// +// PArray :: ReadValue +// +//========================================================================== + +bool PArray::ReadValue(FArchive &ar, void *addr) const +{ + bool readsomething = false; + BYTE tag; + + ar << tag; + if (tag == VAL_Array) + { + unsigned count = ar.ReadCount(); + unsigned i; + BYTE *addrb = (BYTE *)addr; + for (i = 0; i < MIN(count, ElementCount); ++i) + { + readsomething |= ElementType->ReadValue(ar, addrb); + addrb += ElementSize; + } + if (i < ElementCount) + { + DPrintf("Array on disk (%u) is bigger than in memory (%u)\n", + count, ElementCount); + for (; i < ElementCount; ++i) + { + SkipValue(ar); + } + } + return readsomething; + } + SkipValue(ar, tag); + return false; +} + +//========================================================================== +// +// PArray :: SetDefaultValue +// +//========================================================================== + +void PArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const +{ + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->SetDefaultValue(base, offset + i*ElementSize, special); + } +} + //========================================================================== // // NewArray @@ -1800,6 +2662,112 @@ PStruct::PStruct(FName name, DObject *outer) { } +//========================================================================== +// +// PStruct :: SetDefaultValue +// +//========================================================================== + +void PStruct::SetDefaultValue(void *base, unsigned offset, TArray *special) const +{ + for (const PField *field : Fields) + { + if (!(field->Flags & VARF_Native)) + { + field->Type->SetDefaultValue(base, offset + field->Offset, special); + } + } +} + +//========================================================================== +// +// PStruct :: WriteValue +// +//========================================================================== + +void PStruct::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Struct); + WriteFields(ar, addr, Fields); +} + +//========================================================================== +// +// PStruct :: ReadValue +// +//========================================================================== + +bool PStruct::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Struct) + { + return ReadFields(ar, addr); + } + SkipValue(ar, tag); + return true; +} + +//========================================================================== +// +// PStruct :: WriteFields STATIC +// +//========================================================================== + +void PStruct::WriteFields(FArchive &ar, const void *addr, const TArray &fields) +{ + for (unsigned i = 0; i < fields.Size(); ++i) + { + const PField *field = fields[i]; + // Skip fields with native serialization + if (!(field->Flags & VARF_Native)) + { + ar.WriteName(field->SymbolName); + field->Type->WriteValue(ar, (const BYTE *)addr + field->Offset); + } + } + ar.WriteName(NULL); +} + +//========================================================================== +// +// PStruct :: ReadFields +// +//========================================================================== + +bool PStruct::ReadFields(FArchive &ar, void *addr) const +{ + bool readsomething = false; + const char *label = ar.ReadName(); + if (label == NULL) + { // If there is nothing to restore, we count it as success. + return true; + } + for (; label != NULL; label = ar.ReadName()) + { + const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); + if (sym == NULL) + { + DPrintf("Cannot find field %s in %s\n", + label, TypeName.GetChars()); + SkipValue(ar); + } + else if (!sym->IsKindOf(RUNTIME_CLASS(PField))) + { + DPrintf("Symbol %s in %s is not a field\n", + label, TypeName.GetChars()); + SkipValue(ar); + } + else + { + readsomething |= static_cast(sym)->Type->ReadValue(ar, + (BYTE *)addr + static_cast(sym)->Offset); + } + } + return readsomething; +} + //========================================================================== // // PStruct :: AddField @@ -2012,6 +2980,89 @@ IMPLEMENT_POINTY_CLASS(PClass) DECLARE_POINTER(ParentClass) END_POINTERS +//========================================================================== +// +// PClass :: WriteValue +// +// Similar to PStruct's version, except it also needs to traverse parent +// classes. +// +//========================================================================== + +static void RecurseWriteFields(const PClass *type, FArchive &ar, const void *addr) +{ + if (type != NULL) + { + RecurseWriteFields(type->ParentClass, ar, addr); + // Don't write this part if it has no non-native variables + for (unsigned i = 0; i < type->Fields.Size(); ++i) + { + if (!(type->Fields[i]->Flags & VARF_Native)) + { + // Tag this section with the class it came from in case + // a more-derived class has variables that shadow a less- + // derived class. Whether or not that is a language feature + // that will actually be allowed remains to be seen. + ar.UserWriteClass(const_cast(type)); + PStruct::WriteFields(ar, addr, type->Fields); + break; + } + } + } +} + +void PClass::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Class); + RecurseWriteFields(this, ar, addr); + ar.UserWriteClass(NULL); +} + +//========================================================================== +// +// PClass :: ReadValue +// +//========================================================================== + +bool PClass::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag != VAL_Class) + { + SkipValue(ar, tag); + return false; + } + else + { + bool readsomething = false; + PClass *type; + for (ar.UserReadClass(type); type != NULL; ar.UserReadClass(type)) + { + // Only read it if the type is related to this one. + const PClass *parent; + for (parent = this; parent != NULL; parent = parent->ParentClass) + { + if (parent == type) + { + break; + } + } + if (parent != NULL) + { + readsomething |= type->ReadFields(ar, addr); + } + else + { + DPrintf("Unknown superclass %s of class %s\n", + type->TypeName.GetChars(), TypeName.GetChars()); + SkipValue(ar, VAL_Struct); + } + } + return readsomething; + } +} + //========================================================================== // // cregcmp @@ -2275,6 +3326,26 @@ void PClass::InsertIntoHash () } } +//========================================================================== +// +// PClass :: FindParentClass +// +// Finds a parent class that matches the given name, including itself. +// +//========================================================================== + +const PClass *PClass::FindParentClass(FName name) const +{ + for (const PClass *type = this; type != NULL; type = type->ParentClass) + { + if (type->TypeName == name) + { + return type; + } + } + return NULL; +} + //========================================================================== // // PClass :: FindClass @@ -2314,9 +3385,50 @@ DObject *PClass::CreateNew() const ConstructNative (mem); ((DObject *)mem)->SetClass (const_cast(this)); + if (Defaults != NULL) + { + InitializeSpecials(mem); + } return (DObject *)mem; } +//========================================================================== +// +// PClass :: InitializeSpecials +// +// Initialize special fields of a newly-created instance (e.g. strings). +// +//========================================================================== + +void PClass::InitializeSpecials(void *addr) const +{ + if (ParentClass != NULL) + { + ParentClass->InitializeSpecials(addr); + } + for (auto tao : SpecialInits) + { + tao.first->InitializeValue((BYTE*)addr + tao.second, Defaults + tao.second); + } +} + +//========================================================================== +// +// PClass :: DestroySpecials +// +//========================================================================== + +void PClass::DestroySpecials(void *addr) const +{ + if (ParentClass != NULL) + { + ParentClass->DestroySpecials(addr); + } + for (auto tao : SpecialInits) + { + tao.first->DestroyValue((BYTE *)addr + tao.second); + } +} //========================================================================== // // PClass :: Derive @@ -2403,22 +3515,25 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) //========================================================================== // -// PClass:: Extend -// -// Add bytes to the end of this class. Returns the previous -// size of the class. +// PClass :: AddField // //========================================================================== -unsigned int PClass::Extend(unsigned int extension) +PField *PClass::AddField(FName name, PType *type, DWORD flags) { - assert(this->bRuntimeClass); - - unsigned int oldsize = Size; - Size += extension; - Defaults = (BYTE *)M_Realloc(Defaults, Size); - memset(Defaults + oldsize, 0, extension); - return oldsize; + unsigned oldsize = Size; + PField *field = Super::AddField(name, type, flags); + if (field != NULL) + { + Defaults = (BYTE *)M_Realloc(Defaults, Size); + memset(Defaults + oldsize, 0, Size - oldsize); + // If this is a native class, then we must not initialize and + // destroy any of its members. We do, however, initialize the + // default instance since it's not a normal instance of the class. + type->SetDefaultValue(Defaults, field->Offset, + bRuntimeClass ? &SpecialInits : NULL); + } + return field; } //========================================================================== diff --git a/src/dobjtype.h b/src/dobjtype.h index b482efe457..fe04bc6343 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -7,6 +7,8 @@ #include "vm.h" +typedef std::pair FTypeAndOffset; + // Variable/parameter/field flags ------------------------------------------- // Making all these different storage types use a common set of flags seems @@ -186,11 +188,43 @@ public: int FindConversion(PType *target, const Conversion **slots, int numslots); + // Writes the value of a variable of this type at (addr) to an archive, preceded by + // a tag indicating its type. The tag is there so that variable types can be changed + // without completely breaking savegames, provided that the change isn't between + // totally unrelated types. + virtual void WriteValue(FArchive &ar, const void *addr) const; + + // Returns true if the stored value was compatible. False otherwise. + // If the value was incompatible, then the memory at *addr is unchanged. + virtual bool ReadValue(FArchive &ar, void *addr) const; + + // Skips over a value written with WriteValue + static void SkipValue(FArchive &ar); + static void SkipValue(FArchive &ar, int tag); + + // Sets the default value for this type at (base + offset) + // If the default value is binary 0, then this function doesn't need + // to do anything, because PClass::Extend() takes care of that. + // + // The stroffs array is so that types that need special initialization + // and destruction (e.g. strings) can add their offsets to it for special + // initialization when the object is created and destruction when the + // object is destroyed. + virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const; + + // Initialize the value, if needed (e.g. strings) + virtual void InitializeValue(void *addr, const void *def) const; + + // Destroy the value, if needed (e.g. strings) + virtual void DestroyValue(void *addr) const; + // Sets the value of a variable of this type at (addr) virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); // Gets the value of a variable of this type at (addr) virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; // Gets the opcode to store from a register to memory virtual int GetStoreOp() const; @@ -319,8 +353,13 @@ class PInt : public PBasicType public: PInt(unsigned int size, bool unsign); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; virtual int GetRegType() const; @@ -343,8 +382,13 @@ class PFloat : public PBasicType public: PFloat(unsigned int size); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; virtual int GetRegType() const; @@ -375,6 +419,12 @@ public: PString(); virtual int GetRegType() const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; }; // Variations of integer types ---------------------------------------------- @@ -384,6 +434,9 @@ class PName : public PInt DECLARE_CLASS(PName, PInt); public: PName(); + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; }; class PSound : public PInt @@ -391,6 +444,9 @@ class PSound : public PInt DECLARE_CLASS(PSound, PInt); public: PSound(); + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; }; class PColor : public PInt @@ -409,8 +465,13 @@ class PFixed : public PFloat public: PFixed(); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; }; @@ -421,8 +482,13 @@ class PAngle : public PFloat public: PAngle(); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; }; @@ -435,6 +501,9 @@ class PStatePointer : public PBasicType public: PStatePointer(); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual int GetStoreOp() const; virtual int GetLoadOp() const; virtual int GetRegType() const; @@ -455,6 +524,10 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + protected: PPointer(); }; @@ -521,6 +594,12 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + + void SetDefaultValue(void *base, unsigned offset, TArray *special) const override; + protected: PArray(); }; @@ -575,9 +654,16 @@ public: TArray Fields; - PField *AddField(FName name, PType *type, DWORD flags=0); + virtual PField *AddField(FName name, PType *type, DWORD flags=0); size_t PropagateMark(); + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) const override; + + static void WriteFields(FArchive &ar, const void *addr, const TArray &fields); + bool ReadFields(FArchive &ar, void *addr) const; protected: PStruct(); }; @@ -635,11 +721,16 @@ class PClass : public PStruct protected: // We unravel _WITH_META here just as we did for PType. enum { MetaClassNum = CLASSREG_PClassClass }; + TArray SpecialInits; virtual void Derive(PClass *newclass); + void InitializeSpecials(void *addr) const; public: typedef PClassClass MetaClass; MetaClass *GetClass() const; + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void DeriveData(PClass *newclass) {} static void StaticInit(); static void StaticShutdown(); @@ -660,9 +751,10 @@ public: void InsertIntoHash(); DObject *CreateNew() const; PClass *CreateDerivedClass(FName name, unsigned int size); - unsigned int Extend(unsigned int extension); + PField *AddField(FName name, PType *type, DWORD flags=0) override; void InitializeActorInfo(); void BuildFlatPointers(); + void DestroySpecials(void *addr) const; const PClass *NativeClass() const; // Returns true if this type is an ancestor of (or same as) the passed type. @@ -682,6 +774,9 @@ public: } // Find a type, given its name. + const PClass *FindParentClass(FName name) const; + PClass *FindParentClass(FName name) { return const_cast(const_cast(this)->FindParentClass(name)); } + static PClass *FindClass(const char *name) { return FindClass(FName(name, true)); } static PClass *FindClass(const FString &name) { return FindClass(FName(name, true)); } static PClass *FindClass(ENamedName name) { return FindClass(FName(name)); } @@ -832,4 +927,31 @@ public: void ReleaseGlobalSymbols(); +// Enumerations for serializing types in an archive ------------------------- + +enum ETypeVal : BYTE +{ + VAL_Int8, + VAL_UInt8, + VAL_Int16, + VAL_UInt16, + VAL_Int32, + VAL_UInt32, + VAL_Int64, + VAL_UInt64, + VAL_Zero, + VAL_One, + VAL_Float32, + VAL_Float64, + VAL_Fixed, + VAL_BAM, + VAL_String, + VAL_Name, + VAL_Struct, + VAL_Array, + VAL_Object, + VAL_State, + VAL_Class, +}; + #endif diff --git a/src/doomstat.cpp b/src/doomstat.cpp index 8f314ad506..d693aae1b9 100644 --- a/src/doomstat.cpp +++ b/src/doomstat.cpp @@ -43,7 +43,6 @@ CVAR (Bool, developer, false, 0) // [RH] Feature control cvars CVAR (Bool, var_friction, true, CVAR_SERVERINFO); -CVAR (Bool, var_pushers, true, CVAR_SERVERINFO); CVAR (Bool, alwaysapplydmflags, false, CVAR_SERVERINFO); diff --git a/src/doomstat.h b/src/doomstat.h index 43d81e8666..e762b07dc2 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -212,7 +212,6 @@ extern bool ToggleFullscreen; extern int Net_Arbitrator; EXTERN_CVAR (Bool, var_friction) -EXTERN_CVAR (Bool, var_pushers) // [RH] Miscellaneous info for DeHackEd support diff --git a/src/dthinker.h b/src/dthinker.h index c7b92730c8..10a53309d6 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -42,6 +42,7 @@ class AActor; class player_t; struct pspdef_s; struct FState; +class DThinker; class FThinkerIterator; diff --git a/src/farchive.cpp b/src/farchive.cpp index 8fd647fd29..1f805ae8ec 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -721,6 +721,29 @@ void FArchive::Close () } } +void FArchive::WriteByte(BYTE val) +{ + m_File->Write(&val, 1); +} + +void FArchive::WriteInt16(WORD val) +{ + WORD out = LittleShort(val); + m_File->Write(&out, 2); +} + +void FArchive::WriteInt32(DWORD val) +{ + int out = LittleLong(val); + m_File->Write(&out, 4); +} + +void FArchive::WriteInt64(QWORD val) +{ + long long out = SWAP_QWORD(val); + m_File->Write(&out, 8); +} + void FArchive::WriteCount (DWORD count) { BYTE out; @@ -832,6 +855,14 @@ void FArchive::WriteString (const char *str) } } +void FArchive::WriteString(const FString &str) +{ + // The count includes the '\0' terminator, but we don't + // actually write it out. + WriteCount(str.Len() + 1); + Write(str, str.Len()); +} + FArchive &FArchive::operator<< (char *&str) { if (m_Storing) @@ -868,7 +899,7 @@ FArchive &FArchive::operator<< (FString &str) { if (m_Storing) { - WriteString (str.GetChars()); + WriteString (str); } else { @@ -883,8 +914,7 @@ FArchive &FArchive::operator<< (FString &str) char *str2 = (char *)alloca(size*sizeof(char)); size--; Read (str2, size); - str2[size] = 0; - str = str2; + str = FString(str2, size); } } return *this; diff --git a/src/farchive.h b/src/farchive.h index b646827def..2324c6b2ac 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -166,7 +166,14 @@ public: virtual void Write (const void *mem, unsigned int len); virtual void Read (void *mem, unsigned int len); + void WriteString(const FString &str); void WriteString (const char *str); + + void WriteByte(BYTE val); + void WriteInt16(WORD val); + void WriteInt32(DWORD val); + void WriteInt64(QWORD val); + void WriteCount (DWORD count); DWORD ReadCount (); diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 953297b317..bfda35ad4e 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -56,7 +56,6 @@ #include "c_console.h" #include "c_dispatch.h" #include "d_player.h" -#include "a_doomglobal.h" #include "w_wad.h" #include "gi.h" #include "zstring.h" diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index b43b5a1d50..b47518f690 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -84,6 +84,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePistol) return 0; P_SetPsprite (self->player, ps_flash, weapon->FindState(NAME_Flash)); + self->player->psprites[ps_flash].processPending = true; } self->player->mo->PlayAttacking2 (); @@ -275,6 +276,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return 0; P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); + self->player->psprites[ps_flash].processPending = true; } player->mo->PlayAttacking2 (); @@ -311,6 +313,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun2) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 2)) return 0; P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); + self->player->psprites[ps_flash].processPending = true; } player->mo->PlayAttacking2 (); @@ -384,12 +387,14 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i { // we're ok so set the state P_SetPsprite (player, ps_flash, flashstate + index); + player->psprites[ps_flash].processPending = true; return; } else { // oh, no! The state is beyond the end of the state table so use the original flash state. P_SetPsprite (player, ps_flash, flashstate); + player->psprites[ps_flash].processPending = true; return; } } @@ -406,6 +411,7 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i index = 0; } P_SetPsprite (player, ps_flash, flashstate + index); + player->psprites[ps_flash].processPending = true; } // diff --git a/src/g_level.cpp b/src/g_level.cpp index 74e576056d..2283fbd128 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -2025,7 +2025,7 @@ void FLevelLocals::Tick () // //========================================================================== -void FLevelLocals::AddScroller (DScroller *scroller, int secnum) +void FLevelLocals::AddScroller (int secnum) { if (secnum < 0) { diff --git a/src/g_level.h b/src/g_level.h index ca70b654b3..17a2a1920c 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -383,7 +383,7 @@ struct FSectorScrollValues struct FLevelLocals { void Tick (); - void AddScroller (DScroller *, int secnum); + void AddScroller (int secnum); int time; // time in the hub int maptime; // time in the map diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 2e1c5fa163..1a9211b4c0 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -37,7 +37,7 @@ DEarthquake::DEarthquake() DEarthquake::DEarthquake (AActor *center, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesound, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ) + double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint) : DThinker(STAT_EARTHQUAKE) { m_QuakeSFX = quakesound; @@ -54,6 +54,9 @@ DEarthquake::DEarthquake (AActor *center, int intensityX, int intensityY, int in m_WaveSpeedX = (float)waveSpeedX; m_WaveSpeedY = (float)waveSpeedY; m_WaveSpeedZ = (float)waveSpeedZ; + m_Falloff = falloff << FRACBITS; + m_Highpoint = highpoint; + m_MiniCount = highpoint; } //========================================================================== @@ -97,6 +100,14 @@ void DEarthquake::Serialize (FArchive &arc) { arc << m_WaveSpeedX << m_WaveSpeedY << m_WaveSpeedZ; } + if (SaveVersion < 4534) + { + m_Falloff = m_Highpoint = m_MiniCount = 0; + } + else + { + arc << m_Falloff << m_Highpoint << m_MiniCount; + } } //========================================================================== @@ -122,6 +133,7 @@ void DEarthquake::Tick () { S_Sound (m_Spot, CHAN_BODY | CHAN_LOOP, m_QuakeSFX, 1, ATTN_NORM); } + if (m_DamageRadius > 0) { for (i = 0; i < MAXPLAYERS; i++) @@ -158,6 +170,8 @@ void DEarthquake::Tick () } } + if (m_MiniCount > 0) + m_MiniCount--; if (--m_Countdown == 0) { if (S_IsActorPlayingSomething(m_Spot, CHAN_BODY, m_QuakeSFX)) @@ -168,12 +182,18 @@ void DEarthquake::Tick () } } +//========================================================================== +// +// DEarthquake :: GetModWave +// +// QF_WAVE converts intensity into amplitude and unlocks a new property, the +// wave length. This is, in short, waves per second. Named waveMultiplier +// because that's as the name implies: adds more waves per second. +// +//========================================================================== + fixed_t DEarthquake::GetModWave(double waveMultiplier) const { - //QF_WAVE converts intensity into amplitude and unlocks a new property, the wave length. - //This is, in short, waves per second (full cycles, mind you, from 0 to 360.) - //Named waveMultiplier because that's as the name implies: adds more waves per second. - double time = m_Countdown - FIXED2DBL(r_TicFrac); return FLOAT2FIXED(sin(waveMultiplier * time * (M_PI * 2 / TICRATE))); } @@ -189,35 +209,95 @@ fixed_t DEarthquake::GetModWave(double waveMultiplier) const fixed_t DEarthquake::GetModIntensity(fixed_t intensity) const { assert(m_CountdownStart >= m_Countdown); + intensity += intensity; // always doubled if (m_Flags & (QF_SCALEDOWN | QF_SCALEUP)) { + // Adjustable maximums must use a range between 1 and m_CountdownStart to constrain between no quake and full quake. + bool check = !!(m_Highpoint > 0 && m_Highpoint < m_CountdownStart); + int divider = (check) ? m_Highpoint : m_CountdownStart; int scalar; + if ((m_Flags & (QF_SCALEDOWN | QF_SCALEUP)) == (QF_SCALEDOWN | QF_SCALEUP)) { - scalar = (m_Flags & QF_MAX) ? MAX(m_Countdown, m_CountdownStart - m_Countdown) - : MIN(m_Countdown, m_CountdownStart - m_Countdown); + if (check) + { + if (m_MiniCount > 0) + scalar = (m_Flags & QF_MAX) ? m_MiniCount : (m_Highpoint - m_MiniCount); + else + { + divider = m_CountdownStart - m_Highpoint; + scalar = (m_Flags & QF_MAX) ? (divider - m_Countdown) : m_Countdown; + } + } + else + { + // Defaults to middle of the road. + divider = m_CountdownStart; + scalar = (m_Flags & QF_MAX) ? MAX(m_Countdown, m_CountdownStart - m_Countdown) + : MIN(m_Countdown, m_CountdownStart - m_Countdown); + } + scalar = (scalar > divider) ? divider : scalar; if (m_Flags & QF_FULLINTENSITY) { scalar *= 2; } } - else if (m_Flags & QF_SCALEDOWN) + else { - scalar = m_Countdown; - } - else // QF_SCALEUP - { - scalar = m_CountdownStart - m_Countdown; - } - assert(m_CountdownStart > 0); - intensity = Scale(intensity, scalar, m_CountdownStart); + if (m_Flags & QF_SCALEDOWN) + { + scalar = m_Countdown; + } + else // QF_SCALEUP + { + scalar = m_CountdownStart - m_Countdown; + if (m_Highpoint > 0) + { + if ((m_Highpoint - m_MiniCount) < divider) + scalar = m_Highpoint - m_MiniCount; + else + scalar = divider; + } + } + scalar = (scalar > divider) ? divider : scalar; + } + assert(divider > 0); + intensity = Scale(intensity, scalar, divider); } return intensity; } +//========================================================================== +// +// DEarthquake :: GetFalloff +// +// Given the distance of the player from the quake, find the multiplier. +// Process everything as doubles, and output again as fixed_t (mainly +// because fixed_t was misbehaving here...) +// +//========================================================================== + +fixed_t DEarthquake::GetFalloff(fixed_t dist) const +{ + if ((dist < m_Falloff) || (m_Falloff >= m_TremorRadius) || (m_Falloff <= 0) || (m_TremorRadius - m_Falloff <= 0)) + { //Player inside the minimum falloff range, or safety check kicked in. + return FRACUNIT; + } + else if ((dist > m_Falloff) && (dist < m_TremorRadius)) + { //Player inside the radius, and outside the min distance for falloff. + fixed_t tremorsize = m_TremorRadius - m_Falloff; + assert(tremorsize > 0); + return (FRACUNIT - FixedDiv((dist - m_Falloff), tremorsize)); + } + else + { //Shouldn't happen. + return FRACUNIT; + } +} + //========================================================================== // // DEarthquake::StaticGetQuakeIntensity @@ -247,12 +327,16 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger fixed_t dist = quake->m_Spot->AproxDistance (victim, true); if (dist < quake->m_TremorRadius) { + fixed_t falloff = quake->GetFalloff(dist); ++count; fixed_t x = quake->GetModIntensity(quake->m_IntensityX); fixed_t y = quake->GetModIntensity(quake->m_IntensityY); fixed_t z = quake->GetModIntensity(quake->m_IntensityZ); + + if (!(quake->m_Flags & QF_WAVE)) { + jiggers.Falloff = MAX(falloff, jiggers.Falloff); if (quake->m_Flags & QF_RELATIVE) { jiggers.RelIntensityX = MAX(x, jiggers.RelIntensityX); @@ -268,6 +352,7 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger } else { + jiggers.WFalloff = MAX(falloff, jiggers.WFalloff); fixed_t mx = FixedMul(x, quake->GetModWave(quake->m_WaveSpeedX)); fixed_t my = FixedMul(y, quake->GetModWave(quake->m_WaveSpeedY)); fixed_t mz = FixedMul(z, quake->GetModWave(quake->m_WaveSpeedZ)); @@ -304,7 +389,7 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ) + double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint) { AActor *center; bool res = false; @@ -318,7 +403,7 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, if (activator != NULL) { new DEarthquake(activator, intensityX, intensityY, intensityZ, duration, damrad, tremrad, - quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ); + quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint); return true; } } @@ -329,7 +414,7 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, { res = true; new DEarthquake(center, intensityX, intensityY, intensityZ, duration, damrad, tremrad, - quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ); + quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint); } } @@ -337,6 +422,6 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, } bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx) -{ //Maintains original behavior by passing 0 to intensityZ, and flags. - return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0); +{ //Maintains original behavior by passing 0 to intensityZ, flags, and everything else after QSFX. + return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0, 0, 0); } diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 1e59bf740d..1d5fd4ba5b 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -157,6 +157,7 @@ struct FQuakeJiggers int RelIntensityX, RelIntensityY, RelIntensityZ; int OffsetX, OffsetY, OffsetZ; int RelOffsetX, RelOffsetY, RelOffsetZ; + int Falloff, WFalloff; }; class DEarthquake : public DThinker @@ -166,7 +167,7 @@ class DEarthquake : public DThinker public: DEarthquake(AActor *center, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ); + double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint); void Serialize (FArchive &arc); void Tick (); @@ -178,9 +179,12 @@ public: int m_Flags; fixed_t m_IntensityX, m_IntensityY, m_IntensityZ; float m_WaveSpeedX, m_WaveSpeedY, m_WaveSpeedZ; + fixed_t m_Falloff; + int m_Highpoint, m_MiniCount; fixed_t GetModIntensity(int intensity) const; fixed_t GetModWave(double waveMultiplier) const; + fixed_t GetFalloff(fixed_t dist) const; static int StaticGetQuakeIntensities(AActor *viewer, FQuakeJiggers &jiggers); diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 23733efcfd..2fcc29b050 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -206,67 +206,31 @@ class SBarInfoCommandFlowControl : public SBarInfoCommand { public: SBarInfoCommandFlowControl(SBarInfo *script) : SBarInfoCommand(script), truth(false) {} - ~SBarInfoCommandFlowControl() - { - for(unsigned int i = 0;i < 2;i++) - { - for(unsigned int j = 0;j < commands[i].Size();j++) - delete commands[i][j]; - } - } void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) { - for(unsigned int i = 0;i < commands[truth].Size();i++) - commands[truth][i]->Draw(block, statusBar); + for(auto command : commands[truth]) + command->Draw(block, statusBar); } int NumCommands() const { return commands[truth].Size(); } void Parse(FScanner &sc, bool fullScreenOffsets) { - bool elseBlock = false; - SBarInfoCommand *cmd = NULL; - // Should loop no more than twice. - while(true) - { - if(sc.CheckToken('{')) - { - while((cmd = NextCommand(sc)) != NULL) - { - cmd->Parse(sc, fullScreenOffsets); - commands[!elseBlock].Push(cmd); - } - } - else - { - if((cmd = NextCommand(sc)) != NULL) - { - cmd->Parse(sc, fullScreenOffsets); - commands[!elseBlock].Push(cmd); - } - else - sc.ScriptError("Missing command for flow control statement."); - } - - if(!elseBlock && sc.CheckToken(TK_Else)) - { - elseBlock = true; - continue; - } - break; - } + ParseBlock(commands[1], sc, fullScreenOffsets); + if(sc.CheckToken(TK_Else)) + ParseBlock(commands[0], sc, fullScreenOffsets); } void Reset() { for(unsigned int i = 0;i < 2;i++) { - for(unsigned int j = 0;j < commands[i].Size();j++) - commands[i][j]->Reset(); + for(auto command : commands[i]) + command->Reset(); } } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { - for(unsigned int i = 0;i < commands[truth].Size();i++) - commands[truth][i]->Tick(block, statusBar, hudChanged); + for(auto command : commands[truth]) + command->Tick(block, statusBar, hudChanged); } protected: @@ -283,11 +247,65 @@ class SBarInfoCommandFlowControl : public SBarInfoCommand Tick(block, statusBar, true); } + void Negate() + { + swapvalues(commands[0], commands[1]); + } + private: + void ParseBlock(TDeletingArray &commands, FScanner &sc, bool fullScreenOffsets) + { + if(sc.CheckToken('{')) + { + while(SBarInfoCommand *cmd = NextCommand(sc)) + { + cmd->Parse(sc, fullScreenOffsets); + commands.Push(cmd); + } + } + else + { + if(SBarInfoCommand *cmd = NextCommand(sc)) + { + cmd->Parse(sc, fullScreenOffsets); + commands.Push(cmd); + } + else + sc.ScriptError("Missing command for flow control statement."); + } + } + SBarInfoCommand *NextCommand(FScanner &sc); - bool truth; - TArray commands[2]; + TDeletingArray commands[2]; + bool truth; +}; + +class SBarInfoNegatableFlowControl : public SBarInfoCommandFlowControl +{ + public: + SBarInfoNegatableFlowControl(SBarInfo *script) : SBarInfoCommandFlowControl(script) {} + + void Parse(FScanner &sc, bool fullScreenOffsets) + { + bool negate = false; + if(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("not")) + negate = true; + else + sc.UnGet(); + } + + ParseNegatable(sc, fullScreenOffsets); + + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + + if(negate) + Negate(); + } + + virtual void ParseNegatable(FScanner &sc, bool fullScreenOffsets) {} }; class SBarInfoMainBlock : public SBarInfoCommandFlowControl diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index e08fb1a20f..a471ff8b25 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -1830,33 +1830,17 @@ const char* const CommandGameMode::modeNames[] = //////////////////////////////////////////////////////////////////////////////// -class CommandUsesAmmo : public SBarInfoCommandFlowControl +class CommandUsesAmmo : public SBarInfoNegatableFlowControl { public: - CommandUsesAmmo(SBarInfo *script) : SBarInfoCommandFlowControl(script), - negate(false) - { - } + CommandUsesAmmo(SBarInfo *script) : SBarInfoNegatableFlowControl(script) {} - void Parse(FScanner &sc, bool fullScreenOffsets) - { - if(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("not")) - negate = true; - else - sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String); - } - SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); - } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { - SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); - SetTruth((statusBar->CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL)) ^ negate, block, statusBar); + SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL), block, statusBar); } - protected: - bool negate; }; //////////////////////////////////////////////////////////////////////////////// @@ -1872,7 +1856,7 @@ class CommandUsesSecondaryAmmo : public CommandUsesAmmo { SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); - SetTruth((statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2) ^ negate, block, statusBar); + SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2, block, statusBar); } }; @@ -2890,28 +2874,18 @@ class CommandDrawBar : public SBarInfoCommand //////////////////////////////////////////////////////////////////////////////// -class CommandIsSelected : public SBarInfoCommandFlowControl +class CommandIsSelected : public SBarInfoNegatableFlowControl { public: - CommandIsSelected(SBarInfo *script) : SBarInfoCommandFlowControl(script), - negate(false) + CommandIsSelected(SBarInfo *script) : SBarInfoNegatableFlowControl(script) { weapon[0] = NULL; weapon[1] = NULL; } - void Parse(FScanner &sc, bool fullScreenOffsets) + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) { - if(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("not")) - { - negate = true; - if(!sc.CheckToken(TK_StringConst)) - sc.MustGetToken(TK_Identifier); - } - } - else + if(!sc.CheckToken(TK_Identifier)) sc.MustGetToken(TK_StringConst); for(int i = 0;i < 2;i++) { @@ -2930,24 +2904,18 @@ class CommandIsSelected : public SBarInfoCommandFlowControl else break; } - SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { - SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); if(statusBar->CPlayer->ReadyWeapon != NULL) { const PClass *readyWeapon = statusBar->CPlayer->ReadyWeapon->GetClass(); - SetTruth(((weapon[1] != NULL) && - ((negate && (weapon[0] != readyWeapon && weapon[1] != readyWeapon)) || - (!negate && (weapon[0] == readyWeapon || weapon[1] == readyWeapon)))) || - ((weapon[1] == NULL) && - ((!negate && weapon[0] == readyWeapon) || (negate && weapon[0] != readyWeapon))), block, statusBar); + SetTruth(weapon[0] == readyWeapon || (weapon[1] && weapon[1] == readyWeapon), block, statusBar); } } protected: - bool negate; const PClass *weapon[2]; }; @@ -3245,26 +3213,20 @@ FRandom CommandDrawGem::pr_chainwiggle; //use the same method of chain wiggling //////////////////////////////////////////////////////////////////////////////// -class CommandWeaponAmmo : public SBarInfoCommandFlowControl +class CommandWeaponAmmo : public SBarInfoNegatableFlowControl { public: - CommandWeaponAmmo(SBarInfo *script) : SBarInfoCommandFlowControl(script), - conditionAnd(false), negate(false) + CommandWeaponAmmo(SBarInfo *script) : SBarInfoNegatableFlowControl(script), + conditionAnd(false) { ammo[0] = NULL; ammo[1] = NULL; } - void Parse(FScanner &sc, bool fullScreenOffsets) + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) { if(!sc.CheckToken(TK_StringConst)) sc.MustGetToken(TK_Identifier); - if(sc.Compare("not") && sc.TokenType == TK_Identifier) - { - negate = true; - if(!sc.CheckToken(TK_StringConst)) - sc.MustGetToken(TK_Identifier); - } for(int i = 0;i < 2;i++) { ammo[i] = PClass::FindClass(sc.String); @@ -3289,11 +3251,10 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl else break; } - SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { - SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); if(statusBar->CPlayer->ReadyWeapon != NULL) { @@ -3301,41 +3262,25 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl const PClass *AmmoType2 = statusBar->CPlayer->ReadyWeapon->AmmoType2; bool usesammo1 = (AmmoType1 != NULL); bool usesammo2 = (AmmoType2 != NULL); - if(negate && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. - { - SetTruth(true, block, statusBar); - return; - } + //if(!usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. + //{ + // SetTruth(false, block, statusBar); + // return; + //} //Or means only 1 ammo type needs to match and means both need to match. if(ammo[1] != NULL) { bool match1 = ((usesammo1 && (AmmoType1 == ammo[0] || AmmoType1 == ammo[1])) || !usesammo1); bool match2 = ((usesammo2 && (AmmoType2 == ammo[0] || AmmoType2 == ammo[1])) || !usesammo2); if((!conditionAnd && (match1 || match2)) || (conditionAnd && (match1 && match2))) - { - if(!negate) - { - SetTruth(true, block, statusBar); - return; - } - } - else if(negate) { SetTruth(true, block, statusBar); return; } } - else //Every thing here could probably be one long if statement but then it would be more confusing. + else { if((usesammo1 && (AmmoType1 == ammo[0])) || (usesammo2 && (AmmoType2 == ammo[0]))) - { - if(!negate) - { - SetTruth(true, block, statusBar); - return; - } - } - else if(negate) { SetTruth(true, block, statusBar); return; @@ -3345,33 +3290,26 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl SetTruth(false, block, statusBar); } protected: - bool conditionAnd; - bool negate; const PClass *ammo[2]; + bool conditionAnd; }; //////////////////////////////////////////////////////////////////////////////// -class CommandInInventory : public SBarInfoCommandFlowControl +class CommandInInventory : public SBarInfoNegatableFlowControl { public: - CommandInInventory(SBarInfo *script) : SBarInfoCommandFlowControl(script), - conditionAnd(false), negate(false) + CommandInInventory(SBarInfo *script) : SBarInfoNegatableFlowControl(script), + conditionAnd(false) { item[0] = item[1] = NULL; amount[0] = amount[1] = 0; } - void Parse(FScanner &sc, bool fullScreenOffsets) + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) { if(!sc.CheckToken(TK_StringConst)) sc.MustGetToken(TK_Identifier); - if(sc.Compare("not") && sc.TokenType == TK_Identifier) - { - negate = true; - if(!sc.CheckToken(TK_StringConst)) - sc.MustGetToken(TK_Identifier); - } for(int i = 0;i < 2;i++) { item[i] = PClass::FindActor(sc.String); @@ -3402,11 +3340,10 @@ class CommandInInventory : public SBarInfoCommandFlowControl else break; } - SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { - SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); AInventory *invItem[2] = { statusBar->CPlayer->mo->FindInventory(item[0]), statusBar->CPlayer->mo->FindInventory(item[1]) }; if (invItem[0] != NULL && amount[0] > 0 && invItem[0]->Amount < amount[0]) invItem[0] = NULL; @@ -3415,16 +3352,15 @@ class CommandInInventory : public SBarInfoCommandFlowControl if (item[1]) { if (conditionAnd) - SetTruth((invItem[0] && invItem[1]) != negate, block, statusBar); + SetTruth(invItem[0] && invItem[1], block, statusBar); else - SetTruth((invItem[0] || invItem[1]) != negate, block, statusBar); + SetTruth(invItem[0] || invItem[1], block, statusBar); } else - SetTruth((invItem[0] != NULL) != negate, block, statusBar); + SetTruth(invItem[0] != NULL, block, statusBar); } protected: bool conditionAnd; - bool negate; PClassActor *item[2]; int amount[2]; }; @@ -3459,42 +3395,48 @@ class CommandAlpha : public SBarInfoMainBlock //////////////////////////////////////////////////////////////////////////////// -class CommandIfHealth : public SBarInfoCommandFlowControl +class CommandIfHealth : public SBarInfoNegatableFlowControl { public: - CommandIfHealth(SBarInfo *script) : SBarInfoCommandFlowControl(script), - negate(false), percentage(false) + CommandIfHealth(SBarInfo *script) : SBarInfoNegatableFlowControl(script), + percentage(false) { } - void Parse(FScanner &sc, bool fullScreenOffsets) + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) { - if (sc.CheckToken(TK_Identifier)) - { - if (sc.Compare("not")) - negate = true; - else - sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String); - } - sc.MustGetToken(TK_IntConst); percentage = sc.CheckToken('%'); hpamount = sc.Number; - - SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { - SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); int phealth = percentage ? statusBar->CPlayer->mo->health * 100 / statusBar->CPlayer->mo->GetMaxHealth() : statusBar->CPlayer->mo->health; - SetTruth((phealth >= hpamount) ^ negate, block, statusBar); + SetTruth(phealth >= hpamount, block, statusBar); } protected: - bool negate; - bool percentage; int hpamount; + bool percentage; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandIfInvulnerable : public SBarInfoNegatableFlowControl +{ + public: + CommandIfInvulnerable(SBarInfo *script) : SBarInfoNegatableFlowControl(script) + { + } + + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); + + SetTruth((statusBar->CPlayer->mo->flags2 & MF2_INVULNERABLE) || (statusBar->CPlayer->cheats & (CF_GODMODE | CF_GODMODE2)), block, statusBar); + } }; //////////////////////////////////////////////////////////////////////////////// @@ -3509,6 +3451,7 @@ static const char *SBarInfoCommandNames[] = "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", "weaponammo", "ininventory", "alpha", "ifhealth", + "ifinvulnerable", NULL }; @@ -3522,6 +3465,7 @@ enum SBarInfoCommands SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH, + SBARINFO_IFINVULNERABLE, }; SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) @@ -3555,6 +3499,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) case SBARINFO_ININVENTORY: return new CommandInInventory(script); case SBARINFO_ALPHA: return new CommandAlpha(script); case SBARINFO_IFHEALTH: return new CommandIfHealth(script); + case SBARINFO_IFINVULNERABLE: return new CommandIfInvulnerable(script); } sc.ScriptError("Unknown command '%s'.\n", sc.String); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index d0153a98fd..16f6d7319e 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -52,7 +52,6 @@ #include "v_palette.h" #include "d_player.h" #include "farchive.h" -#include "a_hexenglobal.h" #include "gstrings.h" #include "../version.h" diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index 8129a8bfc7..90a934b82b 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -714,6 +714,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade) return 0; P_SetPsprite (player, ps_flash, flash); + self->player->psprites[ps_flash].processPending = true; if (grenadetype != NULL) { diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 11b169e6a1..2668fd4a1a 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4529,12 +4529,12 @@ int DLevelScript::LineFromID(int id) } } -bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType *&type) +bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType *&type, bool readonly) { PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); PArray *arraytype; - if (var == NULL || (var->Flags & VARF_Native)) + if (var == NULL || (!readonly && (var->Flags & VARF_Native))) { return false; } @@ -4557,6 +4557,17 @@ bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType * return false; } addr = baddr; + // We don't want Int subclasses like Name or Color to be accessible, + // but we do want to support Float subclasses like Fixed. + if (!type->IsA(RUNTIME_CLASS(PInt)) && !type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + // For reading, we also support Name and String types. + if (readonly && (type->IsA(RUNTIME_CLASS(PName)) || type->IsA(RUNTIME_CLASS(PString)))) + { + return true; + } + return false; + } return true; } @@ -4565,9 +4576,16 @@ static void SetUserVariable(AActor *self, FName varname, int index, int value) void *addr; PType *type; - if (GetVarAddrType(self, varname, index, addr, type)) + if (GetVarAddrType(self, varname, index, addr, type, false)) { - type->SetValue(addr, value); + if (!type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + type->SetValue(addr, value); + } + else + { + type->SetValue(addr, FIXED2DBL(value)); + } } } @@ -4576,9 +4594,24 @@ static int GetUserVariable(AActor *self, FName varname, int index) void *addr; PType *type; - if (GetVarAddrType(self, varname, index, addr, type)) + if (GetVarAddrType(self, varname, index, addr, type, true)) { - return type->GetValueInt(addr); + if (type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + return FLOAT2FIXED(type->GetValueFloat(addr)); + } + else if (type->IsA(RUNTIME_CLASS(PName))) + { + return GlobalACSStrings.AddString(FName(ENamedName(type->GetValueInt(addr))).GetChars()); + } + else if (type->IsA(RUNTIME_CLASS(PString))) + { + return GlobalACSStrings.AddString(*(FString *)addr); + } + else + { + return type->GetValueInt(addr); + } } return 0; } @@ -5747,7 +5780,9 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) argCount > 8 ? args[8] : 0, argCount > 9 ? FIXED2DBL(args[9]) : 1.0, argCount > 10 ? FIXED2DBL(args[10]) : 1.0, - argCount > 11 ? FIXED2DBL(args[11]) : 1.0 ); + argCount > 11 ? FIXED2DBL(args[11]) : 1.0, + argCount > 12 ? args[12] : 0, + argCount > 13 ? args[13] : 0); } case ACSF_SetLineActivation: diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 7b4fb2118f..95be8f7bcf 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -46,6 +46,14 @@ inline FArchive &operator<< (FArchive &arc, DCeiling::ECeiling &type) return arc; } +inline FArchive &operator<< (FArchive &arc, DCeiling::ECrushMode &type) +{ + BYTE val = (BYTE)type; + arc << val; + type = (DCeiling::ECrushMode)val; + return arc; +} + //============================================================================ // // CEILINGS @@ -80,7 +88,7 @@ void DCeiling::Serialize (FArchive &arc) << m_NewSpecial << m_Tag << m_OldDirection - << m_Hexencrush; + << m_CrushMode; } //============================================================================ @@ -159,7 +167,7 @@ void DCeiling::Tick () case -1: // DOWN - res = MoveCeiling (m_Speed, m_BottomHeight, m_Crush, m_Direction, m_Hexencrush); + res = MoveCeiling (m_Speed, m_BottomHeight, m_Crush, m_Direction, m_CrushMode == ECrushMode::crushHexen); if (res == pastdest) { @@ -196,7 +204,7 @@ void DCeiling::Tick () { case ceilCrushAndRaise: case ceilLowerAndCrush: - if (m_Speed1 == FRACUNIT && m_Speed2 == FRACUNIT) + if (m_CrushMode == ECrushMode::crushSlowdown) m_Speed = FRACUNIT / 8; break; @@ -224,7 +232,7 @@ DCeiling::DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent) : DMovingCeiling (sec) { m_Crush = -1; - m_Hexencrush = false; + m_CrushMode = ECrushMode::crushDoom; m_Speed = m_Speed1 = speed1; m_Speed2 = speed2; m_Silent = silent; @@ -238,7 +246,7 @@ DCeiling::DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent) DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, fixed_t speed, fixed_t speed2, fixed_t height, - int crush, int silent, int change, bool hexencrush) + int crush, int silent, int change, ECrushMode hexencrush) { fixed_t targheight = 0; // Silence, GCC @@ -387,7 +395,7 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, ceiling->m_Tag = tag; ceiling->m_Type = type; ceiling->m_Crush = crush; - ceiling->m_Hexencrush = hexencrush; + ceiling->m_CrushMode = hexencrush; // Do not interpolate instant movement ceilings. // Note for ZDoomGL: Check to make sure that you update the sector @@ -476,7 +484,7 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, fixed_t speed, fixed_t speed2, fixed_t height, - int crush, int silent, int change, bool hexencrush) + int crush, int silent, int change, DCeiling::ECrushMode hexencrush) { int secnum; bool rtn; diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 90b43323c2..648b9f46cc 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -47,8 +47,7 @@ #include "b_bot.h" //Added by MC: -#include "ravenshared.h" -#include "a_hexenglobal.h" +#include "d_player.h" #include "a_sharedglobal.h" #include "a_pickups.h" #include "gi.h" diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 926a475b32..3bfab82389 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -88,9 +88,21 @@ int LS_SetGlobalFogParameter(line_t *ln, AActor *it, bool backSide, int arg0, in #define OCTICS(a) (((a)*TICRATE)/8) #define BYTEANGLE(a) ((angle_t)((a)<<24)) #define CRUSH(a) ((a) > 0? (a) : -1) -#define CRUSHTYPE(a) ((a)==1? false : (a)==2? true : gameinfo.gametype == GAME_Hexen) #define CHANGE(a) (((a) >= 0 && (a)<=7)? ChangeMap[a]:0) +static bool CRUSHTYPE(int a) +{ + return ((a) == 1 ? false : (a) == 2 ? true : gameinfo.gametype == GAME_Hexen); +} + +static DCeiling::ECrushMode CRUSHTYPE(int a, bool withslowdown) +{ + static DCeiling::ECrushMode map[] = { DCeiling::ECrushMode::crushDoom, DCeiling::ECrushMode::crushHexen, DCeiling::ECrushMode::crushSlowdown }; + if (a >= 1 && a <= 3) return map[a - 1]; + if (gameinfo.gametype == GAME_Hexen) return DCeiling::ECrushMode::crushHexen; + return withslowdown? DCeiling::ECrushMode::crushSlowdown : DCeiling::ECrushMode::crushDoom; +} + static FRandom pr_glass ("GlassBreak"); // There are aliases for the ACS specials that take names instead of numbers. @@ -632,43 +644,43 @@ FUNC(LS_Pillar_Open) FUNC(LS_Ceiling_LowerByValue) // Ceiling_LowerByValue (tag, speed, height, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3), false); + return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3)); } FUNC(LS_Ceiling_RaiseByValue) // Ceiling_RaiseByValue (tag, speed, height, change) { - return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3), false); + return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, CRUSH(arg4), 0, CHANGE(arg3)); } FUNC(LS_Ceiling_LowerByValueTimes8) // Ceiling_LowerByValueTimes8 (tag, speed, height, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3), false); + return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3)); } FUNC(LS_Ceiling_RaiseByValueTimes8) // Ceiling_RaiseByValueTimes8 (tag, speed, height, change) { - return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3), false); + return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3)); } FUNC(LS_Ceiling_CrushAndRaise) // Ceiling_CrushAndRaise (tag, speed, crush, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3, false)); } FUNC(LS_Ceiling_LowerAndCrush) // Ceiling_LowerAndCrush (tag, speed, crush, crushtype) { - return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3)); + return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3, arg1 == 8)); } FUNC(LS_Ceiling_LowerAndCrushDist) // Ceiling_LowerAndCrush (tag, speed, crush, dist, crushtype) { - return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4, arg1 == 8)); } FUNC(LS_Ceiling_CrushStop) @@ -680,141 +692,141 @@ FUNC(LS_Ceiling_CrushStop) FUNC(LS_Ceiling_CrushRaiseAndStay) // Ceiling_CrushRaiseAndStay (tag, speed, crush, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3)); + return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3, false)); } FUNC(LS_Ceiling_MoveToValueTimes8) // Ceiling_MoveToValueTimes8 (tag, speed, height, negative, change) { return EV_DoCeiling (DCeiling::ceilMoveToValue, ln, arg0, SPEED(arg1), 0, - arg2*FRACUNIT*8*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4), false); + arg2*FRACUNIT*8*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4)); } FUNC(LS_Ceiling_MoveToValue) // Ceiling_MoveToValue (tag, speed, height, negative, change) { return EV_DoCeiling (DCeiling::ceilMoveToValue, ln, arg0, SPEED(arg1), 0, - arg2*FRACUNIT*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4), false); + arg2*FRACUNIT*((arg3) ? -1 : 1), -1, 0, CHANGE(arg4)); } FUNC(LS_Ceiling_LowerToHighestFloor) // Ceiling_LowerToHighestFloor (tag, speed, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2), false); + return EV_DoCeiling (DCeiling::ceilLowerToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2)); } FUNC(LS_Ceiling_LowerInstant) // Ceiling_LowerInstant (tag, unused, height, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, CRUSH(arg4), 0, CHANGE(arg3), false); + return EV_DoCeiling (DCeiling::ceilLowerInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, CRUSH(arg4), 0, CHANGE(arg3)); } FUNC(LS_Ceiling_RaiseInstant) // Ceiling_RaiseInstant (tag, unused, height, change) { - return EV_DoCeiling (DCeiling::ceilRaiseInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3), false); + return EV_DoCeiling (DCeiling::ceilRaiseInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, -1, 0, CHANGE(arg3)); } FUNC(LS_Ceiling_CrushRaiseAndStayA) // Ceiling_CrushRaiseAndStayA (tag, dnspeed, upspeed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4, false)); } FUNC(LS_Ceiling_CrushRaiseAndStaySilA) // Ceiling_CrushRaiseAndStaySilA (tag, dnspeed, upspeed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4, false)); } FUNC(LS_Ceiling_CrushAndRaiseA) // Ceiling_CrushAndRaiseA (tag, dnspeed, upspeed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4, arg1 == 8 && arg2 == 8)); } FUNC(LS_Ceiling_CrushAndRaiseDist) // Ceiling_CrushAndRaiseDist (tag, dist, speed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4, arg2 == 8)); } FUNC(LS_Ceiling_CrushAndRaiseSilentA) // Ceiling_CrushAndRaiseSilentA (tag, dnspeed, upspeed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4, arg1 == 8 && arg2 == 8)); } FUNC(LS_Ceiling_CrushAndRaiseSilentDist) // Ceiling_CrushAndRaiseSilentDist (tag, dist, upspeed, damage, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 1, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 1, 0, CRUSHTYPE(arg4, arg2 == 8)); } FUNC(LS_Ceiling_RaiseToNearest) // Ceiling_RaiseToNearest (tag, speed, change) { - return EV_DoCeiling (DCeiling::ceilRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false); + return EV_DoCeiling (DCeiling::ceilRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0); } FUNC(LS_Ceiling_RaiseToHighest) // Ceiling_RaiseToHighest (tag, speed, change) { - return EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false); + return EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0); } FUNC(LS_Ceiling_RaiseToLowest) // Ceiling_RaiseToLowest (tag, speed, change) { - return EV_DoCeiling (DCeiling::ceilRaiseToLowest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false); + return EV_DoCeiling (DCeiling::ceilRaiseToLowest, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0); } FUNC(LS_Ceiling_RaiseToHighestFloor) // Ceiling_RaiseToHighestFloor (tag, speed, change) { - return EV_DoCeiling (DCeiling::ceilRaiseToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false); + return EV_DoCeiling (DCeiling::ceilRaiseToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0); } FUNC(LS_Ceiling_RaiseByTexture) // Ceiling_RaiseByTexture (tag, speed, change) { - return EV_DoCeiling (DCeiling::ceilRaiseByTexture, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0, false); + return EV_DoCeiling (DCeiling::ceilRaiseByTexture, ln, arg0, SPEED(arg1), 0, 0, -1, CHANGE(arg2), 0); } FUNC(LS_Ceiling_LowerToLowest) // Ceiling_LowerToLowest (tag, speed, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2), false); + return EV_DoCeiling (DCeiling::ceilLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2)); } FUNC(LS_Ceiling_LowerToNearest) // Ceiling_LowerToNearest (tag, speed, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerToNearest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2), false); + return EV_DoCeiling (DCeiling::ceilLowerToNearest, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg2)); } FUNC(LS_Ceiling_ToHighestInstant) // Ceiling_ToHighestInstant (tag, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerToHighest, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1), false); + return EV_DoCeiling (DCeiling::ceilLowerToHighest, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1)); } FUNC(LS_Ceiling_ToFloorInstant) // Ceiling_ToFloorInstant (tag, change, crush) { - return EV_DoCeiling (DCeiling::ceilRaiseToFloor, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1), false); + return EV_DoCeiling (DCeiling::ceilRaiseToFloor, ln, arg0, FRACUNIT*2, 0, 0, CRUSH(arg2), 0, CHANGE(arg1)); } FUNC(LS_Ceiling_LowerToFloor) // Ceiling_LowerToFloor (tag, speed, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerToFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4), false); + return EV_DoCeiling (DCeiling::ceilLowerToFloor, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4)); } FUNC(LS_Ceiling_LowerByTexture) // Ceiling_LowerByTexture (tag, speed, change, crush) { - return EV_DoCeiling (DCeiling::ceilLowerByTexture, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4), false); + return EV_DoCeiling (DCeiling::ceilLowerByTexture, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4)); } FUNC(LS_Generic_Ceiling) @@ -845,14 +857,14 @@ FUNC(LS_Generic_Ceiling) } return EV_DoCeiling (type, ln, arg0, SPEED(arg1), SPEED(arg1), arg2*FRACUNIT, - (arg4 & 16) ? 20 : -1, 0, arg4 & 7, false); + (arg4 & 16) ? 20 : -1, 0, arg4 & 7); } FUNC(LS_Generic_Crusher) // Generic_Crusher (tag, dnspeed, upspeed, silent, damage) { return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), - SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, false); + SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, (arg1 <= 24 && arg2 <= 24)? DCeiling::ECrushMode::crushSlowdown : DCeiling::ECrushMode::crushDoom); } FUNC(LS_Generic_Crusher2) @@ -860,7 +872,7 @@ FUNC(LS_Generic_Crusher2) { // same as above but uses Hexen's crushing method. return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), - SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, true); + SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, DCeiling::ECrushMode::crushHexen); } FUNC(LS_Plat_PerpetualRaise) @@ -1948,7 +1960,7 @@ FUNC(LS_FloorAndCeiling_RaiseByValue) FUNC(LS_FloorAndCeiling_LowerRaise) // FloorAndCeiling_LowerRaise (tag, fspeed, cspeed, boomemu) { - bool res = EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0, false); + bool res = EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0); // The switch based Boom equivalents of FloorandCeiling_LowerRaise do incorrect checks // which cause the floor only to move when the ceiling fails to do so. // To avoid problems with maps that have incorrect args this only uses a @@ -2137,51 +2149,9 @@ FUNC(LS_Sector_ChangeFlags) return rtn; } -struct FThinkerCollection -{ - int RefNum; - DThinker *Obj; -}; -static TArray Collection; -void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type) -{ - // Find pushers already attached to the sector, and change their parameters. - { - TThinkerIterator iterator; - FThinkerCollection collect; - - while ( (collect.Obj = iterator.Next ()) ) - { - if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch (type, tag)) >= 0) - { - ((DPusher *)collect.Obj)->ChangeValues (magnitude, angle); - Collection.Push (collect); - } - } - } - - size_t numcollected = Collection.Size (); - int secnum; - - // Now create pushers for any sectors that don't already have them. - FSectorTagIterator itr(tag); - while ((secnum = itr.Next()) >= 0) - { - unsigned int i; - for (i = 0; i < numcollected; i++) - { - if (Collection[i].RefNum == sectors[secnum].sectornum) - break; - } - if (i == numcollected) - { - new DPusher (type, NULL, magnitude, angle, NULL, secnum); - } - } - Collection.Clear (); -} +void AdjustPusher(int tag, int magnitude, int angle, bool wind); FUNC(LS_Sector_SetWind) // Sector_SetWind (tag, amount, angle) @@ -2189,7 +2159,7 @@ FUNC(LS_Sector_SetWind) if (arg3) return false; - AdjustPusher (arg0, arg1, arg2, DPusher::p_wind); + AdjustPusher (arg0, arg1, arg2, true); return true; } @@ -2199,7 +2169,7 @@ FUNC(LS_Sector_SetCurrent) if (arg3) return false; - AdjustPusher (arg0, arg1, arg2, DPusher::p_current); + AdjustPusher (arg0, arg1, arg2, false); return true; } @@ -2241,76 +2211,9 @@ FUNC(LS_Sector_SetLink) return false; } +void SetWallScroller(int id, int sidechoice, fixed_t dx, fixed_t dy, EScrollPos Where); +void SetScroller(int tag, EScroll type, fixed_t dx, fixed_t dy); -static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int Where) -{ - Where &=7; - if (Where == 0) return; - - if ((dx | dy) == 0) - { - // Special case: Remove the scroller, because the deltas are both 0. - TThinkerIterator iterator (STAT_SCROLLER); - DScroller *scroller; - - while ( (scroller = iterator.Next ()) ) - { - int wallnum = scroller->GetWallNum (); - - if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) && - int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum && - Where == scroller->GetScrollParts()) - { - scroller->Destroy (); - } - } - } - else - { - // Find scrollers already attached to the matching walls, and change - // their rates. - { - TThinkerIterator iterator (STAT_SCROLLER); - FThinkerCollection collect; - - while ( (collect.Obj = iterator.Next ()) ) - { - if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 && - tagManager.LineHasID(sides[collect.RefNum].linedef, id) && - int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum && - Where == ((DScroller *)collect.Obj)->GetScrollParts()) - { - ((DScroller *)collect.Obj)->SetRate (dx, dy); - Collection.Push (collect); - } - } - } - - size_t numcollected = Collection.Size (); - int linenum; - - // Now create scrollers for any walls that don't already have them. - FLineIdIterator itr(id); - while ((linenum = itr.Next()) >= 0) - { - if (lines[linenum].sidedef[sidechoice] != NULL) - { - int sidenum = int(lines[linenum].sidedef[sidechoice] - sides); - unsigned int i; - for (i = 0; i < numcollected; i++) - { - if (Collection[i].RefNum == sidenum) - break; - } - if (i == numcollected) - { - new DScroller (DScroller::sc_side, dx, dy, -1, sidenum, 0, Where); - } - } - } - Collection.Clear (); - } -} FUNC(LS_Scroll_Texture_Both) // Scroll_Texture_Both (id, left, right, up, down) @@ -2332,7 +2235,7 @@ FUNC(LS_Scroll_Texture_Both) sidechoice = 0; } - SetWallScroller (arg0, sidechoice, dx, dy, 7); + SetWallScroller (arg0, sidechoice, dx, dy, scw_all); return true; } @@ -2343,47 +2246,10 @@ FUNC(LS_Scroll_Wall) if (arg0 == 0) return false; - SetWallScroller (arg0, !!arg3, arg1, arg2, arg4); + SetWallScroller (arg0, !!arg3, arg1, arg2, EScrollPos(arg4)); return true; } -static void SetScroller (int tag, DScroller::EScrollType type, fixed_t dx, fixed_t dy) -{ - TThinkerIterator iterator (STAT_SCROLLER); - DScroller *scroller; - int i; - - // Check if there is already a scroller for this tag - // If at least one sector with this tag is scrolling, then they all are. - // If the deltas are both 0, we don't remove the scroller, because a - // displacement/accelerative scroller might have been set up, and there's - // no way to create one after the level is fully loaded. - i = 0; - while ( (scroller = iterator.Next ()) ) - { - if (scroller->IsType (type)) - { - if (tagManager.SectorHasTag(scroller->GetAffectee (), tag)) - { - i++; - scroller->SetRate (dx, dy); - } - } - } - - if (i > 0 || (dx|dy) == 0) - { - return; - } - - // Need to create scrollers for the sector(s) - FSectorTagIterator itr(tag); - while ((i = itr.Next()) >= 0) - { - new DScroller (type, dx, dy, -1, i, 0); - } -} - // NOTE: For the next two functions, x-move and y-move are // 0-based, not 128-based as they are if they appear on lines. // Note also that parameter ordering is different. @@ -2396,19 +2262,19 @@ FUNC(LS_Scroll_Floor) if (arg3 == 0 || arg3 == 2) { - SetScroller (arg0, DScroller::sc_floor, -dx, dy); + SetScroller (arg0, EScroll::sc_floor, -dx, dy); } else { - SetScroller (arg0, DScroller::sc_floor, 0, 0); + SetScroller (arg0, EScroll::sc_floor, 0, 0); } if (arg3 > 0) { - SetScroller (arg0, DScroller::sc_carry, dx, dy); + SetScroller (arg0, EScroll::sc_carry, dx, dy); } else { - SetScroller (arg0, DScroller::sc_carry, 0, 0); + SetScroller (arg0, EScroll::sc_carry, 0, 0); } return true; } @@ -2419,7 +2285,7 @@ FUNC(LS_Scroll_Ceiling) fixed_t dx = arg1 * FRACUNIT/32; fixed_t dy = arg2 * FRACUNIT/32; - SetScroller (arg0, DScroller::sc_ceiling, -dx, dy); + SetScroller (arg0, EScroll::sc_ceiling, -dx, dy); return true; } diff --git a/src/p_map.cpp b/src/p_map.cpp index aafe2d125c..c6439d9668 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -5363,7 +5363,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo points *= thing->GetClass()->RDFactor / (float)FRACUNIT; // points and bombdamage should be the same sign - if (((points * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) + if (((int(points) * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { // OK to damage; target is in direct path double vz; double thrust; diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp new file mode 100644 index 0000000000..722e1dc0cb --- /dev/null +++ b/src/p_pusher.cpp @@ -0,0 +1,485 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Initializes and implements BOOM linedef triggers for +// Wind/Current +// +//----------------------------------------------------------------------------- + + +#include +#include "actor.h" +#include "p_spec.h" +#include "farchive.h" +#include "p_lnspec.h" +#include "c_cvars.h" +#include "p_maputl.h" +#include "p_local.h" +#include "d_player.h" + +CVAR(Bool, var_pushers, true, CVAR_SERVERINFO); + +// phares 3/20/98: added new model of Pushers for push/pull effects + +class DPusher : public DThinker +{ + DECLARE_CLASS (DPusher, DThinker) + HAS_OBJECT_POINTERS +public: + enum EPusher + { + p_push, + p_pull, + p_wind, + p_current + }; + + DPusher (); + DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); + void Serialize (FArchive &arc); + int CheckForSectorMatch (EPusher type, int tag); + void ChangeValues (int magnitude, int angle) + { + angle_t ang = ((angle_t)(angle<<24)) >> ANGLETOFINESHIFT; + m_Xmag = (magnitude * finecosine[ang]) >> FRACBITS; + m_Ymag = (magnitude * finesine[ang]) >> FRACBITS; + m_Magnitude = magnitude; + } + + void Tick (); + +protected: + EPusher m_Type; + TObjPtr m_Source;// Point source if point pusher + int m_Xmag; // X Strength + int m_Ymag; // Y Strength + int m_Magnitude; // Vector strength for point pusher + int m_Radius; // Effective radius for point pusher + int m_X; // X of point source if point pusher + int m_Y; // Y of point source if point pusher + int m_Affectee; // Number of affected sector + + friend bool PIT_PushThing (AActor *thing); +}; + + +IMPLEMENT_POINTY_CLASS (DPusher) + DECLARE_POINTER (m_Source) +END_POINTERS + +DPusher::DPusher () +{ +} + +inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type) +{ + BYTE val = (BYTE)type; + arc << val; + type = (DPusher::EPusher)val; + return arc; +} + +void DPusher::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + arc << m_Type + << m_Source + << m_Xmag + << m_Ymag + << m_Magnitude + << m_Radius + << m_X + << m_Y + << m_Affectee; +} + + +//////////////////////////////////////////////////////////////////////////// +// +// PUSH/PULL EFFECT +// +// phares 3/20/98: Start of push/pull effects +// +// This is where push/pull effects are applied to objects in the sectors. +// +// There are four kinds of push effects +// +// 1) Pushing Away +// +// Pushes you away from a point source defined by the location of an +// MT_PUSH Thing. The force decreases linearly with distance from the +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PUSH. The force is felt only if the point +// MT_PUSH can see the target object. +// +// 2) Pulling toward +// +// Same as Pushing Away except you're pulled toward an MT_PULL point +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PULL. The force is felt only if the point +// MT_PULL can see the target object. +// +// 3) Wind +// +// Pushes you in a constant direction. Full force above ground, half +// force on the ground, nothing if you're below it (water). +// +// 4) Current +// +// Pushes you in a constant direction. No force above ground, full +// force if on the ground or below it (water). +// +// The magnitude of the force is controlled by the length of a controlling +// linedef. The force vector for types 3 & 4 is determined by the angle +// of the linedef, and is constant. +// +// For each sector where these effects occur, the sector special type has +// to have the PUSH_MASK bit set. If this bit is turned off by a switch +// at run-time, the effect will not occur. The controlling sector for +// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing. + + +#define PUSH_FACTOR 7 + +///////////////////////////// +// +// Add a push thinker to the thinker list + +DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle, + AActor *source, int affectee) +{ + m_Source = source; + m_Type = type; + if (l) + { + m_Xmag = l->dx>>FRACBITS; + m_Ymag = l->dy>>FRACBITS; + m_Magnitude = P_AproxDistance (m_Xmag, m_Ymag); + } + else + { // [RH] Allow setting magnitude and angle with parameters + ChangeValues (magnitude, angle); + } + if (source) // point source exist? + { + m_Radius = (m_Magnitude) << (FRACBITS+1); // where force goes to zero + m_X = m_Source->X(); + m_Y = m_Source->Y(); + } + m_Affectee = affectee; +} + +int DPusher::CheckForSectorMatch (EPusher type, int tag) +{ + if (m_Type == type && tagManager.SectorHasTag(m_Affectee, tag)) + return m_Affectee; + else + return -1; +} + + +///////////////////////////// +// +// T_Pusher looks for all objects that are inside the radius of +// the effect. +// +void DPusher::Tick () +{ + sector_t *sec; + AActor *thing; + msecnode_t *node; + int xspeed,yspeed; + int ht; + + if (!var_pushers) + return; + + sec = sectors + m_Affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->Flags & SECF_PUSH)) + return; + + // For constant pushers (wind/current) there are 3 situations: + // + // 1) Affected Thing is above the floor. + // + // Apply the full force if wind, no force if current. + // + // 2) Affected Thing is on the ground. + // + // Apply half force if wind, full force if current. + // + // 3) Affected Thing is below the ground (underwater effect). + // + // Apply no force if wind, full force if current. + // + // Apply the effect to clipped players only for now. + // + // In Phase II, you can apply these effects to Things other than players. + // [RH] No Phase II, but it works with anything having MF2_WINDTHRUST now. + + if (m_Type == p_push) + { + // Seek out all pushable things within the force radius of this + // point pusher. Crosses sectors, so use blockmap. + + FPortalGroupArray check(FPortalGroupArray::PGA_NoSectorPortals); // no sector portals because this thing is utterly z-unaware. + FMultiBlockThingsIterator it(check, m_X, m_Y, 0, 0, m_Radius, false, m_Source->Sector); + FMultiBlockThingsIterator::CheckResult cres; + + + while (it.Next(&cres)) + { + AActor *thing = cres.thing; + // Normal ZDoom is based only on the WINDTHRUST flag, with the noclip cheat as an exemption. + bool pusharound = ((thing->flags2 & MF2_WINDTHRUST) && !(thing->flags & MF_NOCLIP)); + + // MBF allows any sentient or shootable thing to be affected, but players with a fly cheat aren't. + if (compatflags & COMPATF_MBFMONSTERMOVE) + { + pusharound = ((pusharound || (thing->IsSentient()) || (thing->flags & MF_SHOOTABLE)) // Add categories here + && (!(thing->player && (thing->flags & (MF_NOGRAVITY))))); // Exclude flying players here + } + + if ((pusharound) ) + { + int sx = m_X; + int sy = m_Y; + int dist = thing->AproxDistance (sx, sy); + int speed = (m_Magnitude - ((dist>>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); + + // If speed <= 0, you're outside the effective radius. You also have + // to be able to see the push/pull source point. + + if ((speed > 0) && (P_CheckSight (thing, m_Source, SF_IGNOREVISIBILITY))) + { + angle_t pushangle = thing->AngleTo(sx, sy); + if (m_Source->GetClass()->TypeName == NAME_PointPusher) + pushangle += ANG180; // away + pushangle >>= ANGLETOFINESHIFT; + thing->vel.x += FixedMul (speed, finecosine[pushangle]); + thing->vel.y += FixedMul (speed, finesine[pushangle]); + } + } + } + return; + } + + // constant pushers p_wind and p_current + + node = sec->touching_thinglist; // things touching this sector + for ( ; node ; node = node->m_snext) + { + thing = node->m_thing; + if (!(thing->flags2 & MF2_WINDTHRUST) || (thing->flags & MF_NOCLIP)) + continue; + + sector_t *hsec = sec->GetHeightSec(); + fixedvec3 pos = thing->PosRelative(sec); + if (m_Type == p_wind) + { + if (hsec == NULL) + { // NOT special water sector + if (thing->Z() > thing->floorz) // above ground + { + xspeed = m_Xmag; // full force + yspeed = m_Ymag; + } + else // on ground + { + xspeed = (m_Xmag)>>1; // half force + yspeed = (m_Ymag)>>1; + } + } + else // special water sector + { + ht = hsec->floorplane.ZatPoint(pos); + if (thing->Z() > ht) // above ground + { + xspeed = m_Xmag; // full force + yspeed = m_Ymag; + } + else if (thing->player->viewz < ht) // underwater + { + xspeed = yspeed = 0; // no force + } + else // wading in water + { + xspeed = (m_Xmag)>>1; // half force + yspeed = (m_Ymag)>>1; + } + } + } + else // p_current + { + const secplane_t *floor; + + if (hsec == NULL) + { // NOT special water sector + floor = &sec->floorplane; + } + else + { // special water sector + floor = &hsec->floorplane; + } + if (thing->Z() > floor->ZatPoint(pos)) + { // above ground + xspeed = yspeed = 0; // no force + } + else + { // on ground/underwater + xspeed = m_Xmag; // full force + yspeed = m_Ymag; + } + } + thing->vel.x += xspeed<<(FRACBITS-PUSH_FACTOR); + thing->vel.y += yspeed<<(FRACBITS-PUSH_FACTOR); + } +} + +///////////////////////////// +// +// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, +// NULL otherwise. + +AActor *P_GetPushThing (int s) +{ + AActor* thing; + sector_t* sec; + + sec = sectors + s; + thing = sec->thinglist; + + while (thing && + thing->GetClass()->TypeName != NAME_PointPusher && + thing->GetClass()->TypeName != NAME_PointPuller) + { + thing = thing->snext; + } + return thing; +} + +///////////////////////////// +// +// Initialize the sectors where pushers are present +// + +void P_SpawnPushers () +{ + int i; + line_t *l = lines; + int s; + + for (i = 0; i < numlines; i++, l++) + { + switch (l->special) + { + case Sector_SetWind: // wind + { + FSectorTagIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + new DPusher(DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s); + l->special = 0; + break; + } + + case Sector_SetCurrent: // current + { + FSectorTagIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + new DPusher(DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s); + l->special = 0; + break; + } + + case PointPush_SetForce: // push/pull + if (l->args[0]) { // [RH] Find thing by sector + FSectorTagIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + { + AActor *thing = P_GetPushThing (s); + if (thing) { // No MT_P* means no effect + // [RH] Allow narrowing it down by tid + if (!l->args[1] || l->args[1] == thing->tid) + new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2], + 0, thing, s); + } + } + } else { // [RH] Find thing by tid + AActor *thing; + FActorIterator iterator (l->args[1]); + + while ( (thing = iterator.Next ()) ) + { + if (thing->GetClass()->TypeName == NAME_PointPusher || + thing->GetClass()->TypeName == NAME_PointPuller) + { + new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2], + 0, thing, int(thing->Sector - sectors)); + } + } + } + l->special = 0; + break; + } + } +} + +void AdjustPusher (int tag, int magnitude, int angle, bool wind) +{ + DPusher::EPusher type = wind? DPusher::p_wind : DPusher::p_current; + + // Find pushers already attached to the sector, and change their parameters. + TArray Collection; + { + TThinkerIterator iterator; + FThinkerCollection collect; + + while ( (collect.Obj = iterator.Next ()) ) + { + if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch (type, tag)) >= 0) + { + ((DPusher *)collect.Obj)->ChangeValues (magnitude, angle); + Collection.Push (collect); + } + } + } + + size_t numcollected = Collection.Size (); + int secnum; + + // Now create pushers for any sectors that don't already have them. + FSectorTagIterator itr(tag); + while ((secnum = itr.Next()) >= 0) + { + unsigned int i; + for (i = 0; i < numcollected; i++) + { + if (Collection[i].RefNum == sectors[secnum].sectornum) + break; + } + if (i == numcollected) + { + new DPusher (type, NULL, magnitude, angle, NULL, secnum); + } + } +} diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp new file mode 100644 index 0000000000..96889b6243 --- /dev/null +++ b/src/p_scroll.cpp @@ -0,0 +1,719 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Initializes and implements BOOM linedef triggers for +// Scrollers/Conveyors +// +//----------------------------------------------------------------------------- + + +#include +#include "actor.h" +#include "p_spec.h" +#include "farchive.h" +#include "p_lnspec.h" +#include "r_data/r_interpolate.h" + +//----------------------------------------------------------------------------- +// +// killough 3/7/98: Add generalized scroll effects +// +//----------------------------------------------------------------------------- + +class DScroller : public DThinker +{ + DECLARE_CLASS (DScroller, DThinker) + HAS_OBJECT_POINTERS +public: + + DScroller (EScroll type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + DScroller (fixed_t dx, fixed_t dy, const line_t *l, int control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + void Destroy(); + + void Serialize (FArchive &arc); + void Tick (); + + bool AffectsWall (int wallnum) const { return m_Type == EScroll::sc_side && m_Affectee == wallnum; } + int GetWallNum () const { return m_Type == EScroll::sc_side ? m_Affectee : -1; } + void SetRate (fixed_t dx, fixed_t dy) { m_dx = dx; m_dy = dy; } + bool IsType (EScroll type) const { return type == m_Type; } + int GetAffectee () const { return m_Affectee; } + EScrollPos GetScrollParts() const { return m_Parts; } + +protected: + EScroll m_Type; // Type of scroll effect + fixed_t m_dx, m_dy; // (dx,dy) scroll speeds + int m_Affectee; // Number of affected sidedef, sector, tag, or whatever + int m_Control; // Control sector (-1 if none) used to control scrolling + fixed_t m_LastHeight; // Last known height of control sector + fixed_t m_vdx, m_vdy; // Accumulated velocity if accelerative + int m_Accel; // Whether it's accelerative + EScrollPos m_Parts; // Which parts of a sidedef are being scrolled? + TObjPtr m_Interpolations[3]; + +private: + DScroller () + { + } +}; + + +IMPLEMENT_POINTY_CLASS (DScroller) + DECLARE_POINTER (m_Interpolations[0]) + DECLARE_POINTER (m_Interpolations[1]) + DECLARE_POINTER (m_Interpolations[2]) +END_POINTERS + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +inline FArchive &operator<< (FArchive &arc, EScroll &type) +{ + BYTE val = (BYTE)type; + arc << val; + type = (EScroll)val; + return arc; +} + +inline FArchive &operator<< (FArchive &arc, EScrollPos &type) +{ + int val = (int)type; + arc << val; + type = (EScrollPos)val; + return arc; +} + +EScrollPos operator &(EScrollPos one, EScrollPos two) +{ + return EScrollPos(int(one) & int(two)); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void DScroller::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + arc << m_Type + << m_dx << m_dy + << m_Affectee + << m_Control + << m_LastHeight + << m_vdx << m_vdy + << m_Accel + << m_Parts + << m_Interpolations[0] + << m_Interpolations[1] + << m_Interpolations[2]; +} + +//----------------------------------------------------------------------------- +// +// [RH] Compensate for rotated sector textures by rotating the scrolling +// in the opposite direction. +// +//----------------------------------------------------------------------------- + +static void RotationComp(const sector_t *sec, int which, fixed_t dx, fixed_t dy, fixed_t &tdx, fixed_t &tdy) +{ + angle_t an = sec->GetAngle(which); + if (an == 0) + { + tdx = dx; + tdy = dy; + } + else + { + an = an >> ANGLETOFINESHIFT; + fixed_t ca = -finecosine[an]; + fixed_t sa = -finesine[an]; + tdx = DMulScale16(dx, ca, -dy, sa); + tdy = DMulScale16(dy, ca, dx, sa); + } +} + +//----------------------------------------------------------------------------- +// +// killough 2/28/98: +// +// This function, with the help of r_plane.c and r_bsp.c, supports generalized +// scrolling floors and walls, with optional mobj-carrying properties, e.g. +// conveyor belts, rivers, etc. A linedef with a special type affects all +// tagged sectors the same way, by creating scrolling and/or object-carrying +// properties. Multiple linedefs may be used on the same sector and are +// cumulative, although the special case of scrolling a floor and carrying +// things on it, requires only one linedef. The linedef's direction determines +// the scrolling direction, and the linedef's length determines the scrolling +// speed. This was designed so that an edge around the sector could be used to +// control the direction of the sector's scrolling, which is usually what is +// desired. +// +// Process the active scrollers. +// +// This is the main scrolling code +// killough 3/7/98 +// +//----------------------------------------------------------------------------- + +void DScroller::Tick () +{ + fixed_t dx = m_dx, dy = m_dy, tdx, tdy; + + if (m_Control != -1) + { // compute scroll amounts based on a sector's height changes + fixed_t height = sectors[m_Control].CenterFloor () + + sectors[m_Control].CenterCeiling (); + fixed_t delta = height - m_LastHeight; + m_LastHeight = height; + dx = FixedMul(dx, delta); + dy = FixedMul(dy, delta); + } + + // killough 3/14/98: Add acceleration + if (m_Accel) + { + m_vdx = dx += m_vdx; + m_vdy = dy += m_vdy; + } + + if (!(dx | dy)) // no-op if both (x,y) offsets are 0 + return; + + switch (m_Type) + { + case EScroll::sc_side: // killough 3/7/98: Scroll wall texture + if (m_Parts & EScrollPos::scw_top) + { + sides[m_Affectee].AddTextureXOffset(side_t::top, dx); + sides[m_Affectee].AddTextureYOffset(side_t::top, dy); + } + if (m_Parts & EScrollPos::scw_mid && (sides[m_Affectee].linedef->backsector == NULL || + !(sides[m_Affectee].linedef->flags&ML_3DMIDTEX))) + { + sides[m_Affectee].AddTextureXOffset(side_t::mid, dx); + sides[m_Affectee].AddTextureYOffset(side_t::mid, dy); + } + if (m_Parts & EScrollPos::scw_bottom) + { + sides[m_Affectee].AddTextureXOffset(side_t::bottom, dx); + sides[m_Affectee].AddTextureYOffset(side_t::bottom, dy); + } + break; + + case EScroll::sc_floor: // killough 3/7/98: Scroll floor texture + RotationComp(§ors[m_Affectee], sector_t::floor, dx, dy, tdx, tdy); + sectors[m_Affectee].AddXOffset(sector_t::floor, tdx); + sectors[m_Affectee].AddYOffset(sector_t::floor, tdy); + break; + + case EScroll::sc_ceiling: // killough 3/7/98: Scroll ceiling texture + RotationComp(§ors[m_Affectee], sector_t::ceiling, dx, dy, tdx, tdy); + sectors[m_Affectee].AddXOffset(sector_t::ceiling, tdx); + sectors[m_Affectee].AddYOffset(sector_t::ceiling, tdy); + break; + + // [RH] Don't actually carry anything here. That happens later. + case EScroll::sc_carry: + level.Scrolls[m_Affectee].ScrollX += dx; + level.Scrolls[m_Affectee].ScrollY += dy; + break; + + case EScroll::sc_carry_ceiling: // to be added later + break; + } +} + +//----------------------------------------------------------------------------- +// +// Add_Scroller() +// +// Add a generalized scroller to the thinker list. +// +// type: the enumerated type of scrolling: floor, ceiling, floor carrier, +// wall, floor carrier & scroller +// +// (dx,dy): the direction and speed of the scrolling or its acceleration +// +// control: the sector whose heights control this scroller's effect +// remotely, or -1 if no control sector +// +// affectee: the index of the affected object (sector or sidedef) +// +// accel: non-zero if this is an accelerative effect +// +//----------------------------------------------------------------------------- + +DScroller::DScroller (EScroll type, fixed_t dx, fixed_t dy, + int control, int affectee, int accel, EScrollPos scrollpos) + : DThinker (STAT_SCROLLER) +{ + m_Type = type; + m_dx = dx; + m_dy = dy; + m_Accel = accel; + m_Parts = scrollpos; + m_vdx = m_vdy = 0; + if ((m_Control = control) != -1) + m_LastHeight = + sectors[control].CenterFloor () + sectors[control].CenterCeiling (); + m_Affectee = affectee; + m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL; + + switch (type) + { + case EScroll::sc_carry: + level.AddScroller (affectee); + break; + + case EScroll::sc_side: + sides[affectee].Flags |= WALLF_NOAUTODECALS; + if (m_Parts & EScrollPos::scw_top) + { + m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top); + } + if (m_Parts & EScrollPos::scw_mid && (sides[m_Affectee].linedef->backsector == NULL || + !(sides[m_Affectee].linedef->flags&ML_3DMIDTEX))) + { + m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid); + } + if (m_Parts & EScrollPos::scw_bottom) + { + m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom); + } + break; + + case EScroll::sc_floor: + m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::FloorScroll, false); + break; + + case EScroll::sc_ceiling: + m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::CeilingScroll, false); + break; + + default: + break; + } +} + +void DScroller::Destroy () +{ + for(int i=0;i<3;i++) + { + if (m_Interpolations[i] != NULL) + { + m_Interpolations[i]->DelRef(); + m_Interpolations[i] = NULL; + } + } + Super::Destroy(); +} + +//----------------------------------------------------------------------------- +// +// Adds wall scroller. Scroll amount is rotated with respect to wall's +// linedef first, so that scrolling towards the wall in a perpendicular +// direction is translated into vertical motion, while scrolling along +// the wall in a parallel direction is translated into horizontal motion. +// +// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff +// +//----------------------------------------------------------------------------- + +DScroller::DScroller (fixed_t dx, fixed_t dy, const line_t *l, + int control, int accel, EScrollPos scrollpos) + : DThinker (STAT_SCROLLER) +{ + fixed_t x = abs(l->dx), y = abs(l->dy), d; + if (y > x) + d = x, x = y, y = d; + d = FixedDiv (x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90) + >> ANGLETOFINESHIFT]); + x = -FixedDiv (FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d); + y = -FixedDiv (FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d); + + m_Type = EScroll::sc_side; + m_dx = x; + m_dy = y; + m_vdx = m_vdy = 0; + m_Accel = accel; + m_Parts = scrollpos; + if ((m_Control = control) != -1) + m_LastHeight = sectors[control].CenterFloor() + sectors[control].CenterCeiling(); + m_Affectee = int(l->sidedef[0] - sides); + sides[m_Affectee].Flags |= WALLF_NOAUTODECALS; + m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL; + + if (m_Parts & EScrollPos::scw_top) + { + m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top); + } + if (m_Parts & EScrollPos::scw_mid && (sides[m_Affectee].linedef->backsector == NULL || + !(sides[m_Affectee].linedef->flags&ML_3DMIDTEX))) + { + m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid); + } + if (m_Parts & EScrollPos::scw_bottom) + { + m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom); + } +} + +// Amount (dx,dy) vector linedef is shifted right to get scroll amount +#define SCROLL_SHIFT 5 +#define SCROLLTYPE(i) EScrollPos(((i) <= 0) || ((i) & ~7) ? 7 : (i)) + +//----------------------------------------------------------------------------- +// +// Initialize the scrollers +// +//----------------------------------------------------------------------------- + +void P_SpawnScrollers(void) +{ + int i; + line_t *l = lines; + TArray copyscrollers; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == Sector_CopyScroller) + { + // don't allow copying the scroller if the sector has the same tag as it would just duplicate it. + if (!tagManager.SectorHasTag(lines[i].frontsector, lines[i].args[0])) + { + copyscrollers.Push(i); + } + lines[i].special = 0; + } + } + + for (i = 0; i < numlines; i++, l++) + { + fixed_t dx; // direction and speed of scrolling + fixed_t dy; + int control = -1, accel = 0; // no control sector or acceleration + int special = l->special; + + // Check for undefined parameters that are non-zero and output messages for them. + // We don't report for specials we don't understand. + FLineSpecial *spec = P_GetLineSpecialInfo(special); + if (spec != NULL) + { + int max = spec->map_args; + for (unsigned arg = max; arg < countof(l->args); ++arg) + { + if (l->args[arg] != 0) + { + Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n", + i, special, spec->name, arg+1, l->args[arg]); + } + } + } + + // killough 3/7/98: Types 245-249 are same as 250-254 except that the + // first side's sector's heights cause scrolling when they change, and + // this linedef controls the direction and speed of the scrolling. The + // most complicated linedef since donuts, but powerful :) + // + // killough 3/15/98: Add acceleration. Types 214-218 are the same but + // are accelerative. + + // [RH] Assume that it's a scroller and zero the line's special. + l->special = 0; + + dx = dy = 0; // Shut up, GCC + + if (special == Scroll_Ceiling || + special == Scroll_Floor || + special == Scroll_Texture_Model) + { + if (l->args[1] & 3) + { + // if 1, then displacement + // if 2, then accelerative (also if 3) + control = int(l->sidedef[0]->sector - sectors); + if (l->args[1] & 2) + accel = 1; + } + if (special == Scroll_Texture_Model || + l->args[1] & 4) + { + // The line housing the special controls the + // direction and speed of scrolling. + dx = l->dx >> SCROLL_SHIFT; + dy = l->dy >> SCROLL_SHIFT; + } + else + { + // The speed and direction are parameters to the special. + dx = (l->args[3] - 128) * (FRACUNIT / 32); + dy = (l->args[4] - 128) * (FRACUNIT / 32); + } + } + + switch (special) + { + int s; + + case Scroll_Ceiling: + { + FSectorTagIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + { + new DScroller(EScroll::sc_ceiling, -dx, dy, control, s, accel); + } + for (unsigned j = 0; j < copyscrollers.Size(); j++) + { + line_t *line = &lines[copyscrollers[j]]; + + if (line->args[0] == l->args[0] && (line->args[1] & 1)) + { + new DScroller(EScroll::sc_ceiling, -dx, dy, control, int(line->frontsector - sectors), accel); + } + } + break; + } + + case Scroll_Floor: + if (l->args[2] != 1) + { // scroll the floor texture + FSectorTagIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + { + new DScroller (EScroll::sc_floor, -dx, dy, control, s, accel); + } + for(unsigned j = 0;j < copyscrollers.Size(); j++) + { + line_t *line = &lines[copyscrollers[j]]; + + if (line->args[0] == l->args[0] && (line->args[1] & 2)) + { + new DScroller (EScroll::sc_floor, -dx, dy, control, int(line->frontsector-sectors), accel); + } + } + } + + if (l->args[2] > 0) + { // carry objects on the floor + FSectorTagIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + { + new DScroller (EScroll::sc_carry, dx, dy, control, s, accel); + } + for(unsigned j = 0;j < copyscrollers.Size(); j++) + { + line_t *line = &lines[copyscrollers[j]]; + + if (line->args[0] == l->args[0] && (line->args[1] & 4)) + { + new DScroller (EScroll::sc_carry, dx, dy, control, int(line->frontsector-sectors), accel); + } + } + } + break; + + // killough 3/1/98: scroll wall according to linedef + // (same direction and speed as scrolling floors) + case Scroll_Texture_Model: + { + FLineIdIterator itr(l->args[0]); + while ((s = itr.Next()) >= 0) + { + if (s != i) + new DScroller(dx, dy, lines + s, control, accel); + } + break; + } + + case Scroll_Texture_Offsets: + // killough 3/2/98: scroll according to sidedef offsets + s = int(lines[i].sidedef[0] - sides); + new DScroller (EScroll::sc_side, -sides[s].GetTextureXOffset(side_t::mid), + sides[s].GetTextureYOffset(side_t::mid), -1, s, accel, SCROLLTYPE(l->args[0])); + break; + + case Scroll_Texture_Left: + l->special = special; // Restore the special, for compat_useblocking's benefit. + s = int(lines[i].sidedef[0] - sides); + new DScroller (EScroll::sc_side, l->args[0] * (FRACUNIT/64), 0, + -1, s, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Right: + l->special = special; + s = int(lines[i].sidedef[0] - sides); + new DScroller (EScroll::sc_side, l->args[0] * (-FRACUNIT/64), 0, + -1, s, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Up: + l->special = special; + s = int(lines[i].sidedef[0] - sides); + new DScroller (EScroll::sc_side, 0, l->args[0] * (FRACUNIT/64), + -1, s, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Down: + l->special = special; + s = int(lines[i].sidedef[0] - sides); + new DScroller (EScroll::sc_side, 0, l->args[0] * (-FRACUNIT/64), + -1, s, accel, SCROLLTYPE(l->args[1])); + break; + + case Scroll_Texture_Both: + s = int(lines[i].sidedef[0] - sides); + if (l->args[0] == 0) { + dx = (l->args[1] - l->args[2]) * (FRACUNIT/64); + dy = (l->args[4] - l->args[3]) * (FRACUNIT/64); + new DScroller (EScroll::sc_side, dx, dy, -1, s, accel); + } + break; + + default: + // [RH] It wasn't a scroller after all, so restore the special. + l->special = special; + break; + } + } +} + +//----------------------------------------------------------------------------- +// +// Modify a wall scroller +// +//----------------------------------------------------------------------------- + +void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, EScrollPos Where) +{ + Where = Where & scw_all; + if (Where == 0) return; + + if ((dx | dy) == 0) + { + // Special case: Remove the scroller, because the deltas are both 0. + TThinkerIterator iterator (STAT_SCROLLER); + DScroller *scroller; + + while ( (scroller = iterator.Next ()) ) + { + int wallnum = scroller->GetWallNum (); + + if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) && + int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum && + Where == scroller->GetScrollParts()) + { + scroller->Destroy (); + } + } + } + else + { + // Find scrollers already attached to the matching walls, and change + // their rates. + TArray Collection; + { + TThinkerIterator iterator (STAT_SCROLLER); + FThinkerCollection collect; + + while ( (collect.Obj = iterator.Next ()) ) + { + if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 && + tagManager.LineHasID(sides[collect.RefNum].linedef, id) && + int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum && + Where == ((DScroller *)collect.Obj)->GetScrollParts()) + { + ((DScroller *)collect.Obj)->SetRate (dx, dy); + Collection.Push (collect); + } + } + } + + size_t numcollected = Collection.Size (); + int linenum; + + // Now create scrollers for any walls that don't already have them. + FLineIdIterator itr(id); + while ((linenum = itr.Next()) >= 0) + { + if (lines[linenum].sidedef[sidechoice] != NULL) + { + int sidenum = int(lines[linenum].sidedef[sidechoice] - sides); + unsigned int i; + for (i = 0; i < numcollected; i++) + { + if (Collection[i].RefNum == sidenum) + break; + } + if (i == numcollected) + { + new DScroller (EScroll::sc_side, dx, dy, -1, sidenum, 0, Where); + } + } + } + } +} + +void SetScroller (int tag, EScroll type, fixed_t dx, fixed_t dy) +{ + TThinkerIterator iterator (STAT_SCROLLER); + DScroller *scroller; + int i; + + // Check if there is already a scroller for this tag + // If at least one sector with this tag is scrolling, then they all are. + // If the deltas are both 0, we don't remove the scroller, because a + // displacement/accelerative scroller might have been set up, and there's + // no way to create one after the level is fully loaded. + i = 0; + while ( (scroller = iterator.Next ()) ) + { + if (scroller->IsType (type)) + { + if (tagManager.SectorHasTag(scroller->GetAffectee (), tag)) + { + i++; + scroller->SetRate (dx, dy); + } + } + } + + if (i > 0 || (dx|dy) == 0) + { + return; + } + + // Need to create scrollers for the sector(s) + FSectorTagIterator itr(tag); + while ((i = itr.Next()) >= 0) + { + new DScroller (type, dx, dy, -1, i, 0); + } +} + +void P_CreateScroller(EScroll type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, EScrollPos scrollpos) +{ + new DScroller(type, dx, dy, control, affectee, accel, scrollpos); +} diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 27c8493ea6..25cd0ead8c 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -952,7 +952,7 @@ fixed_t sector_t::NextHighestCeilingAt(fixed_t x, fixed_t y, fixed_t bottomz, fi fixed_t delta1 = bottomz - (ff_bottom + ((ff_top - ff_bottom) / 2)); fixed_t delta2 = topz - (ff_bottom + ((ff_top - ff_bottom) / 2)); - if (ff_bottom < realceil && abs(delta1) >= abs(delta2)) + if (ff_bottom < realceil && abs(delta1) > abs(delta2)) { if (resultsec) *resultsec = sec; if (resultffloor) *resultffloor = rover; diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 81b1899bae..b7edc99b3c 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -84,74 +84,11 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); EXTERN_CVAR(Bool, cl_predict_specials) -IMPLEMENT_POINTY_CLASS (DScroller) - DECLARE_POINTER (m_Interpolations[0]) - DECLARE_POINTER (m_Interpolations[1]) - DECLARE_POINTER (m_Interpolations[2]) -END_POINTERS - -IMPLEMENT_POINTY_CLASS (DPusher) - DECLARE_POINTER (m_Source) -END_POINTERS - -inline FArchive &operator<< (FArchive &arc, DScroller::EScrollType &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DScroller::EScrollType)val; - return arc; -} - -DScroller::DScroller () -{ -} - -void DScroller::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << m_Type - << m_dx << m_dy - << m_Affectee - << m_Control - << m_LastHeight - << m_vdx << m_vdy - << m_Accel - << m_Parts - << m_Interpolations[0] - << m_Interpolations[1] - << m_Interpolations[2]; -} - -DPusher::DPusher () -{ -} - -inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DPusher::EPusher)val; - return arc; -} - -void DPusher::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << m_Type - << m_Source - << m_Xmag - << m_Ymag - << m_Magnitude - << m_Radius - << m_X - << m_Y - << m_Affectee; -} // killough 3/7/98: Initialize generalized scrolling -static void P_SpawnScrollers(); +void P_SpawnScrollers(); static void P_SpawnFriction (); // phares 3/16/98 -static void P_SpawnPushers (); // phares 3/20/98 +void P_SpawnPushers (); // phares 3/20/98 // [RH] Check dmflags for noexit and respond accordingly @@ -1230,7 +1167,7 @@ void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers) break; case dSector_DoorRaiseIn5Mins: - new DDoor (sector, DDoor::doorWaitRaise, 2*FRACUNIT, TICRATE*30/7, 5*60*TICRATE, 0); + new DDoor (sector, DDoor::doorWaitRaise, 2*FRACUNIT, TICRATE*30/7, 0, 5*60*TICRATE); break; case dFriction_Low: @@ -1260,7 +1197,7 @@ void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers) if (!nothinkers) { new DStrobe(sector, STROBEBRIGHT, FASTDARK, false); - new DScroller(DScroller::sc_floor, -((FRACUNIT / 2) << 3), + P_CreateScroller(EScroll::sc_floor, -((FRACUNIT / 2) << 3), 0, -1, int(sector - sectors), 0); } keepspecial = true; @@ -1321,13 +1258,13 @@ void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers) int i = sector->special - Scroll_North_Slow; fixed_t dx = hexenScrollies[i][0] * (FRACUNIT/2); fixed_t dy = hexenScrollies[i][1] * (FRACUNIT/2); - if (!nothinkers) new DScroller (DScroller::sc_floor, dx, dy, -1, int(sector-sectors), 0); + if (!nothinkers) P_CreateScroller(EScroll::sc_floor, dx, dy, -1, int(sector-sectors), 0); } else if (sector->special >= Carry_East5 && sector->special <= Carry_East35) { // Heretic scroll special // Only east scrollers also scroll the texture - if (!nothinkers) new DScroller (DScroller::sc_floor, + if (!nothinkers) P_CreateScroller(EScroll::sc_floor, (-FRACUNIT/2)<<(sector->special - Carry_East5), 0, -1, int(sector-sectors), 0); } @@ -1560,458 +1497,6 @@ void P_SpawnSpecials (void) FBehavior::StaticStartTypedScripts (SCRIPT_Open, NULL, false); } -// killough 2/28/98: -// -// This function, with the help of r_plane.c and r_bsp.c, supports generalized -// scrolling floors and walls, with optional mobj-carrying properties, e.g. -// conveyor belts, rivers, etc. A linedef with a special type affects all -// tagged sectors the same way, by creating scrolling and/or object-carrying -// properties. Multiple linedefs may be used on the same sector and are -// cumulative, although the special case of scrolling a floor and carrying -// things on it, requires only one linedef. The linedef's direction determines -// the scrolling direction, and the linedef's length determines the scrolling -// speed. This was designed so that an edge around the sector could be used to -// control the direction of the sector's scrolling, which is usually what is -// desired. -// -// Process the active scrollers. -// -// This is the main scrolling code -// killough 3/7/98 - -// [RH] Compensate for rotated sector textures by rotating the scrolling -// in the opposite direction. -static void RotationComp(const sector_t *sec, int which, fixed_t dx, fixed_t dy, fixed_t &tdx, fixed_t &tdy) -{ - angle_t an = sec->GetAngle(which); - if (an == 0) - { - tdx = dx; - tdy = dy; - } - else - { - an = an >> ANGLETOFINESHIFT; - fixed_t ca = -finecosine[an]; - fixed_t sa = -finesine[an]; - tdx = DMulScale16(dx, ca, -dy, sa); - tdy = DMulScale16(dy, ca, dx, sa); - } -} - -void DScroller::Tick () -{ - fixed_t dx = m_dx, dy = m_dy, tdx, tdy; - - if (m_Control != -1) - { // compute scroll amounts based on a sector's height changes - fixed_t height = sectors[m_Control].CenterFloor () + - sectors[m_Control].CenterCeiling (); - fixed_t delta = height - m_LastHeight; - m_LastHeight = height; - dx = FixedMul(dx, delta); - dy = FixedMul(dy, delta); - } - - // killough 3/14/98: Add acceleration - if (m_Accel) - { - m_vdx = dx += m_vdx; - m_vdy = dy += m_vdy; - } - - if (!(dx | dy)) // no-op if both (x,y) offsets are 0 - return; - - switch (m_Type) - { - case sc_side: // killough 3/7/98: Scroll wall texture - if (m_Parts & scw_top) - { - sides[m_Affectee].AddTextureXOffset(side_t::top, dx); - sides[m_Affectee].AddTextureYOffset(side_t::top, dy); - } - if (m_Parts & scw_mid && (sides[m_Affectee].linedef->backsector == NULL || - !(sides[m_Affectee].linedef->flags&ML_3DMIDTEX))) - { - sides[m_Affectee].AddTextureXOffset(side_t::mid, dx); - sides[m_Affectee].AddTextureYOffset(side_t::mid, dy); - } - if (m_Parts & scw_bottom) - { - sides[m_Affectee].AddTextureXOffset(side_t::bottom, dx); - sides[m_Affectee].AddTextureYOffset(side_t::bottom, dy); - } - break; - - case sc_floor: // killough 3/7/98: Scroll floor texture - RotationComp(§ors[m_Affectee], sector_t::floor, dx, dy, tdx, tdy); - sectors[m_Affectee].AddXOffset(sector_t::floor, tdx); - sectors[m_Affectee].AddYOffset(sector_t::floor, tdy); - break; - - case sc_ceiling: // killough 3/7/98: Scroll ceiling texture - RotationComp(§ors[m_Affectee], sector_t::ceiling, dx, dy, tdx, tdy); - sectors[m_Affectee].AddXOffset(sector_t::ceiling, tdx); - sectors[m_Affectee].AddYOffset(sector_t::ceiling, tdy); - break; - - // [RH] Don't actually carry anything here. That happens later. - case sc_carry: - level.Scrolls[m_Affectee].ScrollX += dx; - level.Scrolls[m_Affectee].ScrollY += dy; - break; - - case sc_carry_ceiling: // to be added later - break; - } -} - -// -// Add_Scroller() -// -// Add a generalized scroller to the thinker list. -// -// type: the enumerated type of scrolling: floor, ceiling, floor carrier, -// wall, floor carrier & scroller -// -// (dx,dy): the direction and speed of the scrolling or its acceleration -// -// control: the sector whose heights control this scroller's effect -// remotely, or -1 if no control sector -// -// affectee: the index of the affected object (sector or sidedef) -// -// accel: non-zero if this is an accelerative effect -// - -DScroller::DScroller (EScrollType type, fixed_t dx, fixed_t dy, - int control, int affectee, int accel, int scrollpos) - : DThinker (STAT_SCROLLER) -{ - m_Type = type; - m_dx = dx; - m_dy = dy; - m_Accel = accel; - m_Parts = scrollpos; - m_vdx = m_vdy = 0; - if ((m_Control = control) != -1) - m_LastHeight = - sectors[control].CenterFloor () + sectors[control].CenterCeiling (); - m_Affectee = affectee; - m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL; - - switch (type) - { - case sc_carry: - level.AddScroller (this, affectee); - break; - - case sc_side: - sides[affectee].Flags |= WALLF_NOAUTODECALS; - if (m_Parts & scw_top) - { - m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top); - } - if (m_Parts & scw_mid && (sides[m_Affectee].linedef->backsector == NULL || - !(sides[m_Affectee].linedef->flags&ML_3DMIDTEX))) - { - m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid); - } - if (m_Parts & scw_bottom) - { - m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom); - } - break; - - case sc_floor: - m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::FloorScroll, false); - break; - - case sc_ceiling: - m_Interpolations[0] = sectors[affectee].SetInterpolation(sector_t::CeilingScroll, false); - break; - - default: - break; - } -} - -void DScroller::Destroy () -{ - for(int i=0;i<3;i++) - { - if (m_Interpolations[i] != NULL) - { - m_Interpolations[i]->DelRef(); - m_Interpolations[i] = NULL; - } - } - Super::Destroy(); -} - -// Adds wall scroller. Scroll amount is rotated with respect to wall's -// linedef first, so that scrolling towards the wall in a perpendicular -// direction is translated into vertical motion, while scrolling along -// the wall in a parallel direction is translated into horizontal motion. -// -// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff - -DScroller::DScroller (fixed_t dx, fixed_t dy, const line_t *l, - int control, int accel, int scrollpos) - : DThinker (STAT_SCROLLER) -{ - fixed_t x = abs(l->dx), y = abs(l->dy), d; - if (y > x) - d = x, x = y, y = d; - d = FixedDiv (x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90) - >> ANGLETOFINESHIFT]); - x = -FixedDiv (FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d); - y = -FixedDiv (FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d); - - m_Type = sc_side; - m_dx = x; - m_dy = y; - m_vdx = m_vdy = 0; - m_Accel = accel; - m_Parts = scrollpos; - if ((m_Control = control) != -1) - m_LastHeight = sectors[control].CenterFloor() + sectors[control].CenterCeiling(); - m_Affectee = int(l->sidedef[0] - sides); - sides[m_Affectee].Flags |= WALLF_NOAUTODECALS; - m_Interpolations[0] = m_Interpolations[1] = m_Interpolations[2] = NULL; - - if (m_Parts & scw_top) - { - m_Interpolations[0] = sides[m_Affectee].SetInterpolation(side_t::top); - } - if (m_Parts & scw_mid && (sides[m_Affectee].linedef->backsector == NULL || - !(sides[m_Affectee].linedef->flags&ML_3DMIDTEX))) - { - m_Interpolations[1] = sides[m_Affectee].SetInterpolation(side_t::mid); - } - if (m_Parts & scw_bottom) - { - m_Interpolations[2] = sides[m_Affectee].SetInterpolation(side_t::bottom); - } -} - -// Amount (dx,dy) vector linedef is shifted right to get scroll amount -#define SCROLL_SHIFT 5 -#define SCROLLTYPE(i) (((i) <= 0) || ((i) & ~7) ? 7 : (i)) - -// Initialize the scrollers -static void P_SpawnScrollers(void) -{ - int i; - line_t *l = lines; - TArray copyscrollers; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == Sector_CopyScroller) - { - // don't allow copying the scroller if the sector has the same tag as it would just duplicate it. - if (!tagManager.SectorHasTag(lines[i].frontsector, lines[i].args[0])) - { - copyscrollers.Push(i); - } - lines[i].special = 0; - } - } - - for (i = 0; i < numlines; i++, l++) - { - fixed_t dx; // direction and speed of scrolling - fixed_t dy; - int control = -1, accel = 0; // no control sector or acceleration - int special = l->special; - - // Check for undefined parameters that are non-zero and output messages for them. - // We don't report for specials we don't understand. - FLineSpecial *spec = P_GetLineSpecialInfo(special); - if (spec != NULL) - { - int max = spec->map_args; - for (unsigned arg = max; arg < countof(l->args); ++arg) - { - if (l->args[arg] != 0) - { - Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n", - i, special, spec->name, arg+1, l->args[arg]); - } - } - } - - // killough 3/7/98: Types 245-249 are same as 250-254 except that the - // first side's sector's heights cause scrolling when they change, and - // this linedef controls the direction and speed of the scrolling. The - // most complicated linedef since donuts, but powerful :) - // - // killough 3/15/98: Add acceleration. Types 214-218 are the same but - // are accelerative. - - // [RH] Assume that it's a scroller and zero the line's special. - l->special = 0; - - dx = dy = 0; // Shut up, GCC - - if (special == Scroll_Ceiling || - special == Scroll_Floor || - special == Scroll_Texture_Model) - { - if (l->args[1] & 3) - { - // if 1, then displacement - // if 2, then accelerative (also if 3) - control = int(l->sidedef[0]->sector - sectors); - if (l->args[1] & 2) - accel = 1; - } - if (special == Scroll_Texture_Model || - l->args[1] & 4) - { - // The line housing the special controls the - // direction and speed of scrolling. - dx = l->dx >> SCROLL_SHIFT; - dy = l->dy >> SCROLL_SHIFT; - } - else - { - // The speed and direction are parameters to the special. - dx = (l->args[3] - 128) * (FRACUNIT / 32); - dy = (l->args[4] - 128) * (FRACUNIT / 32); - } - } - - switch (special) - { - int s; - - case Scroll_Ceiling: - { - FSectorTagIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - { - new DScroller(DScroller::sc_ceiling, -dx, dy, control, s, accel); - } - for (unsigned j = 0; j < copyscrollers.Size(); j++) - { - line_t *line = &lines[copyscrollers[j]]; - - if (line->args[0] == l->args[0] && (line->args[1] & 1)) - { - new DScroller(DScroller::sc_ceiling, -dx, dy, control, int(line->frontsector - sectors), accel); - } - } - break; - } - - case Scroll_Floor: - if (l->args[2] != 1) - { // scroll the floor texture - FSectorTagIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - { - new DScroller (DScroller::sc_floor, -dx, dy, control, s, accel); - } - for(unsigned j = 0;j < copyscrollers.Size(); j++) - { - line_t *line = &lines[copyscrollers[j]]; - - if (line->args[0] == l->args[0] && (line->args[1] & 2)) - { - new DScroller (DScroller::sc_floor, -dx, dy, control, int(line->frontsector-sectors), accel); - } - } - } - - if (l->args[2] > 0) - { // carry objects on the floor - FSectorTagIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - { - new DScroller (DScroller::sc_carry, dx, dy, control, s, accel); - } - for(unsigned j = 0;j < copyscrollers.Size(); j++) - { - line_t *line = &lines[copyscrollers[j]]; - - if (line->args[0] == l->args[0] && (line->args[1] & 4)) - { - new DScroller (DScroller::sc_carry, dx, dy, control, int(line->frontsector-sectors), accel); - } - } - } - break; - - // killough 3/1/98: scroll wall according to linedef - // (same direction and speed as scrolling floors) - case Scroll_Texture_Model: - { - FLineIdIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - { - if (s != i) - new DScroller(dx, dy, lines + s, control, accel); - } - break; - } - - case Scroll_Texture_Offsets: - // killough 3/2/98: scroll according to sidedef offsets - s = int(lines[i].sidedef[0] - sides); - new DScroller (DScroller::sc_side, -sides[s].GetTextureXOffset(side_t::mid), - sides[s].GetTextureYOffset(side_t::mid), -1, s, accel, SCROLLTYPE(l->args[0])); - break; - - case Scroll_Texture_Left: - l->special = special; // Restore the special, for compat_useblocking's benefit. - s = int(lines[i].sidedef[0] - sides); - new DScroller (DScroller::sc_side, l->args[0] * (FRACUNIT/64), 0, - -1, s, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Right: - l->special = special; - s = int(lines[i].sidedef[0] - sides); - new DScroller (DScroller::sc_side, l->args[0] * (-FRACUNIT/64), 0, - -1, s, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Up: - l->special = special; - s = int(lines[i].sidedef[0] - sides); - new DScroller (DScroller::sc_side, 0, l->args[0] * (FRACUNIT/64), - -1, s, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Down: - l->special = special; - s = int(lines[i].sidedef[0] - sides); - new DScroller (DScroller::sc_side, 0, l->args[0] * (-FRACUNIT/64), - -1, s, accel, SCROLLTYPE(l->args[1])); - break; - - case Scroll_Texture_Both: - s = int(lines[i].sidedef[0] - sides); - if (l->args[0] == 0) { - dx = (l->args[1] - l->args[2]) * (FRACUNIT/64); - dy = (l->args[4] - l->args[3]) * (FRACUNIT/64); - new DScroller (DScroller::sc_side, dx, dy, -1, s, accel); - } - break; - - default: - // [RH] It wasn't a scroller after all, so restore the special. - l->special = special; - break; - } - } -} - -// killough 3/7/98 -- end generalized scroll effects - //////////////////////////////////////////////////////////////////////////// // // FRICTION EFFECTS @@ -2144,345 +1629,6 @@ void P_SetSectorFriction (int tag, int amount, bool alterFlag) // //////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// -// -// PUSH/PULL EFFECT -// -// phares 3/20/98: Start of push/pull effects -// -// This is where push/pull effects are applied to objects in the sectors. -// -// There are four kinds of push effects -// -// 1) Pushing Away -// -// Pushes you away from a point source defined by the location of an -// MT_PUSH Thing. The force decreases linearly with distance from the -// source. This force crosses sector boundaries and is felt w/in a circle -// whose center is at the MT_PUSH. The force is felt only if the point -// MT_PUSH can see the target object. -// -// 2) Pulling toward -// -// Same as Pushing Away except you're pulled toward an MT_PULL point -// source. This force crosses sector boundaries and is felt w/in a circle -// whose center is at the MT_PULL. The force is felt only if the point -// MT_PULL can see the target object. -// -// 3) Wind -// -// Pushes you in a constant direction. Full force above ground, half -// force on the ground, nothing if you're below it (water). -// -// 4) Current -// -// Pushes you in a constant direction. No force above ground, full -// force if on the ground or below it (water). -// -// The magnitude of the force is controlled by the length of a controlling -// linedef. The force vector for types 3 & 4 is determined by the angle -// of the linedef, and is constant. -// -// For each sector where these effects occur, the sector special type has -// to have the PUSH_MASK bit set. If this bit is turned off by a switch -// at run-time, the effect will not occur. The controlling sector for -// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing. - - -#define PUSH_FACTOR 7 - -///////////////////////////// -// -// Add a push thinker to the thinker list - -DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle, - AActor *source, int affectee) -{ - m_Source = source; - m_Type = type; - if (l) - { - m_Xmag = l->dx>>FRACBITS; - m_Ymag = l->dy>>FRACBITS; - m_Magnitude = P_AproxDistance (m_Xmag, m_Ymag); - } - else - { // [RH] Allow setting magnitude and angle with parameters - ChangeValues (magnitude, angle); - } - if (source) // point source exist? - { - m_Radius = (m_Magnitude) << (FRACBITS+1); // where force goes to zero - m_X = m_Source->X(); - m_Y = m_Source->Y(); - } - m_Affectee = affectee; -} - -int DPusher::CheckForSectorMatch (EPusher type, int tag) -{ - if (m_Type == type && tagManager.SectorHasTag(m_Affectee, tag)) - return m_Affectee; - else - return -1; -} - - -///////////////////////////// -// -// T_Pusher looks for all objects that are inside the radius of -// the effect. -// -void DPusher::Tick () -{ - sector_t *sec; - AActor *thing; - msecnode_t *node; - int xspeed,yspeed; - int ht; - - if (!var_pushers) - return; - - sec = sectors + m_Affectee; - - // Be sure the special sector type is still turned on. If so, proceed. - // Else, bail out; the sector type has been changed on us. - - if (!(sec->Flags & SECF_PUSH)) - return; - - // For constant pushers (wind/current) there are 3 situations: - // - // 1) Affected Thing is above the floor. - // - // Apply the full force if wind, no force if current. - // - // 2) Affected Thing is on the ground. - // - // Apply half force if wind, full force if current. - // - // 3) Affected Thing is below the ground (underwater effect). - // - // Apply no force if wind, full force if current. - // - // Apply the effect to clipped players only for now. - // - // In Phase II, you can apply these effects to Things other than players. - // [RH] No Phase II, but it works with anything having MF2_WINDTHRUST now. - - if (m_Type == p_push) - { - // Seek out all pushable things within the force radius of this - // point pusher. Crosses sectors, so use blockmap. - - FPortalGroupArray check(FPortalGroupArray::PGA_NoSectorPortals); // no sector portals because this thing is utterly z-unaware. - FMultiBlockThingsIterator it(check, m_X, m_Y, 0, 0, m_Radius, false, m_Source->Sector); - FMultiBlockThingsIterator::CheckResult cres; - - - while (it.Next(&cres)) - { - AActor *thing = cres.thing; - // Normal ZDoom is based only on the WINDTHRUST flag, with the noclip cheat as an exemption. - bool pusharound = ((thing->flags2 & MF2_WINDTHRUST) && !(thing->flags & MF_NOCLIP)); - - // MBF allows any sentient or shootable thing to be affected, but players with a fly cheat aren't. - if (compatflags & COMPATF_MBFMONSTERMOVE) - { - pusharound = ((pusharound || (thing->IsSentient()) || (thing->flags & MF_SHOOTABLE)) // Add categories here - && (!(thing->player && (thing->flags & (MF_NOGRAVITY))))); // Exclude flying players here - } - - if ((pusharound) ) - { - int sx = m_X; - int sy = m_Y; - int dist = thing->AproxDistance (sx, sy); - int speed = (m_Magnitude - ((dist>>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); - - // If speed <= 0, you're outside the effective radius. You also have - // to be able to see the push/pull source point. - - if ((speed > 0) && (P_CheckSight (thing, m_Source, SF_IGNOREVISIBILITY))) - { - angle_t pushangle = thing->AngleTo(sx, sy); - if (m_Source->GetClass()->TypeName == NAME_PointPusher) - pushangle += ANG180; // away - pushangle >>= ANGLETOFINESHIFT; - thing->vel.x += FixedMul (speed, finecosine[pushangle]); - thing->vel.y += FixedMul (speed, finesine[pushangle]); - } - } - } - return; - } - - // constant pushers p_wind and p_current - - node = sec->touching_thinglist; // things touching this sector - for ( ; node ; node = node->m_snext) - { - thing = node->m_thing; - if (!(thing->flags2 & MF2_WINDTHRUST) || (thing->flags & MF_NOCLIP)) - continue; - - sector_t *hsec = sec->GetHeightSec(); - fixedvec3 pos = thing->PosRelative(sec); - if (m_Type == p_wind) - { - if (hsec == NULL) - { // NOT special water sector - if (thing->Z() > thing->floorz) // above ground - { - xspeed = m_Xmag; // full force - yspeed = m_Ymag; - } - else // on ground - { - xspeed = (m_Xmag)>>1; // half force - yspeed = (m_Ymag)>>1; - } - } - else // special water sector - { - ht = hsec->floorplane.ZatPoint(pos); - if (thing->Z() > ht) // above ground - { - xspeed = m_Xmag; // full force - yspeed = m_Ymag; - } - else if (thing->player->viewz < ht) // underwater - { - xspeed = yspeed = 0; // no force - } - else // wading in water - { - xspeed = (m_Xmag)>>1; // half force - yspeed = (m_Ymag)>>1; - } - } - } - else // p_current - { - const secplane_t *floor; - - if (hsec == NULL) - { // NOT special water sector - floor = &sec->floorplane; - } - else - { // special water sector - floor = &hsec->floorplane; - } - if (thing->Z() > floor->ZatPoint(pos)) - { // above ground - xspeed = yspeed = 0; // no force - } - else - { // on ground/underwater - xspeed = m_Xmag; // full force - yspeed = m_Ymag; - } - } - thing->vel.x += xspeed<<(FRACBITS-PUSH_FACTOR); - thing->vel.y += yspeed<<(FRACBITS-PUSH_FACTOR); - } -} - -///////////////////////////// -// -// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, -// NULL otherwise. - -AActor *P_GetPushThing (int s) -{ - AActor* thing; - sector_t* sec; - - sec = sectors + s; - thing = sec->thinglist; - - while (thing && - thing->GetClass()->TypeName != NAME_PointPusher && - thing->GetClass()->TypeName != NAME_PointPuller) - { - thing = thing->snext; - } - return thing; -} - -///////////////////////////// -// -// Initialize the sectors where pushers are present -// - -static void P_SpawnPushers () -{ - int i; - line_t *l = lines; - int s; - - for (i = 0; i < numlines; i++, l++) - { - switch (l->special) - { - case Sector_SetWind: // wind - { - FSectorTagIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - new DPusher(DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s); - l->special = 0; - break; - } - - case Sector_SetCurrent: // current - { - FSectorTagIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - new DPusher(DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s); - l->special = 0; - break; - } - - case PointPush_SetForce: // push/pull - if (l->args[0]) { // [RH] Find thing by sector - FSectorTagIterator itr(l->args[0]); - while ((s = itr.Next()) >= 0) - { - AActor *thing = P_GetPushThing (s); - if (thing) { // No MT_P* means no effect - // [RH] Allow narrowing it down by tid - if (!l->args[1] || l->args[1] == thing->tid) - new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2], - 0, thing, s); - } - } - } else { // [RH] Find thing by tid - AActor *thing; - FActorIterator iterator (l->args[1]); - - while ( (thing = iterator.Next ()) ) - { - if (thing->GetClass()->TypeName == NAME_PointPusher || - thing->GetClass()->TypeName == NAME_PointPuller) - { - new DPusher (DPusher::p_push, l->args[3] ? l : NULL, l->args[2], - 0, thing, int(thing->Sector - sectors)); - } - } - } - l->special = 0; - break; - } - } -} - -// -// phares 3/20/98: End of Pusher effects -// -//////////////////////////////////////////////////////////////////////////// - void sector_t::AdjustFloorClip () const { msecnode_t *node; diff --git a/src/p_spec.h b/src/p_spec.h index e3b1c05ad7..73f913a00a 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -32,6 +32,32 @@ class FScanner; struct level_info_t; +struct FThinkerCollection +{ + int RefNum; + DThinker *Obj; +}; + +enum class EScroll : int +{ + sc_side, + sc_floor, + sc_ceiling, + sc_carry, + sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings +}; + +enum EScrollPos : int +{ + scw_top = 1, + scw_mid = 2, + scw_bottom = 4, + scw_all = 7, +}; + +void P_CreateScroller(EScroll type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + + //jff 2/23/98 identify the special classes that can share sectors typedef enum @@ -41,107 +67,10 @@ typedef enum lighting_special, } special_e; -// killough 3/7/98: Add generalized scroll effects - -class DScroller : public DThinker -{ - DECLARE_CLASS (DScroller, DThinker) - HAS_OBJECT_POINTERS -public: - enum EScrollType - { - sc_side, - sc_floor, - sc_ceiling, - sc_carry, - sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings - }; - enum EScrollPos - { - scw_top=1, - scw_mid=2, - scw_bottom=4, - scw_all=7, - }; - - DScroller (EScrollType type, fixed_t dx, fixed_t dy, int control, int affectee, int accel, int scrollpos = scw_all); - DScroller (fixed_t dx, fixed_t dy, const line_t *l, int control, int accel, int scrollpos = scw_all); - void Destroy(); - - void Serialize (FArchive &arc); - void Tick (); - - bool AffectsWall (int wallnum) const { return m_Type == sc_side && m_Affectee == wallnum; } - int GetWallNum () const { return m_Type == sc_side ? m_Affectee : -1; } - void SetRate (fixed_t dx, fixed_t dy) { m_dx = dx; m_dy = dy; } - bool IsType (EScrollType type) const { return type == m_Type; } - int GetAffectee () const { return m_Affectee; } - int GetScrollParts() const { return m_Parts; } - -protected: - EScrollType m_Type; // Type of scroll effect - fixed_t m_dx, m_dy; // (dx,dy) scroll speeds - int m_Affectee; // Number of affected sidedef, sector, tag, or whatever - int m_Control; // Control sector (-1 if none) used to control scrolling - fixed_t m_LastHeight; // Last known height of control sector - fixed_t m_vdx, m_vdy; // Accumulated velocity if accelerative - int m_Accel; // Whether it's accelerative - int m_Parts; // Which parts of a sidedef are being scrolled? - TObjPtr m_Interpolations[3]; - -private: - DScroller (); -}; - // Factor to scale scrolling effect into mobj-carrying properties = 3/32. // (This is so scrolling floors and objects on them can move at same speed.) enum { CARRYFACTOR = (3*FRACUNIT >> 5) }; -// phares 3/20/98: added new model of Pushers for push/pull effects - -class DPusher : public DThinker -{ - DECLARE_CLASS (DPusher, DThinker) - HAS_OBJECT_POINTERS -public: - enum EPusher - { - p_push, - p_pull, - p_wind, - p_current - }; - - DPusher (); - DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); - void Serialize (FArchive &arc); - int CheckForSectorMatch (EPusher type, int tag); - void ChangeValues (int magnitude, int angle) - { - angle_t ang = ((angle_t)(angle<<24)) >> ANGLETOFINESHIFT; - m_Xmag = (magnitude * finecosine[ang]) >> FRACBITS; - m_Ymag = (magnitude * finesine[ang]) >> FRACBITS; - m_Magnitude = magnitude; - } - - void Tick (); - -protected: - EPusher m_Type; - TObjPtr m_Source;// Point source if point pusher - int m_Xmag; // X Strength - int m_Ymag; // Y Strength - int m_Magnitude; // Vector strength for point pusher - int m_Radius; // Effective radius for point pusher - int m_X; // X of point source if point pusher - int m_Y; // Y of point source if point pusher - int m_Affectee; // Number of affected sector - - friend bool PIT_PushThing (AActor *thing); -}; - -bool PIT_PushThing (AActor *thing); - // Define values for map objects #define MO_TELEPORTMAN 14 @@ -535,7 +464,7 @@ public: }; DDoor (sector_t *sector); - DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int topcountdown, int lightTag); + DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int lightTag, int topcountdown); void Serialize (FArchive &arc); void Tick (); @@ -647,6 +576,14 @@ public: genCeilingChg }; + enum class ECrushMode + { + crushDoom = 0, + crushHexen = 1, + crushSlowdown = 2 + }; + + DCeiling (sector_t *sec); DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent); @@ -655,7 +592,7 @@ public: static DCeiling *Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, fixed_t speed, fixed_t speed2, fixed_t height, - int crush, int silent, int change, bool hexencrush); + int crush, int silent, int change, ECrushMode hexencrush); protected: ECeiling m_Type; @@ -665,7 +602,7 @@ protected: fixed_t m_Speed1; // [RH] dnspeed of crushers fixed_t m_Speed2; // [RH] upspeed of crushers int m_Crush; - bool m_Hexencrush; + ECrushMode m_CrushMode; int m_Silent; int m_Direction; // 1 = up, 0 = waiting, -1 = down @@ -688,7 +625,7 @@ private: bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, fixed_t speed, fixed_t speed2, fixed_t height, - int crush, int silent, int change, bool hexencrush); + int crush, int silent, int change, DCeiling::ECrushMode hexencrush = DCeiling::ECrushMode::crushDoom); bool EV_CeilingCrushStop (int tag); void P_ActivateInStasisCeiling (int tag); @@ -944,7 +881,7 @@ void P_DoDeferedScripts (void); // // [RH] p_quake.c // -bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ); +bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint); bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx); #endif diff --git a/src/po_man.h b/src/po_man.h index be3b958b26..6ab7d20a73 100644 --- a/src/po_man.h +++ b/src/po_man.h @@ -5,6 +5,7 @@ #include "r_defs.h" #include "m_bbox.h" +class DPolyAction; struct FPolyVertex { diff --git a/src/portal.cpp b/src/portal.cpp index de2d1d14bf..ad8e166b10 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -155,6 +155,8 @@ static void BuildBlockmap() void FLinePortalTraverse::AddLineIntercepts(int bx, int by) { + if (by < 0 || by >= bmapheight || bx < 0 || bx >= bmapwidth) return; + FPortalBlock &block = PortalBlockmap(bx, by); for (unsigned i = 0; iangle) >> ANGLETOFINESHIFT; - fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityX, jiggers.RelOffsetX); + fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityX, jiggers.RelOffsetX, jiggers.Falloff, jiggers.WFalloff); viewx += FixedMul(finecosine[ang], power); viewy += FixedMul(finesine[ang], power); } if ((jiggers.RelIntensityY | jiggers.RelOffsetY) != 0) { int ang = (camera->angle + ANG90) >> ANGLETOFINESHIFT; - fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityY, jiggers.RelOffsetY); + fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityY, jiggers.RelOffsetY, jiggers.Falloff, jiggers.WFalloff); viewx += FixedMul(finecosine[ang], power); viewy += FixedMul(finesine[ang], power); } // FIXME: Relative Z is not relative - // [MC]On it! Will be introducing pitch after QF_WAVE. if ((jiggers.RelIntensityZ | jiggers.RelOffsetZ) != 0) { - viewz += QuakePower(quakefactor, jiggers.RelIntensityZ, jiggers.RelOffsetZ); + viewz += QuakePower(quakefactor, jiggers.RelIntensityZ, jiggers.RelOffsetZ, jiggers.Falloff, jiggers.WFalloff); } if ((jiggers.IntensityX | jiggers.OffsetX) != 0) { - viewx += QuakePower(quakefactor, jiggers.IntensityX, jiggers.OffsetX); + viewx += QuakePower(quakefactor, jiggers.IntensityX, jiggers.OffsetX, jiggers.Falloff, jiggers.WFalloff); } if ((jiggers.IntensityY | jiggers.OffsetY) != 0) { - viewy += QuakePower(quakefactor, jiggers.IntensityY, jiggers.OffsetY); + viewy += QuakePower(quakefactor, jiggers.IntensityY, jiggers.OffsetY, jiggers.Falloff, jiggers.WFalloff); } if ((jiggers.IntensityZ | jiggers.OffsetZ) != 0) { - viewz += QuakePower(quakefactor, jiggers.IntensityZ, jiggers.OffsetZ); + viewz += QuakePower(quakefactor, jiggers.IntensityZ, jiggers.OffsetZ, jiggers.Falloff, jiggers.WFalloff); } } } diff --git a/src/statistics.cpp b/src/statistics.cpp index 292d66f510..3676b8678f 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -62,7 +62,6 @@ #include "cmdlib.h" #include "p_terrain.h" #include "decallib.h" -#include "a_doomglobal.h" #include "autosegs.h" #include "i_cd.h" #include "stats.h" diff --git a/src/tarray.h b/src/tarray.h index 7b9d5970bf..bd28d86abc 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -39,6 +39,7 @@ #include #include #include +#include #if !defined(_WIN32) #include // for intptr_t @@ -93,17 +94,23 @@ public: { return &Array[0]; } + const_iterator begin() const + { + return &Array[0]; + } + const_iterator cbegin() const + { + return &Array[0]; + } iterator end() { return &Array[Count]; } - - const_iterator cbegin() const + const_iterator end() const { - return &Array[0]; + return &Array[Count]; } - const_iterator cend() const { return &Array[Count]; @@ -137,11 +144,17 @@ public: Count = 0; Array = (T *)M_Malloc (sizeof(T)*max); } - TArray (const TArray &other) + TArray (const TArray &other) { DoCopy (other); } - TArray &operator= (const TArray &other) + TArray (TArray &&other) + { + Array = other.Array; other.Array = NULL; + Most = other.Most; other.Most = 0; + Count = other.Count; other.Count = 0; + } + TArray &operator= (const TArray &other) { if (&other != this) { @@ -157,6 +170,21 @@ public: } return *this; } + TArray &operator= (TArray &&other) + { + if (Array) + { + if (Count > 0) + { + DoDelete (0, Count-1); + } + M_Free (Array); + } + Array = other.Array; other.Array = NULL; + Most = other.Most; other.Most = 0; + Count = other.Count; other.Count = 0; + return *this; + } ~TArray () { if (Array) @@ -417,6 +445,14 @@ template class TDeletingArray : public TArray { public: + TDeletingArray() : TArray() {} + TDeletingArray(TDeletingArray &&other) : TArray(std::move(other)) {} + TDeletingArray &operator=(TDeletingArray &&other) + { + TArray::operator=(std::move(other)); + return *this; + } + ~TDeletingArray () { for (unsigned int i = 0; i < TArray::Size(); ++i) diff --git a/src/templates.h b/src/templates.h index 4f75a961ae..0737fc4e91 100644 --- a/src/templates.h +++ b/src/templates.h @@ -40,6 +40,7 @@ #endif #include +#include //========================================================================== // @@ -204,7 +205,7 @@ template inline void swapvalues (T &a, T &b) { - T temp = a; a = b; b = temp; + T temp = std::move(a); a = std::move(b); b = std::move(temp); } #endif //__TEMPLATES_H__ diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 551dde93af..a24e8831c9 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -57,7 +57,6 @@ #include "m_argv.h" #include "p_local.h" #include "doomerrors.h" -#include "a_hexenglobal.h" #include "a_weaponpiece.h" #include "p_conversation.h" #include "v_text.h" diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c5081f57aa..c09615067a 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2761,6 +2761,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) return 0; } +//========================================================================= +// +// A_LogFloat +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogFloat) +{ + PARAM_ACTION_PROLOGUE; + PARAM_FLOAT(num); + IGNORE_FORMAT_PRE + Printf("%H\n", num); + IGNORE_FORMAT_POST + return 0; +} + //=========================================================================== // // A_SetTranslucent @@ -4681,22 +4697,46 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) +static PField *GetVar(AActor *self, FName varname) { - PARAM_ACTION_PROLOGUE; - PARAM_NAME (varname); - PARAM_INT (value); - PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); if (var == NULL || (var->Flags & VARF_Native) || !var->Type->IsKindOf(RUNTIME_CLASS(PBasicType))) { Printf("%s is not a user variable in class %s\n", varname.GetChars(), self->GetClass()->TypeName.GetChars()); - return 0; + return nullptr; } + return var; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) +{ + PARAM_ACTION_PROLOGUE; + PARAM_NAME (varname); + PARAM_INT (value); + // Set the value of the specified user variable. - var->Type->SetValue(reinterpret_cast(self) + var->Offset, value); + PField *var = GetVar(self, varname); + if (var != nullptr) + { + var->Type->SetValue(reinterpret_cast(self) + var->Offset, value); + } + return 0; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVarFloat) +{ + PARAM_ACTION_PROLOGUE; + PARAM_NAME (varname); + PARAM_FLOAT (value); + + // Set the value of the specified user variable. + PField *var = GetVar(self, varname); + if (var != nullptr) + { + var->Type->SetValue(reinterpret_cast(self) + var->Offset, value); + } return 0; } @@ -4706,13 +4746,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) // //=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +static PField *GetArrayVar(AActor *self, FName varname, int pos) { - PARAM_ACTION_PROLOGUE; - PARAM_NAME (varname); - PARAM_INT (pos); - PARAM_INT (value); - PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); if (var == NULL || (var->Flags & VARF_Native) || @@ -4721,17 +4756,48 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) { Printf("%s is not a user array in class %s\n", varname.GetChars(), self->GetClass()->TypeName.GetChars()); - return 0; + return nullptr; } - PArray *arraytype = static_cast(var->Type); - if ((unsigned)pos >= arraytype->ElementCount) + if ((unsigned)pos >= static_cast(var->Type)->ElementCount) { Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), self->GetClass()->TypeName.GetChars()); - return 0; + return nullptr; } + return var; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +{ + PARAM_ACTION_PROLOGUE; + PARAM_NAME (varname); + PARAM_INT (pos); + PARAM_INT (value); + // Set the value of the specified user array at index pos. - arraytype->ElementType->SetValue(reinterpret_cast(self) + var->Offset + arraytype->ElementSize * pos, value); + PField *var = GetArrayVar(self, varname, pos); + if (var != nullptr) + { + PArray *arraytype = static_cast(var->Type); + arraytype->ElementType->SetValue(reinterpret_cast(self) + var->Offset + arraytype->ElementSize * pos, value); + } + return 0; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArrayFloat) +{ + PARAM_ACTION_PROLOGUE; + PARAM_NAME (varname); + PARAM_INT (pos); + PARAM_FLOAT (value); + + // Set the value of the specified user array at index pos. + PField *var = GetArrayVar(self, varname, pos); + if (var != nullptr) + { + PArray *arraytype = static_cast(var->Type); + arraytype->ElementType->SetValue(reinterpret_cast(self) + var->Offset + arraytype->ElementSize * pos, value); + } return 0; } @@ -4969,7 +5035,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) // A_QuakeEx // // Extended version of A_Quake. Takes individual axis into account and can -// take a flag. +// take flags. //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_QuakeEx) @@ -4986,7 +5052,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_QuakeEx) PARAM_FLOAT_OPT(mulWaveX) { mulWaveX = 1.; } PARAM_FLOAT_OPT(mulWaveY) { mulWaveY = 1.; } PARAM_FLOAT_OPT(mulWaveZ) { mulWaveZ = 1.; } - P_StartQuakeXYZ(self, 0, intensityX, intensityY, intensityZ, duration, damrad, tremrad, sound, flags, mulWaveX, mulWaveY, mulWaveZ); + PARAM_INT_OPT(falloff) { falloff = 0; } + PARAM_INT_OPT(highpoint) { highpoint = 0; } + P_StartQuakeXYZ(self, 0, intensityX, intensityY, intensityZ, duration, damrad, tremrad, sound, flags, mulWaveX, mulWaveY, mulWaveZ, falloff, highpoint); return 0; } @@ -6694,14 +6762,20 @@ enum CBF CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. CBF_DROPOFF = 1 << 5, //Check for dropoffs. CBF_NOACTORS = 1 << 6, //Don't check actors. + CBF_ABSOLUTEPOS = 1 << 7, //Absolute position for offsets. + CBF_ABSOLUTEANGLE = 1 << 8, //Absolute angle for offsets. }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) { PARAM_ACTION_PROLOGUE; PARAM_STATE(block) - PARAM_INT_OPT(flags) { flags = 0; } - PARAM_INT_OPT(ptr) { ptr = AAPTR_DEFAULT; } + PARAM_INT_OPT(flags) { flags = 0; } + PARAM_INT_OPT(ptr) { ptr = AAPTR_DEFAULT; } + PARAM_FIXED_OPT(xofs) { xofs = 0; } + PARAM_FIXED_OPT(yofs) { yofs = 0; } + PARAM_FIXED_OPT(zofs) { zofs = 0; } + PARAM_ANGLE_OPT(angle) { angle = 0; } AActor *mobj = COPY_AAPTR(self, ptr); @@ -6711,8 +6785,48 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) ACTION_RETURN_STATE(NULL); } + if (!(flags & CBF_ABSOLUTEANGLE)) + { + angle += self->angle; + } + + angle_t ang = angle >> ANGLETOFINESHIFT; + fixedvec3 oldpos = mobj->Pos(); + fixedvec3 pos; + + if (flags & CBF_ABSOLUTEPOS) + { + pos.x = xofs; + pos.y = yofs; + pos.z = zofs; + } + else + { + pos = mobj->Vec3Offset( + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]), + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]), + mobj->Z() + zofs); + } + + // Next, try checking the position based on the sensitivity desired. + // If checking for dropoffs, set the z so we can have maximum flexibility. + // Otherwise, set origin and set it back after testing. + + bool checker = false; + if (flags & CBF_DROPOFF) + { + mobj->SetZ(pos.z); + checker = P_CheckMove(mobj, pos.x, pos.y); + mobj->SetZ(oldpos.z); + } + else + { + mobj->SetOrigin(pos, true); + checker = P_TestMobjLocation(mobj); + mobj->SetOrigin(oldpos, true); + } + //Nothing to block it so skip the rest. - bool checker = (flags & CBF_DROPOFF) ? P_CheckMove(mobj, mobj->X(), mobj->Y()) : P_TestMobjLocation(mobj); if (checker) { ACTION_RETURN_STATE(NULL); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 7ecc3c3c47..e1a9e653ef 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2994,10 +2994,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) } ValueType = arraytype->ElementType; - if (ValueType->GetRegType() != REGT_INT) + if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT) { // int arrays only for now - ScriptPosition.Message(MSG_ERROR, "Only integer arrays are supported."); + ScriptPosition.Message(MSG_ERROR, "Only numeric arrays are supported."); delete this; return NULL; } @@ -3014,7 +3014,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { ExpEmit start = Array->Emit(build); - ExpEmit dest(build, REGT_INT); + PArray *const arraytype = static_cast(Array->ValueType); + PType *const elementtype = arraytype->ElementType; + ExpEmit dest(build, elementtype->GetRegType()); + if (start.Konst) { ExpEmit tmpstart(build, REGT_POINTER); @@ -3024,19 +3027,30 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) if (index->isConstant()) { unsigned indexval = static_cast(index)->GetValue().GetInt(); - if (indexval >= static_cast(Array->ValueType)->ElementCount) + if (indexval >= arraytype->ElementCount) { I_Error("Array index out of bounds"); } - indexval <<= 2; - build->Emit(OP_LW, dest.RegNum, start.RegNum, build->GetConstantInt(indexval)); + indexval *= arraytype->ElementSize; + build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, + start.RegNum, build->GetConstantInt(indexval)); } else { ExpEmit indexv(index->Emit(build)); - build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, 2); - build->Emit(OP_BOUND, indexv.RegNum, static_cast(Array->ValueType)->ElementCount); - build->Emit(OP_LW_R, dest.RegNum, start.RegNum, indexv.RegNum); + int shiftbits = 0; + while (1u << shiftbits < arraytype->ElementSize) + { + shiftbits++; + } + assert(1 << shiftbits == arraytype->ElementSize && "Element sizes other than power of 2 are not implemented"); + build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount); + if (shiftbits > 0) + { + build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits); + } + build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that + dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register indexv.Free(build); } start.Free(build); diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 97e22dc8c0..70f2c050dd 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -531,14 +531,14 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl sc.ScriptError("Native classes may not have user variables"); } - // Read the type and make sure it's int. + // Read the type and make sure it's acceptable. sc.MustGetAnyToken(); - if (sc.TokenType != TK_Int) + if (sc.TokenType != TK_Int && sc.TokenType != TK_Float) { - sc.ScriptMessage("User variables must be of type int"); + sc.ScriptMessage("User variables must be of type 'int' or 'float'"); FScriptPosition::ErrorCounter++; } - type = TypeSInt32; + type = sc.TokenType == TK_Int ? (PType *)TypeSInt32 : (PType *)TypeFloat64; sc.MustGetToken(TK_Identifier); // For now, restrict user variables to those that begin with "user_" to guarantee @@ -584,11 +584,9 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl } sc.MustGetToken(';'); - PField *sym = new PField(symname, type, 0); - sym->Offset = cls->Extend(sizeof(int) * maxelems); - if (symt->AddSymbol(sym) == NULL) + PField *sym = cls->AddField(symname, type, 0); + if (sym == NULL) { - delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; diff --git a/src/v_blend.cpp b/src/v_blend.cpp index 3aff4a3a31..b8f3e713d7 100644 --- a/src/v_blend.cpp +++ b/src/v_blend.cpp @@ -52,7 +52,6 @@ #include "v_palette.h" #include "d_player.h" #include "farchive.h" -#include "a_hexenglobal.h" diff --git a/src/version.h b/src/version.h index 02a602f992..4fe2d9ef32 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4533 +#define SAVEVER 4535 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index aeeaec813a..aa751e3322 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -203,6 +203,7 @@ ACTOR Actor native //: Thinker action native A_PrintBold(string whattoprint, float time = 0, name fontname = ""); action native A_Log(string whattoprint); action native A_LogInt(int whattoprint); + action native A_LogFloat(float whattoprint); action native A_SetTranslucent(float alpha, int style = 0); action native A_FadeIn(float reduce = 0.1, int flags = 0); action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true @@ -275,9 +276,11 @@ ACTOR Actor native //: Thinker action native A_SetArg(int pos, int value); action native A_SetUserVar(name varname, int value); action native A_SetUserArray(name varname, int index, int value); + action native A_SetUserVarFloat(name varname, float value); + action native A_SetUserArrayFloat(name varname, int index, float value); action native A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0); action native A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake"); - action native A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1); + action native A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1, int falloff = 0, int highpoint = 0); action native A_SetTics(int tics); action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); @@ -317,7 +320,7 @@ ACTOR Actor native //: Thinker action native A_SetRipMax(int maximum); action native A_SetChaseThreshold(int threshold, bool def = false, int ptr = AAPTR_DEFAULT); action native state A_CheckProximity(state jump, class classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT); - action native state A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT); + action native state A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0); action native state A_CheckSightOrRange(float distance, state label, bool two_dimension = false); action native state A_CheckRange(float distance, state label, bool two_dimension = false); action native bool A_FaceMovementDirection(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 3060c2c2d9..9ccb1fd6ee 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -527,6 +527,8 @@ enum CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. CBF_DROPOFF = 1 << 5, //Check for dropoffs. CBF_NOACTORS = 1 << 6, //Don't check actors. + CBF_ABSOLUTEPOS = 1 << 7, //Absolute position for offsets. + CBF_ABSOLUTEANGLE = 1 << 8, //Absolute angle for offsets. }; enum