From c3ffeb1e9d7a07569a2d9cee972f923ebb0f3394 Mon Sep 17 00:00:00 2001 From: MajorCooke <paul.growney22@gmail.com> Date: Mon, 22 Feb 2016 11:30:44 -0600 Subject: [PATCH 01/13] A_RadiusGive Missile check fix - Don't use isMissile(). Check directly for the flag at the moment of calling and not the default. Otherwise, things changing themselves will still be ineligible for non-missile checks. --- src/thingdef/thingdef_codeptr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 7119b577d..2cf05778b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5381,13 +5381,13 @@ enum RadiusGiveFlags static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amount, fixed_t distance, int flags, PClassActor *filter, FName species, fixed_t mindist) { // [MC] We only want to make an exception for missiles here. Nothing else. - bool missilePass = !!((flags & RGF_MISSILES) && thing->isMissile()); + bool missilePass = !!((flags & RGF_MISSILES) && thing->flags & MF_MISSILE); if (thing == self) { if (!(flags & RGF_GIVESELF)) return false; } - else if (thing->isMissile()) + else if (thing->flags & MF_MISSILE) { if (!missilePass) return false; From 78552cc676d5c42f215d7dc6e805cbc1aee274d6 Mon Sep 17 00:00:00 2001 From: DaMan <daman6009@comcast.net> Date: Mon, 15 Feb 2016 03:59:32 -0500 Subject: [PATCH 02/13] Disable UAC virtualization --- src/win32/zdoom.exe.manifest | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/win32/zdoom.exe.manifest b/src/win32/zdoom.exe.manifest index 0d2074069..2ed84626b 100644 --- a/src/win32/zdoom.exe.manifest +++ b/src/win32/zdoom.exe.manifest @@ -5,6 +5,13 @@ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity> </dependentAssembly> </dependency> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level='asInvoker' uiAccess='false' /> + </requestedPrivileges> + </security> + </trustInfo> <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>true</dpiAware> From a8e985f4f6c29a231b5913444d4ab3f660599feb Mon Sep 17 00:00:00 2001 From: Randy Heit <rheit@users.noreply.github.com> Date: Mon, 22 Feb 2016 18:14:24 -0600 Subject: [PATCH 03/13] Don't use fast math for gdtoa - gdtoa is for converting floating point numbers to and from string representations. I'd consider accuracy more important than speed here. --- gdtoa/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/gdtoa/CMakeLists.txt b/gdtoa/CMakeLists.txt index 4dff4c30d..a0b76e0c3 100644 --- a/gdtoa/CMakeLists.txt +++ b/gdtoa/CMakeLists.txt @@ -12,9 +12,6 @@ if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra" ) endif() -# Enable fast flag for gdtoa -set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}" ) - include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) add_definitions( -DINFNAN_CHECK -DMULTIPLE_THREADS ) From 1ffb7ad1099f8f6c7a5d92d09f0333892a620c2e Mon Sep 17 00:00:00 2001 From: Randy Heit <rheit@users.noreply.github.com> Date: Tue, 23 Feb 2016 16:26:00 -0600 Subject: [PATCH 04/13] Add min and max to DECORATE --- src/sc_man_scanner.re | 2 + src/sc_man_tokens.h | 2 + src/thingdef/thingdef_exp.cpp | 15 ++ src/thingdef/thingdef_exp.h | 18 +++ src/thingdef/thingdef_expression.cpp | 219 +++++++++++++++++++++++++-- wadsrc/static/actors/actor.txt | 4 +- 6 files changed, 248 insertions(+), 12 deletions(-) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index cc051b05f..4a838f89a 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -191,6 +191,8 @@ std2: 'frandom' { RET(TK_FRandom); } 'randompick' { RET(TK_RandomPick); } 'frandompick' { RET(TK_FRandomPick); } + 'min' { RET(TK_Min); } + 'max' { RET(TK_Max); } L (L|D)* { RET(TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 1665a1cae..4dcddd332 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -128,6 +128,8 @@ xx(TK_SizeOf, "'sizeof'") xx(TK_AlignOf, "'alignof'") xx(TK_RandomPick, "'randompick'") xx(TK_FRandomPick, "'frandompick'") +xx(TK_Min, "'min'") +xx(TK_Max, "'max'") xx(TK_States, "'states'") xx(TK_Loop, "'loop'") xx(TK_Fail, "'fail'") diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 17d97e4fa..2204a5652 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -349,6 +349,21 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) // a cheap way to get them working when people use "name" instead of 'name'. return new FxConstant(FName(sc.String), scpos); } + else if (sc.CheckToken(TK_Min) || sc.CheckToken(TK_Max)) + { + int type = sc.TokenType; + TArray<FxExpression*> list; + sc.MustGetToken('('); + for (;;) + { + FxExpression *expr = ParseExpressionM(sc, cls); + list.Push(expr); + if (sc.CheckToken(')')) + break; + sc.MustGetToken(','); + } + return new FxMinMax(list, type, sc); + } else if (sc.CheckToken(TK_Random)) { FRandom *rng; diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 0802c08f8..330caf0a5 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -629,6 +629,24 @@ public: // //========================================================================== +class FxMinMax : public FxExpression +{ + TDeletingArray<FxExpression *> choices; + int Type; + +public: + FxMinMax(TArray<FxExpression*> &expr, int type, const FScriptPosition &pos); + FxExpression *Resolve(FCompileContext&); + + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// +// +//========================================================================== + class FxRandom : public FxExpression { protected: diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index f9ab32b73..f5c123f4d 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2038,6 +2038,214 @@ ExpEmit FxAbs::Emit(VMFunctionBuilder *build) return out; } +//========================================================================== +// +// +// +//========================================================================== +FxMinMax::FxMinMax(TArray<FxExpression*> &expr, int type, const FScriptPosition &pos) +: FxExpression(pos), Type(type) +{ + assert(expr.Size() > 0); + assert(type == TK_Min || type == TK_Max); + + ValueType = VAL_Unknown; + choices.Resize(expr.Size()); + for (unsigned i = 0; i < expr.Size(); ++i) + { + choices[i] = expr[i]; + } +} + +//========================================================================== +// +// +// +//========================================================================== +FxExpression *FxMinMax::Resolve(FCompileContext &ctx) +{ + unsigned int i; + bool isconst; + int intcount, floatcount; + + CHECKRESOLVED(); + + // Determine if float or int + intcount = floatcount = 0; + for (i = 0; i < choices.Size(); ++i) + { + RESOLVE(choices[i], ctx); + ABORT(choices[i]); + + if (choices[i]->ValueType == VAL_Float) + { + floatcount++; + } + else if (choices[i]->ValueType == VAL_Int) + { + intcount++; + } + else + { + ScriptPosition.Message(MSG_ERROR, "Arguments must be of type int or float"); + delete this; + return NULL; + } + } + if (floatcount != 0) + { + ValueType = VAL_Float; + if (intcount != 0) + { // There are some ints that need to be cast to floats + for (i = 0; i < choices.Size(); ++i) + { + if (choices[i]->ValueType == VAL_Int) + { + choices[i] = new FxFloatCast(choices[i]); + RESOLVE(choices[i], ctx); + ABORT(choices[i]); + } + } + } + } + else + { + ValueType = VAL_Int; + } + + // Determine if every argument is constant + isconst = true; + for (i = 0; i < choices.Size(); ++i) + { + if (!choices[i]->isConstant()) + { + isconst = false; + break; + } + } + + // If every argument is constant, we can decide this now. + if (isconst) + { + ExpVal best = static_cast<FxConstant *>(choices[0])->GetValue(); + for (i = 1; i < choices.Size(); ++i) + { + ExpVal value = static_cast<FxConstant *>(choices[i])->GetValue(); + assert(value.Type == ValueType.Type); + if (Type == TK_Min) + { + if (value.Type == VAL_Float) + { + if (value.Float < best.Float) + { + best.Float = value.Float; + } + } + else + { + if (value.Int < best.Int) + { + best.Int = value.Int; + } + } + } + else + { + if (value.Type == VAL_Float) + { + if (value.Float > best.Float) + { + best.Float = value.Float; + } + } + else + { + if (value.Int > best.Int) + { + best.Int = value.Int; + } + } + } + } + FxExpression *x = new FxConstant(best, ScriptPosition); + delete this; + return x; + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== +static void EmitLoad(VMFunctionBuilder *build, const ExpEmit resultreg, const ExpVal &value) +{ + if (resultreg.RegType == REGT_FLOAT) + { + build->Emit(OP_LKF, resultreg.RegNum, build->GetConstantFloat(value.GetFloat())); + } + else + { + build->EmitLoadInt(resultreg.RegNum, value.GetInt()); + } +} + +ExpEmit FxMinMax::Emit(VMFunctionBuilder *build) +{ + unsigned i; + int opcode, opA; + + assert(choices.Size() > 0); + assert(OP_LTF_RK == OP_LTF_RR+1); + assert(OP_LT_RK == OP_LT_RR+1); + assert(OP_LEF_RK == OP_LEF_RR+1); + assert(OP_LE_RK == OP_LE_RR+1); + + if (Type == TK_Min) + { + opcode = ValueType.Type == VAL_Float ? OP_LEF_RR : OP_LE_RR; + opA = 1; + } + else + { + opcode = ValueType.Type == VAL_Float ? OP_LTF_RR : OP_LT_RR; + opA = 0; + } + + ExpEmit bestreg; + + // Get first value into a register. This will also be the result register. + if (choices[0]->isConstant()) + { + bestreg = ExpEmit(build, ValueType.Type == VAL_Float ? REGT_FLOAT : REGT_INT); + EmitLoad(build, bestreg, static_cast<FxConstant *>(choices[0])->GetValue()); + } + else + { + bestreg = choices[0]->Emit(build); + } + + // Compare every choice. Better matches get copied to the bestreg. + for (i = 1; i < choices.Size(); ++i) + { + ExpEmit checkreg = choices[i]->Emit(build); + assert(checkreg.RegType == bestreg.RegType); + build->Emit(opcode + checkreg.Konst, opA, bestreg.RegNum, checkreg.RegNum); + build->Emit(OP_JMP, 1); + if (checkreg.Konst) + { + build->Emit(bestreg.RegType == REGT_FLOAT ? OP_LKF : OP_LK, bestreg.RegNum, checkreg.RegNum); + } + else + { + build->Emit(bestreg.RegType == REGT_FLOAT ? OP_MOVEF : OP_MOVE, bestreg.RegNum, checkreg.RegNum, 0); + checkreg.Free(build); + } + } + return bestreg; +} + //========================================================================== // // @@ -2269,16 +2477,7 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) build->BackpatchToHere(jumptable + i); if (choices[i]->isConstant()) { - if (ValueType == VAL_Int) - { - int val = static_cast<FxConstant *>(choices[i])->GetValue().GetInt(); - build->EmitLoadInt(resultreg.RegNum, val); - } - else - { - double val = static_cast<FxConstant *>(choices[i])->GetValue().GetFloat(); - build->Emit(OP_LKF, resultreg.RegNum, build->GetConstantFloat(val)); - } + EmitLoad(build, resultreg, static_cast<FxConstant *>(choices[i])->GetValue()); } else { diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 2425ce694..642269f28 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -311,8 +311,8 @@ ACTOR Actor native //: Thinker action native state A_JumpIfHigherOrLower(state high, state low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET); action native A_SetSpecies(name species, int ptr = AAPTR_DEFAULT); action native A_SetRipperLevel(int level); - action native A_SetRipMin(int min); - action native A_SetRipMax(int max); + action native A_SetRipMin(int mininum); + 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<Actor> 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); From 1f09341d2b8020d5da2c1add45c2708bf40248cf Mon Sep 17 00:00:00 2001 From: Randy Heit <rheit@users.noreply.github.com> Date: Tue, 23 Feb 2016 16:37:52 -0600 Subject: [PATCH 05/13] Let min/max pre-solve for 2+ constants, even if there are non-constants --- src/thingdef/thingdef_expression.cpp | 117 +++++++++++++++------------ 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index f5c123f4d..8d0f158ba 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2065,7 +2065,6 @@ FxMinMax::FxMinMax(TArray<FxExpression*> &expr, int type, const FScriptPosition FxExpression *FxMinMax::Resolve(FCompileContext &ctx) { unsigned int i; - bool isconst; int intcount, floatcount; CHECKRESOLVED(); @@ -2113,64 +2112,76 @@ FxExpression *FxMinMax::Resolve(FCompileContext &ctx) ValueType = VAL_Int; } - // Determine if every argument is constant - isconst = true; + // If at least two arguments are constants, they can be solved now. + + // Look for first constant for (i = 0; i < choices.Size(); ++i) { - if (!choices[i]->isConstant()) + if (choices[i]->isConstant()) { - isconst = false; + ExpVal best = static_cast<FxConstant *>(choices[i])->GetValue(); + // Compare against remaining constants, which are removed. + // The best value gets stored in this one. + for (unsigned j = i + 1; j < choices.Size(); ) + { + if (!choices[j]->isConstant()) + { + j++; + } + else + { + ExpVal value = static_cast<FxConstant *>(choices[j])->GetValue(); + assert(value.Type == ValueType.Type); + if (Type == TK_Min) + { + if (value.Type == VAL_Float) + { + if (value.Float < best.Float) + { + best.Float = value.Float; + } + } + else + { + if (value.Int < best.Int) + { + best.Int = value.Int; + } + } + } + else + { + if (value.Type == VAL_Float) + { + if (value.Float > best.Float) + { + best.Float = value.Float; + } + } + else + { + if (value.Int > best.Int) + { + best.Int = value.Int; + } + } + } + delete choices[j]; + choices[j] = NULL; + choices.Delete(j); + } + } + FxExpression *x = new FxConstant(best, ScriptPosition); + if (i == 0 && choices.Size() == 1) + { // Every choice was constant + delete this; + return x; + } + delete choices[i]; + choices[i] = x; break; } } - - // If every argument is constant, we can decide this now. - if (isconst) - { - ExpVal best = static_cast<FxConstant *>(choices[0])->GetValue(); - for (i = 1; i < choices.Size(); ++i) - { - ExpVal value = static_cast<FxConstant *>(choices[i])->GetValue(); - assert(value.Type == ValueType.Type); - if (Type == TK_Min) - { - if (value.Type == VAL_Float) - { - if (value.Float < best.Float) - { - best.Float = value.Float; - } - } - else - { - if (value.Int < best.Int) - { - best.Int = value.Int; - } - } - } - else - { - if (value.Type == VAL_Float) - { - if (value.Float > best.Float) - { - best.Float = value.Float; - } - } - else - { - if (value.Int > best.Int) - { - best.Int = value.Int; - } - } - } - } - FxExpression *x = new FxConstant(best, ScriptPosition); - delete this; - return x; - } return this; } From 58839200e577cebc88ba7ed26824031f7435e089 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <coelckers@zdoom.fake> Date: Tue, 23 Feb 2016 16:59:16 +0100 Subject: [PATCH 06/13] - fixed some line clipping issues with portals * Blocking lines above or below the current sector should only block if they actually intersect with the currently checking actor. * Sectors above a ceiling portal should not change current floor information and vice versa. --- src/p_map.cpp | 132 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 30 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 0b1d78f23..d68771b22 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -748,6 +748,30 @@ int P_GetMoveFactor(const AActor *mo, int *frictionp) } +//========================================================================== +// +// Checks if the line intersects with the actor +// returns +// - 1 when above/below +// - 0 when intersecting +// - -1 when outside the portal +// +//========================================================================== + +static int LineIsAbove(line_t *line, AActor *actor) +{ + AActor *point = line->frontsector->SkyBoxes[sector_t::floor]; + if (point == NULL) return -1; + return point->threshold >= actor->Top(); +} + +static int LineIsBelow(line_t *line, AActor *actor) +{ + AActor *point = line->frontsector->SkyBoxes[sector_t::ceiling]; + if (point == NULL) return -1; + return point->threshold <= actor->Z(); +} + // // MOVEMENT ITERATOR FUNCTIONS // @@ -788,6 +812,13 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec if (!ld->backsector) { // One sided line + if (((cres.portalflags & FFCF_NOFLOOR) && LineIsAbove(cres.line, tm.thing) != 0) || + ((cres.portalflags & FFCF_NOCEILING) && LineIsBelow(cres.line, tm.thing) != 0)) + { + // this blocking line is in a different vertical layer and does not intersect with the actor that is being checked. + // Since a one-sided line does not have an opening there's nothing left to do about it. + return true; + } if (tm.thing->flags2 & MF2_BLASTED) { P_DamageMobj(tm.thing, NULL, NULL, tm.thing->Mass >> 5, NAME_Melee); @@ -818,17 +849,52 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec ((Projectile) && (ld->flags & ML_BLOCKPROJECTILE)) || // block projectiles ((tm.thing->flags & MF_FLOAT) && (ld->flags & ML_BLOCK_FLOATERS))) // block floaters { - if (tm.thing->flags2 & MF2_BLASTED) + if (cres.portalflags & FFCF_NOFLOOR) { - P_DamageMobj(tm.thing, NULL, NULL, tm.thing->Mass >> 5, NAME_Melee); + int state = LineIsAbove(cres.line, tm.thing); + if (state == -1) return true; + if (state == 1) + { + // the line should not block but we should set the ceilingz to the portal boundary so that we can't float up into that line. + fixed_t portalz = cres.line->frontsector->SkyBoxes[sector_t::floor]->threshold; + if (portalz < tm.ceilingz) + { + tm.ceilingz = portalz; + tm.ceilingsector = cres.line->frontsector; + } + return true; + } + } + else if (cres.portalflags & FFCF_NOCEILING) + { + // same, but for downward portals + int state = LineIsBelow(cres.line, tm.thing); + if (state == -1) return true; + if (state == 1) + { + fixed_t portalz = cres.line->frontsector->SkyBoxes[sector_t::ceiling]->threshold; + if (portalz > tm.floorz) + { + tm.floorz = portalz; + tm.floorsector = cres.line->frontsector; + tm.floorterrain = 0; + } + return true; + } + } + else + { + if (tm.thing->flags2 & MF2_BLASTED) + { + P_DamageMobj(tm.thing, NULL, NULL, tm.thing->Mass >> 5, NAME_Melee); + } + tm.thing->BlockingLine = ld; + // Calculate line side based on the actor's original position, not the new one. + CheckForPushSpecial(ld, P_PointOnLineSide(cres.position.x, cres.position.y, ld), tm.thing); + return false; } - tm.thing->BlockingLine = ld; - // Calculate line side based on the actor's original position, not the new one. - CheckForPushSpecial(ld, P_PointOnLineSide(cres.position.x, cres.position.y, ld), tm.thing); - return false; } } - fixedvec2 ref = FindRefPoint(ld, cres.position); FLineOpening open; @@ -867,33 +933,39 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec } // adjust floor / ceiling heights - if (open.top < tm.ceilingz) + if (!(cres.portalflags & FFCF_NOCEILING)) { - tm.ceilingz = open.top; - tm.ceilingsector = open.topsec; - tm.ceilingpic = open.ceilingpic; - tm.ceilingline = ld; - tm.thing->BlockingLine = ld; + if (open.top < tm.ceilingz) + { + tm.ceilingz = open.top; + tm.ceilingsector = open.topsec; + tm.ceilingpic = open.ceilingpic; + tm.ceilingline = ld; + tm.thing->BlockingLine = ld; + } } - if (open.bottom > tm.floorz) + if (!(cres.portalflags & FFCF_NOFLOOR)) { - tm.floorz = open.bottom; - tm.floorsector = open.bottomsec; - tm.floorpic = open.floorpic; - tm.floorterrain = open.floorterrain; - tm.touchmidtex = open.touchmidtex; - tm.abovemidtex = open.abovemidtex; - tm.thing->BlockingLine = ld; - } - else if (open.bottom == tm.floorz) - { - tm.touchmidtex |= open.touchmidtex; - tm.abovemidtex |= open.abovemidtex; - } + if (open.bottom > tm.floorz) + { + tm.floorz = open.bottom; + tm.floorsector = open.bottomsec; + tm.floorpic = open.floorpic; + tm.floorterrain = open.floorterrain; + tm.touchmidtex = open.touchmidtex; + tm.abovemidtex = open.abovemidtex; + tm.thing->BlockingLine = ld; + } + else if (open.bottom == tm.floorz) + { + tm.touchmidtex |= open.touchmidtex; + tm.abovemidtex |= open.abovemidtex; + } - if (open.lowfloor < tm.dropoffz) - tm.dropoffz = open.lowfloor; + if (open.lowfloor < tm.dropoffz) + tm.dropoffz = open.lowfloor; + } // if contacted a special line, add it to the list spechit_t spec; @@ -903,7 +975,7 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec spec.refpos = cres.position; spechit.Push(spec); } - if (ld->portalindex >= 0) + if (ld->portalindex >= 0 && ld->portalindex != UINT_MAX) { spec.line = ld; spec.refpos = cres.position; From 01bdd8a7da8e204fc6f2115071213ff446c893ae Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <coelckers@zdoom.fake> Date: Wed, 24 Feb 2016 01:06:48 +0100 Subject: [PATCH 07/13] - actor transition through a sector portal is working. --- src/actor.h | 1 + src/p_mobj.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/r_utility.cpp | 27 +++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/actor.h b/src/actor.h index 35afef397..47f6ef44b 100644 --- a/src/actor.h +++ b/src/actor.h @@ -742,6 +742,7 @@ public: bool IsHostile (AActor *other); inline bool IsNoClip2() const; + void CheckPortalTransition(); // What species am I? virtual FName GetSpecies(); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 7228ffbff..73683824b 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3288,6 +3288,43 @@ void AActor::SetRoll(angle_t r, bool interpolate) } } + +void AActor::CheckPortalTransition() +{ + if (!Sector->PortalBlocksMovement(sector_t::ceiling)) + { + AActor *port = Sector->SkyBoxes[sector_t::ceiling]; + if (Z() > port->threshold) + { + fixedvec3 oldpos = Pos(); + UnlinkFromWorld(); + SetXYZ(PosRelative(port->Sector)); + PrevX += X() - oldpos.x; + PrevY += Y() - oldpos.y; + PrevZ += Z() - oldpos.z; + LinkToWorld(); + if (player) Printf("Transitioned upwards to sector %d\n", Sector->sectornum); + return; + } + } + if (!Sector->PortalBlocksMovement(sector_t::floor)) + { + AActor *port = Sector->SkyBoxes[sector_t::floor]; + if (Z() < port->threshold && floorz < port->threshold) + { + fixedvec3 oldpos = Pos(); + UnlinkFromWorld(); + SetXYZ(PosRelative(port->Sector)); + PrevX += X() - oldpos.x; + PrevY += Y() - oldpos.y; + PrevZ += Z() - oldpos.z; + LinkToWorld(); + if (player) Printf("Transitioned downwards to sector %d\n", Sector->sectornum); + return; + } + } +} + // // P_MobjThinker // @@ -3355,6 +3392,7 @@ void AActor::Tick () UnlinkFromWorld (); flags |= MF_NOBLOCKMAP; SetXYZ(Vec3Offset(velx, vely, velz)); + CheckPortalTransition(); SetMovement(velx, vely, velz); LinkToWorld (); } @@ -3822,6 +3860,8 @@ void AActor::Tick () Crash(); } + CheckPortalTransition(); + UpdateWaterLevel (oldz); // [RH] Don't advance if predicting a player diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 10a39ba57..60a415d80 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -581,8 +581,11 @@ void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *ivi iview->oviewpitch = iview->nviewpitch; iview->oviewangle = iview->nviewangle; } - viewx = iview->oviewx + FixedMul (frac, iview->nviewx - iview->oviewx); - viewy = iview->oviewy + FixedMul (frac, iview->nviewy - iview->oviewy); + int oldgroup = R_PointInSubsector(iview->oviewx, iview->oviewy)->sector->PortalGroup; + int newgroup = R_PointInSubsector(iview->nviewx, iview->nviewy)->sector->PortalGroup; + fixedvec2 disp = Displacements(oldgroup, newgroup); + viewx = iview->oviewx + FixedMul (frac, iview->nviewx - iview->oviewx - disp.x); + viewy = iview->oviewy + FixedMul (frac, iview->nviewy - iview->oviewy - disp.y); viewz = iview->oviewz + FixedMul (frac, iview->nviewz - iview->oviewz); if (player != NULL && !(player->cheats & CF_INTERPVIEW) && @@ -637,6 +640,26 @@ void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *ivi // Due to interpolation this is not necessarily the same as the sector the camera is in. viewsector = R_PointInSubsector(viewx, viewy)->sector; + if (!viewsector->PortalBlocksMovement(sector_t::ceiling)) + { + AActor *point = viewsector->SkyBoxes[sector_t::ceiling]; + if (viewz > point->threshold) + { + viewx += point->scaleX; + viewy += point->scaleY; + viewsector = R_PointInSubsector(viewx, viewy)->sector; + } + } + if (!viewsector->PortalBlocksMovement(sector_t::floor)) + { + AActor *point = viewsector->SkyBoxes[sector_t::floor]; + if (viewz < point->threshold) + { + viewx += point->scaleX; + viewy += point->scaleY; + viewsector = R_PointInSubsector(viewx, viewy)->sector; + } + } } //========================================================================== From 326907f6abeb674319d011bab4fa0ebae4118098 Mon Sep 17 00:00:00 2001 From: Randy Heit <rheit@users.noreply.github.com> Date: Tue, 23 Feb 2016 19:36:31 -0600 Subject: [PATCH 08/13] Cleanup parsing of DECORATE intrinsics - Split specific parsing for each intrinsic out of ParseExpression0 and into their own functions. - Instead of reserving keywords for intrinsics, identify them by name within TK_Identifier's handling. --- src/namedef.h | 3 + src/sc_man_scanner.re | 8 - src/sc_man_tokens.h | 8 - src/thingdef/thingdef_exp.cpp | 291 ++++++++++++++------------- src/thingdef/thingdef_exp.h | 4 +- src/thingdef/thingdef_expression.cpp | 8 +- 6 files changed, 155 insertions(+), 167 deletions(-) diff --git a/src/namedef.h b/src/namedef.h index 323d6f29c..83f4aca9b 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -280,7 +280,10 @@ xx(Cast) // 'damage type' for the cast call // Special names for thingdef_exp.cpp xx(Random) +xx(FRandom) xx(Random2) +xx(RandomPick) +xx(FRandomPick) xx(Cos) xx(Sin) xx(Alpha) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 4a838f89a..6ebd9a413 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -185,14 +185,6 @@ std2: '#include' { RET(TK_Include); } 'fixed_t' { RET(TK_Fixed_t); } 'angle_t' { RET(TK_Angle_t); } - 'abs' { RET(TK_Abs); } - 'random' { RET(TK_Random); } - 'random2' { RET(TK_Random2); } - 'frandom' { RET(TK_FRandom); } - 'randompick' { RET(TK_RandomPick); } - 'frandompick' { RET(TK_FRandomPick); } - 'min' { RET(TK_Min); } - 'max' { RET(TK_Max); } L (L|D)* { RET(TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 4dcddd332..30a8201b9 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -113,10 +113,6 @@ xx(TK_Stop, "'stop'") xx(TK_Include, "'include'") xx(TK_Fixed_t, "'fixed_t'") xx(TK_Angle_t, "'angle_t'") -xx(TK_Abs, "'abs'") -xx(TK_Random, "'random'") -xx(TK_Random2, "'random2'") -xx(TK_FRandom, "'frandom'") xx(TK_Is, "'is'") xx(TK_Replaces, "'replaces'") @@ -126,10 +122,6 @@ xx(TK_Array, "'array'") xx(TK_In, "'in'") xx(TK_SizeOf, "'sizeof'") xx(TK_AlignOf, "'alignof'") -xx(TK_RandomPick, "'randompick'") -xx(TK_FRandomPick, "'frandompick'") -xx(TK_Min, "'min'") -xx(TK_Max, "'max'") xx(TK_States, "'states'") xx(TK_Loop, "'loop'") xx(TK_Fail, "'fail'") diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 2204a5652..7ef93563c 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -52,6 +52,12 @@ FRandom pr_exrandom ("EX_Random"); +static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls); +static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls); +static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls); +static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls); +static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls); + // // ParseExpression // [GRB] Parses an expression and stores it into Expression array @@ -349,161 +355,63 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) // a cheap way to get them working when people use "name" instead of 'name'. return new FxConstant(FName(sc.String), scpos); } - else if (sc.CheckToken(TK_Min) || sc.CheckToken(TK_Max)) - { - int type = sc.TokenType; - TArray<FxExpression*> list; - sc.MustGetToken('('); - for (;;) - { - FxExpression *expr = ParseExpressionM(sc, cls); - list.Push(expr); - if (sc.CheckToken(')')) - break; - sc.MustGetToken(','); - } - return new FxMinMax(list, type, sc); - } - else if (sc.CheckToken(TK_Random)) - { - FRandom *rng; - - if (sc.CheckToken('[')) - { - sc.MustGetToken(TK_Identifier); - rng = FRandom::StaticFindRNG(sc.String); - sc.MustGetToken(']'); - } - else - { - rng = &pr_exrandom; - } - sc.MustGetToken('('); - - FxExpression *min = ParseExpressionM (sc, cls); - sc.MustGetToken(','); - FxExpression *max = ParseExpressionM (sc, cls); - sc.MustGetToken(')'); - - return new FxRandom(rng, min, max, sc); - } - else if (sc.CheckToken(TK_RandomPick) || sc.CheckToken(TK_FRandomPick)) - { - bool floaty = sc.TokenType == TK_FRandomPick; - FRandom *rng; - TArray<FxExpression*> list; - list.Clear(); - int index = 0; - - if (sc.CheckToken('[')) - { - sc.MustGetToken(TK_Identifier); - rng = FRandom::StaticFindRNG(sc.String); - sc.MustGetToken(']'); - } - else - { - rng = &pr_exrandom; - } - sc.MustGetToken('('); - - for (;;) - { - FxExpression *expr = ParseExpressionM(sc, cls); - list.Push(expr); - if (sc.CheckToken(')')) - break; - sc.MustGetToken(','); - } - return new FxRandomPick(rng, list, floaty, sc); - } - else if (sc.CheckToken(TK_FRandom)) - { - FRandom *rng; - - if (sc.CheckToken('[')) - { - sc.MustGetToken(TK_Identifier); - rng = FRandom::StaticFindRNG(sc.String); - sc.MustGetToken(']'); - } - else - { - rng = &pr_exrandom; - } - sc.MustGetToken('('); - - FxExpression *min = ParseExpressionM (sc, cls); - sc.MustGetToken(','); - FxExpression *max = ParseExpressionM (sc, cls); - sc.MustGetToken(')'); - - return new FxFRandom(rng, min, max, sc); - } - else if (sc.CheckToken(TK_Random2)) - { - FRandom *rng; - - if (sc.CheckToken('[')) - { - sc.MustGetToken(TK_Identifier); - rng = FRandom::StaticFindRNG(sc.String); - sc.MustGetToken(']'); - } - else - { - rng = &pr_exrandom; - } - - sc.MustGetToken('('); - - FxExpression *mask = NULL; - - if (!sc.CheckToken(')')) - { - mask = ParseExpressionM(sc, cls); - sc.MustGetToken(')'); - } - return new FxRandom2(rng, mask, sc); - } - else if (sc.CheckToken(TK_Abs)) - { - sc.MustGetToken('('); - FxExpression *x = ParseExpressionM (sc, cls); - sc.MustGetToken(')'); - return new FxAbs(x); - } else if (sc.CheckToken(TK_Identifier)) { FName identifier = FName(sc.String); + FArgumentList *args; + PFunction *func; + + switch (identifier) + { + case NAME_Random: + case NAME_FRandom: + return ParseRandom(sc, identifier, cls); + case NAME_RandomPick: + case NAME_FRandomPick: + return ParseRandomPick(sc, identifier, cls); + case NAME_Random2: + return ParseRandom2(sc, cls); + default: + break; + } if (sc.CheckToken('(')) { - FArgumentList *args = new FArgumentList; - PFunction *func = dyn_cast<PFunction>(cls->Symbols.FindSymbol(identifier, true)); - try + switch (identifier) { - // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. - if (func != NULL && identifier != NAME_ACS_NamedExecuteWithResult) + case NAME_Min: + case NAME_Max: + return ParseMinMax(sc, identifier, cls); + case NAME_Abs: + return ParseAbs(sc, cls); + default: + args = new FArgumentList; + func = dyn_cast<PFunction>(cls->Symbols.FindSymbol(identifier, true)); + try { - sc.UnGet(); - ParseFunctionParameters(sc, cls, *args, func, "", NULL); - return new FxVMFunctionCall(func, args, sc); - } - else if (!sc.CheckToken(')')) - { - do + // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. + if (func != NULL && identifier != NAME_ACS_NamedExecuteWithResult) { - args->Push(ParseExpressionM (sc, cls)); + sc.UnGet(); + ParseFunctionParameters(sc, cls, *args, func, "", NULL); + return new FxVMFunctionCall(func, args, sc); } - while (sc.CheckToken(',')); - sc.MustGetToken(')'); + else if (!sc.CheckToken(')')) + { + do + { + args->Push(ParseExpressionM (sc, cls)); + } + while (sc.CheckToken(',')); + sc.MustGetToken(')'); + } + return new FxFunctionCall(NULL, identifier, args, sc); } - return new FxFunctionCall(NULL, identifier, args, sc); - } - catch (...) - { - delete args; - throw; + catch (...) + { + delete args; + throw; + } + break; } } else @@ -519,4 +427,97 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) return NULL; } +static FRandom *ParseRNG(FScanner &sc) +{ + FRandom *rng; + if (sc.CheckToken('[')) + { + sc.MustGetToken(TK_Identifier); + rng = FRandom::StaticFindRNG(sc.String); + sc.MustGetToken(']'); + } + else + { + rng = &pr_exrandom; + } + return rng; +} + +static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls) +{ + FRandom *rng = ParseRNG(sc); + + sc.MustGetToken('('); + FxExpression *min = ParseExpressionM (sc, cls); + sc.MustGetToken(','); + FxExpression *max = ParseExpressionM (sc, cls); + sc.MustGetToken(')'); + + if (identifier == NAME_Random) + { + return new FxRandom(rng, min, max, sc); + } + else + { + return new FxFRandom(rng, min, max, sc); + } +} + +static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls) +{ + bool floaty = identifier == NAME_FRandomPick; + FRandom *rng; + TArray<FxExpression*> list; + list.Clear(); + int index = 0; + + rng = ParseRNG(sc); + sc.MustGetToken('('); + + for (;;) + { + FxExpression *expr = ParseExpressionM(sc, cls); + list.Push(expr); + if (sc.CheckToken(')')) + break; + sc.MustGetToken(','); + } + return new FxRandomPick(rng, list, floaty, sc); +} + +static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls) +{ + FRandom *rng = ParseRNG(sc); + FxExpression *mask = NULL; + + sc.MustGetToken('('); + + if (!sc.CheckToken(')')) + { + mask = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + } + return new FxRandom2(rng, mask, sc); +} + +static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls) +{ + FxExpression *x = ParseExpressionM (sc, cls); + sc.MustGetToken(')'); + return new FxAbs(x); +} + +static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls) +{ + TArray<FxExpression*> list; + for (;;) + { + FxExpression *expr = ParseExpressionM(sc, cls); + list.Push(expr); + if (sc.CheckToken(')')) + break; + sc.MustGetToken(','); + } + return new FxMinMax(list, identifier, sc); +} diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 330caf0a5..294399d79 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -632,10 +632,10 @@ public: class FxMinMax : public FxExpression { TDeletingArray<FxExpression *> choices; - int Type; + FName Type; public: - FxMinMax(TArray<FxExpression*> &expr, int type, const FScriptPosition &pos); + FxMinMax(TArray<FxExpression*> &expr, FName type, const FScriptPosition &pos); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 8d0f158ba..833f3a3d5 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2043,11 +2043,11 @@ ExpEmit FxAbs::Emit(VMFunctionBuilder *build) // // //========================================================================== -FxMinMax::FxMinMax(TArray<FxExpression*> &expr, int type, const FScriptPosition &pos) +FxMinMax::FxMinMax(TArray<FxExpression*> &expr, FName type, const FScriptPosition &pos) : FxExpression(pos), Type(type) { assert(expr.Size() > 0); - assert(type == TK_Min || type == TK_Max); + assert(type == NAME_Min || type == NAME_Max); ValueType = VAL_Unknown; choices.Resize(expr.Size()); @@ -2132,7 +2132,7 @@ FxExpression *FxMinMax::Resolve(FCompileContext &ctx) { ExpVal value = static_cast<FxConstant *>(choices[j])->GetValue(); assert(value.Type == ValueType.Type); - if (Type == TK_Min) + if (Type == NAME_Min) { if (value.Type == VAL_Float) { @@ -2213,7 +2213,7 @@ ExpEmit FxMinMax::Emit(VMFunctionBuilder *build) assert(OP_LEF_RK == OP_LEF_RR+1); assert(OP_LE_RK == OP_LE_RR+1); - if (Type == TK_Min) + if (Type == NAME_Min) { opcode = ValueType.Type == VAL_Float ? OP_LEF_RR : OP_LE_RR; opA = 1; From b01e0fa06e265bc6d321d422d0fd62fccd91f1d7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <c.oelckers@zdoom.fake> Date: Wed, 24 Feb 2016 10:08:23 +0100 Subject: [PATCH 09/13] - removed debug output from portal transition code. - handle the case where a portal transition crosses more than a single boundary in one move. --- src/actor.h | 2 +- src/p_mobj.cpp | 45 +++++++++++++++++++++++++-------------------- src/r_utility.cpp | 22 +++++++++++++++------- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/actor.h b/src/actor.h index 47f6ef44b..15c2ea2ab 100644 --- a/src/actor.h +++ b/src/actor.h @@ -742,7 +742,7 @@ public: bool IsHostile (AActor *other); inline bool IsNoClip2() const; - void CheckPortalTransition(); + void CheckPortalTransition(bool islinked); // What species am I? virtual FName GetSpecies(); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 73683824b..dafbaffba 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3289,40 +3289,45 @@ void AActor::SetRoll(angle_t r, bool interpolate) } -void AActor::CheckPortalTransition() +void AActor::CheckPortalTransition(bool islinked) { - if (!Sector->PortalBlocksMovement(sector_t::ceiling)) + bool moved = false; + while (!Sector->PortalBlocksMovement(sector_t::ceiling)) { AActor *port = Sector->SkyBoxes[sector_t::ceiling]; if (Z() > port->threshold) { fixedvec3 oldpos = Pos(); - UnlinkFromWorld(); + if (islinked && !moved) UnlinkFromWorld(); SetXYZ(PosRelative(port->Sector)); PrevX += X() - oldpos.x; PrevY += Y() - oldpos.y; PrevZ += Z() - oldpos.z; - LinkToWorld(); - if (player) Printf("Transitioned upwards to sector %d\n", Sector->sectornum); - return; + Sector = P_PointInSector(X(), Y()); + moved = true; } + else break; } - if (!Sector->PortalBlocksMovement(sector_t::floor)) + if (!moved) { - AActor *port = Sector->SkyBoxes[sector_t::floor]; - if (Z() < port->threshold && floorz < port->threshold) + while (!Sector->PortalBlocksMovement(sector_t::floor)) { - fixedvec3 oldpos = Pos(); - UnlinkFromWorld(); - SetXYZ(PosRelative(port->Sector)); - PrevX += X() - oldpos.x; - PrevY += Y() - oldpos.y; - PrevZ += Z() - oldpos.z; - LinkToWorld(); - if (player) Printf("Transitioned downwards to sector %d\n", Sector->sectornum); - return; + AActor *port = Sector->SkyBoxes[sector_t::floor]; + if (Z() < port->threshold && floorz < port->threshold) + { + fixedvec3 oldpos = Pos(); + if (islinked && !moved) UnlinkFromWorld(); + SetXYZ(PosRelative(port->Sector)); + PrevX += X() - oldpos.x; + PrevY += Y() - oldpos.y; + PrevZ += Z() - oldpos.z; + Sector = P_PointInSector(X(), Y()); + moved = true; + } + else break; } } + if (islinked && moved) LinkToWorld(); } // @@ -3392,7 +3397,7 @@ void AActor::Tick () UnlinkFromWorld (); flags |= MF_NOBLOCKMAP; SetXYZ(Vec3Offset(velx, vely, velz)); - CheckPortalTransition(); + CheckPortalTransition(false); SetMovement(velx, vely, velz); LinkToWorld (); } @@ -3860,7 +3865,7 @@ void AActor::Tick () Crash(); } - CheckPortalTransition(); + CheckPortalTransition(true); UpdateWaterLevel (oldz); diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 60a415d80..a97ebc65e 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -640,7 +640,8 @@ void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *ivi // Due to interpolation this is not necessarily the same as the sector the camera is in. viewsector = R_PointInSubsector(viewx, viewy)->sector; - if (!viewsector->PortalBlocksMovement(sector_t::ceiling)) + bool moved = false; + while (!viewsector->PortalBlocksMovement(sector_t::ceiling)) { AActor *point = viewsector->SkyBoxes[sector_t::ceiling]; if (viewz > point->threshold) @@ -648,16 +649,23 @@ void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *ivi viewx += point->scaleX; viewy += point->scaleY; viewsector = R_PointInSubsector(viewx, viewy)->sector; + moved = true; } + else break; } - if (!viewsector->PortalBlocksMovement(sector_t::floor)) + if (!moved) { - AActor *point = viewsector->SkyBoxes[sector_t::floor]; - if (viewz < point->threshold) + while (!viewsector->PortalBlocksMovement(sector_t::floor)) { - viewx += point->scaleX; - viewy += point->scaleY; - viewsector = R_PointInSubsector(viewx, viewy)->sector; + AActor *point = viewsector->SkyBoxes[sector_t::floor]; + if (viewz < point->threshold) + { + viewx += point->scaleX; + viewy += point->scaleY; + viewsector = R_PointInSubsector(viewx, viewy)->sector; + moved = true; + } + else break; } } } From 51da78ba29f693260acaee459d51224569b0ca6a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <c.oelckers@zdoom.fake> Date: Wed, 24 Feb 2016 10:35:29 +0100 Subject: [PATCH 10/13] - added a compatibility option to allow multiple exits to be triggered. This is required by Daedalus's travel tubes which contain a faulty script with some leftover debug code. --- src/compatibility.cpp | 1 + src/d_main.cpp | 4 +++- src/doomdef.h | 1 + src/g_level.cpp | 2 +- src/g_mapinfo.cpp | 1 + wadsrc/static/compatibility.txt | 5 +++++ wadsrc/static/language.enu | 1 + wadsrc/static/menudef.txt | 1 + 8 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 498fefb2a..c7df15027 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -146,6 +146,7 @@ static FCompatOption Options[] = { "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 }, { "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 }, { "pointonline", COMPATF2_POINTONLINE, SLOT_COMPAT2 }, + { "multiexit", COMPATF2_MULTIEXIT, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/d_main.cpp b/src/d_main.cpp index d2120f88e..bf451e408 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -572,7 +572,8 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) break; case 4: // Old ZDoom compat mode - v = COMPATF_SOUNDTARGET|COMPATF_LIGHT; + v = COMPATF_SOUNDTARGET | COMPATF_LIGHT; + w = COMPATF2_MULTIEXIT; break; case 5: // MBF compat mode @@ -627,6 +628,7 @@ CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES); CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE); CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF); CVAR (Flag, compat_pointonline, compatflags2, COMPATF2_POINTONLINE); +CVAR (Flag, compat_multiexit, compatflags2, COMPATF2_MULTIEXIT); //========================================================================== // diff --git a/src/doomdef.h b/src/doomdef.h index 802591386..3b066f8d8 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -341,6 +341,7 @@ enum COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom. COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less COMPATF2_POINTONLINE = 1 << 3, // Use original but buggy P_PointOnLineSide() and P_PointOnDivlineSide() + COMPATF2_MULTIEXIT = 1 << 4, // Level exit can be triggered multiple times (required by Daedalus's travel tubes, thanks to a faulty script) }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/g_level.cpp b/src/g_level.cpp index 5f305e5df..cfdfef9f5 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -535,7 +535,7 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill Printf (TEXTCOLOR_RED "Unloading scripts cannot exit the level again.\n"); return; } - if (gameaction == ga_completed) // do not exit multiple times. + if (gameaction == ga_completed && !(i_compatflags2 & COMPATF2_MULTIEXIT)) // do not exit multiple times. { return; } diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 1b1023fcd..b78dccf0d 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1328,6 +1328,7 @@ MapFlagHandlers[] = { "compat_floormove", MITYPE_COMPATFLAG, 0, COMPATF2_FLOORMOVE }, { "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF }, { "compat_pointonline", MITYPE_COMPATFLAG, 0, COMPATF2_POINTONLINE }, + { "compat_multiexit", MITYPE_COMPATFLAG, 0, COMPATF2_MULTIEXIT }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 0bd924622..0e25effb8 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -411,6 +411,11 @@ D0139194F7817BF06F3988DFC47DB38D // Whispers of Satan map29 nopassover } +5397C3B7D9B33AAF526D15A81A762828 // daedalus.wad Travel tubes (they are all identical) +{ + multiexit +} + D7F6E9F08C39A17026349A04F8C0B0BE // Return to Hadron, e1m9 19D03FFC875589E21EDBB7AB74EF4AEF // Return to Hadron, e1m9, 2016.01.03 update { diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 1fbf83b08..12ac43fa9 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2035,6 +2035,7 @@ CMPTMNU_SHORTTEX = "Find shortest textures like Doom"; CMPTMNU_STAIRS = "Use buggier stair building"; CMPTMNU_FLOORMOVE = "Use Doom's floor motion behavior"; CMPTMNU_POINTONLINE = "Use Doom's point-on-line algorithm"; +CMPTMNU_MULTIEXIT = "Level exit can be triggered more than once." CMPTMNU_PHYSICSBEHAVIOR = "Physics Behavior"; CMPTMNU_NOPASSOVER = "Actors are infinitely tall"; CMPTMNU_BOOMSCROLL = "Boom scrollers are additive"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index ab32efa65..40b24f6e5 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1304,6 +1304,7 @@ OptionMenu "CompatibilityOptions" Option "$CMPTMNU_STAIRS", "compat_stairs", "YesNo" Option "$CMPTMNU_FLOORMOVE", "compat_floormove", "YesNo" Option "$CMPTMNU_POINTONLINE", "compat_pointonline", "YesNo" + Option "$CMPTMNU_MULTIEXIT", "compat_multiexit", "YesNo" StaticText " " StaticText "$CMPTMNU_PHYSICSBEHAVIOR",1 From 8ba6f6ced5726a62718396fac52d5fe5857c55f9 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" <alexey.lysiuk@gmail.com> Date: Wed, 24 Feb 2016 11:26:42 +0200 Subject: [PATCH 11/13] Fixed crash when spawning decal without texture See http://forum.zdoom.org/viewtopic.php?t=50977 --- src/g_shared/a_decals.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index ebefcdff0..dcad93a3d 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -528,7 +528,11 @@ void DBaseDecal::Spread (const FDecalTemplate *tpl, side_t *wall, fixed_t x, fix GetWallStuff (wall, v1, ldx, ldy); rorg = Length (x - v1->x, y - v1->y); - tex = TexMan[PicNum]; + if ((tex = TexMan[PicNum]) == NULL) + { + return; + } + int dwidth = tex->GetWidth (); DecalWidth = dwidth * ScaleX; From 21c55a090af4fbf2dfcd3916c692cabcef36ae2a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <c.oelckers@zdoom.fake> Date: Wed, 24 Feb 2016 10:50:42 +0100 Subject: [PATCH 12/13] - fixed: "take armor" cheat should only deplete the armor, not destroy it. - fixed: Hexen armor cannot be depleted by the common function, it needs an override to achieve that. --- src/g_shared/a_armor.cpp | 8 ++++++++ src/g_shared/a_pickups.h | 1 + src/m_cheat.cpp | 6 +++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index c18f4172a..7be18139d 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -550,3 +550,11 @@ void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) } } + +void AHexenArmor::DepleteOrDestroy() +{ + for (int i = 0; i < 4; i++) + { + Slots[i] = 0; + } +} \ No newline at end of file diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 10391e0c8..ec16e129b 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -508,6 +508,7 @@ public: virtual AInventory *CreateTossable (); virtual bool HandlePickup (AInventory *item); virtual void AbsorbDamage (int damage, FName damageType, int &newdamage); + void DepleteOrDestroy(); fixed_t Slots[5]; fixed_t SlotsIncrement[4]; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index aa539f96b..b5da2a0a6 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -927,7 +927,7 @@ void cht_Take (player_t *player, const char *name, int amount) AInventory *ammo = player->mo->FindInventory(static_cast<PClassActor *>(type)); if (ammo) - ammo->Amount = 0; + ammo->DepleteOrDestroy(); } } @@ -943,10 +943,10 @@ void cht_Take (player_t *player, const char *name, int amount) if (type->IsDescendantOf (RUNTIME_CLASS (AArmor))) { - AActor *armor = player->mo->FindInventory(static_cast<PClassActor *>(type)); + AInventory *armor = player->mo->FindInventory(static_cast<PClassActor *>(type)); if (armor) - armor->Destroy (); + armor->DepleteOrDestroy(); } } From 8e2a629e5af7720270687d91964f4ec17f90bf10 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <c.oelckers@zdoom.fake> Date: Wed, 24 Feb 2016 11:19:58 +0100 Subject: [PATCH 13/13] - missed a semicolon, --- wadsrc/static/language.enu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 12ac43fa9..2dd1c3da2 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2035,7 +2035,7 @@ CMPTMNU_SHORTTEX = "Find shortest textures like Doom"; CMPTMNU_STAIRS = "Use buggier stair building"; CMPTMNU_FLOORMOVE = "Use Doom's floor motion behavior"; CMPTMNU_POINTONLINE = "Use Doom's point-on-line algorithm"; -CMPTMNU_MULTIEXIT = "Level exit can be triggered more than once." +CMPTMNU_MULTIEXIT = "Level exit can be triggered more than once."; CMPTMNU_PHYSICSBEHAVIOR = "Physics Behavior"; CMPTMNU_NOPASSOVER = "Actors are infinitely tall"; CMPTMNU_BOOMSCROLL = "Boom scrollers are additive";