From 0d1fbcf65fdc7b66140277343fba5c43728ea7a2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 23 Mar 2016 14:03:10 +0100 Subject: [PATCH 01/34] - flattened the following commits by Major Cooke: * Added falloff parameter to A_QuakeEx. - Treated just like A_Explode's 'fullradiusdamage' parameter, where the quake will fall off from this distance on out to the edge. Default is 0, which means no falloff. - Credits to MaxED and Michaelis for helping. * - Added HighPoint parameter to QuakeEx. - Allows fine tuning of where the quake's maximum or minimum occurs, in tics. This must be a range between [1, duration). - For up or down scaling quakes, this sets the quake to reach maximum sooner or start minimizing later. - For both, this indicates when the strongest will occur. Default is 0, or in the middle. The original commits were nearly impossible to find in the convoluted commit tree, so I think it's preferable to have one clean commit instead. --- src/g_shared/a_quake.cpp | 128 +++++++++++++++++++++++++----- src/g_shared/a_sharedglobal.h | 6 +- src/p_acs.cpp | 4 +- src/p_spec.h | 2 +- src/r_utility.cpp | 19 +++-- src/thingdef/thingdef_codeptr.cpp | 6 +- src/version.h | 2 +- wadsrc/static/actors/actor.txt | 2 +- 8 files changed, 131 insertions(+), 38 deletions(-) diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 2e1c5fa16..c93c574a3 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,96 @@ 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; + fixed_t tremordist = dist - m_Falloff; + assert(tremorsize > 0); + return (FRACUNIT - FixedMul(FRACUNIT,tremordist) / tremorsize); + } + else + { //Shouldn't happen. + return FRACUNIT; + } +} + //========================================================================== // // DEarthquake::StaticGetQuakeIntensity @@ -247,12 +328,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 +353,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 +390,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 +404,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 +415,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 +423,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 1e59bf740..1d5fd4ba5 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/p_acs.cpp b/src/p_acs.cpp index 11b169e6a..fe10921d0 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5747,7 +5747,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_spec.h b/src/p_spec.h index e3b1c05ad..83a50e252 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -944,7 +944,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/r_utility.cpp b/src/r_utility.cpp index f4bd85a9a..a5a422426 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -896,10 +896,9 @@ void R_AddInterpolationPoint(const fixedvec3a &vec) // //========================================================================== -static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset) +static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset, fixed_t falloff, fixed_t wfalloff) { fixed_t randumb; - if (intensity == 0) { randumb = 0; @@ -908,7 +907,8 @@ static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset) { randumb = pr_torchflicker(intensity * 2) - intensity; } - return FixedMul(factor, randumb + offset); + fixed_t rn2 = (FixedMul(wfalloff,offset) + FixedMul(falloff, randumb)); + return FixedMul(factor, rn2); } //========================================================================== @@ -1050,34 +1050,33 @@ void R_SetupFrame (AActor *actor) if ((jiggers.RelIntensityX | jiggers.RelOffsetX) != 0) { int ang = (camera->angle) >> 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/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c5081f57a..9a492130b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4969,7 +4969,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 +4986,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; } diff --git a/src/version.h b/src/version.h index 764675f0b..17fd288b1 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 4534 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index aeeaec813..e4a9b152b 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -277,7 +277,7 @@ ACTOR Actor native //: Thinker action native A_SetUserArray(name varname, int index, int 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); From b16e696157dc6ed9466e3b96d2a3b5e95221a101 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 10 Mar 2016 12:54:23 -0600 Subject: [PATCH 02/34] - Added offset and angle parameters to A_CheckBlock. - Includes 2 flags, affixed by CBF_: AbsolutePos, and AbsoluteAngle. - AbsolutePos: Absolute position of where to check. - AbsoluteAngle: Angle parameter is used as is, not added onto the actor's current angle. --- src/thingdef/thingdef_codeptr.cpp | 52 ++++++++++++++++++++++++++++-- wadsrc/static/actors/actor.txt | 2 +- wadsrc/static/actors/constants.txt | 2 ++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 9a492130b..6efbeb167 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -6696,14 +6696,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); @@ -6713,8 +6719,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/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index e4a9b152b..e3bb06fe7 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -317,7 +317,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 3060c2c2d..9ccb1fd6e 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 From deafa0bce0b145af71067833940ecf4f12b2e880 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 24 Mar 2016 18:01:01 +0100 Subject: [PATCH 03/34] - fixed: The 'open door in 5 minutes sector type had the order of parameters wrong, due to a bad function prototype. --- src/p_spec.cpp | 2 +- src/p_spec.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 8a8f21c50..7e14f1ec3 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -1213,7 +1213,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: diff --git a/src/p_spec.h b/src/p_spec.h index 83a50e252..d40fb3da7 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -535,7 +535,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 (); From 75bcec411eb5aef9713a6f51907be0c36df0fd78 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 24 Mar 2016 18:26:27 +0100 Subject: [PATCH 04/34] - fixed: The portal blockmap's AddLineIntercepts methods needs to check the block's range. --- src/portal.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/portal.cpp b/src/portal.cpp index de2d1d14b..ad8e166b1 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; i Date: Thu, 24 Mar 2016 12:49:09 -0500 Subject: [PATCH 05/34] Fixed A_QuakeEx falloff not working properly. --- src/g_shared/a_quake.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index c93c574a3..1a9211b4c 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -289,9 +289,8 @@ fixed_t DEarthquake::GetFalloff(fixed_t dist) const 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; - fixed_t tremordist = dist - m_Falloff; assert(tremorsize > 0); - return (FRACUNIT - FixedMul(FRACUNIT,tremordist) / tremorsize); + return (FRACUNIT - FixedDiv((dist - m_Falloff), tremorsize)); } else { //Shouldn't happen. From 8add60ed3855787ed29af07097d62e0ff019142a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 24 Mar 2016 19:24:33 +0100 Subject: [PATCH 06/34] - fixed: Getting the nearest ceiling height failed for height-less actors. --- src/p_sectors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 27c8493ea..25cd0ead8 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; From b70fee8ed88351ef714e1c92fc9658e962d11f9e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 25 Mar 2016 02:08:22 +0100 Subject: [PATCH 07/34] - changed the means how to control the slowdown of crushing ceilings encountering an obstacle and corrected a few mistakes in the implementation * there is a new crushing mode 3, which means that the crusher will always slow down if it hits an obstacle. * crushing mode 1 (Doom mode) will never slow down. * crushing mode 0 (compatibility) will only slow down for the specials that did so before, and only if both up and downspeed are 8 and the game is not Hexen. The following specials are affected: * Ceiling_LowerAndCrush * Ceiling_LowerAndCrushDist * Ceiling_CrushAndRaise * Ceiling_CrushAndRaiseA * Ceiling_CrushAndRaiseDist * Ceiling_CrushAndRaiseSilentA * Ceiling_CrushAndRaiseSilentDist * Generic_Crusher was fixed to act like in Boom: Not only a speed value of 8 will cause slowdown, but all speed values up to 24. * Hexen crushing mode will never cause slowdowns because Hexen never did this. (which also makes no real sense, considering that the crusher waits for the obstacle to die.) --- src/p_ceiling.cpp | 22 +++++++++---- src/p_lnspec.cpp | 82 +++++++++++++++++++++++++++-------------------- src/p_spec.h | 14 ++++++-- 3 files changed, 73 insertions(+), 45 deletions(-) diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 7b4fb2118..95be8f7bc 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_lnspec.cpp b/src/p_lnspec.cpp index 7c6f05b29..bc70388ae 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -84,9 +84,21 @@ static const BYTE ChangeMap[8] = { 0, 1, 5, 3, 7, 2, 6, 0 }; #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. @@ -628,43 +640,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) @@ -676,141 +688,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) @@ -841,14 +853,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) @@ -856,7 +868,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) @@ -1944,7 +1956,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 diff --git a/src/p_spec.h b/src/p_spec.h index d40fb3da7..b0baaa1ea 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -647,6 +647,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 +663,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 +673,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 +696,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); From b5f333798ed09e9c66570eb9b110ebf3dc8a1a85 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Mar 2016 17:27:55 +0200 Subject: [PATCH 08/34] - moved all scroller related code into its own file, including the DScroller class definition. --- src/CMakeLists.txt | 1 + src/dobject.h | 2 + src/dthinker.h | 1 + src/g_level.cpp | 2 +- src/g_level.h | 2 +- src/p_lnspec.cpp | 131 +-------- src/p_scroll.cpp | 719 +++++++++++++++++++++++++++++++++++++++++++++ src/p_spec.cpp | 494 +------------------------------ src/p_spec.h | 78 ++--- src/po_man.h | 1 + 10 files changed, 766 insertions(+), 665 deletions(-) create mode 100644 src/p_scroll.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d23eb084a..4a8101d79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1071,6 +1071,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE p_plats.cpp p_pspr.cpp p_saveg.cpp + p_scroll.cpp p_sectors.cpp p_setup.cpp p_sight.cpp diff --git a/src/dobject.h b/src/dobject.h index 22e612723..682e9bb08 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/dthinker.h b/src/dthinker.h index c7b92730c..10a53309d 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/g_level.cpp b/src/g_level.cpp index 844311092..1a626a40c 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -2023,7 +2023,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 ca70b654b..17a2a1920 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/p_lnspec.cpp b/src/p_lnspec.cpp index bc70388ae..ca17a4f20 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2145,17 +2145,11 @@ 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. + TArray Collection; { TThinkerIterator iterator; FThinkerCollection collect; @@ -2188,7 +2182,6 @@ void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type) new DPusher (type, NULL, magnitude, angle, NULL, secnum); } } - Collection.Clear (); } FUNC(LS_Sector_SetWind) @@ -2249,76 +2242,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) @@ -2340,7 +2266,7 @@ FUNC(LS_Scroll_Texture_Both) sidechoice = 0; } - SetWallScroller (arg0, sidechoice, dx, dy, 7); + SetWallScroller (arg0, sidechoice, dx, dy, scw_all); return true; } @@ -2351,47 +2277,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. @@ -2404,19 +2293,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; } @@ -2427,7 +2316,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_scroll.cpp b/src/p_scroll.cpp new file mode 100644 index 000000000..96889b624 --- /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_spec.cpp b/src/p_spec.cpp index 7e14f1ec3..700cf2c1e 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -84,44 +84,10 @@ 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 () { } @@ -149,7 +115,7 @@ void DPusher::Serialize (FArchive &arc) } // 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 @@ -1243,7 +1209,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; @@ -1304,13 +1270,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); } @@ -1533,458 +1499,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 diff --git a/src/p_spec.h b/src/p_spec.h index b0baaa1ea..129e7f03b 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,58 +67,6 @@ 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) }; diff --git a/src/po_man.h b/src/po_man.h index be3b958b2..6ab7d20a7 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 { From 59920095af280ba4bc7df1d34256b885e7fe8761 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Mar 2016 21:57:22 +0200 Subject: [PATCH 09/34] - separated pushers into their own file. --- src/CMakeLists.txt | 1 + src/doomstat.cpp | 1 - src/doomstat.h | 1 - src/p_lnspec.cpp | 41 +---- src/p_spec.cpp | 370 +-------------------------------------------- src/p_spec.h | 45 ------ 6 files changed, 5 insertions(+), 454 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a8101d79..e53749904 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1070,6 +1070,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE p_pillar.cpp p_plats.cpp p_pspr.cpp + p_pusher.cpp p_saveg.cpp p_scroll.cpp p_sectors.cpp diff --git a/src/doomstat.cpp b/src/doomstat.cpp index 8f314ad50..d693aae1b 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 43d81e866..e762b07dc 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/p_lnspec.cpp b/src/p_lnspec.cpp index ca17a4f20..849295c2c 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2146,43 +2146,8 @@ FUNC(LS_Sector_ChangeFlags) } -void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type) -{ - // 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); - } - } -} +void AdjustPusher(int tag, int magnitude, int angle, bool wind); FUNC(LS_Sector_SetWind) // Sector_SetWind (tag, amount, angle) @@ -2190,7 +2155,7 @@ FUNC(LS_Sector_SetWind) if (arg3) return false; - AdjustPusher (arg0, arg1, arg2, DPusher::p_wind); + AdjustPusher (arg0, arg1, arg2, true); return true; } @@ -2200,7 +2165,7 @@ FUNC(LS_Sector_SetCurrent) if (arg3) return false; - AdjustPusher (arg0, arg1, arg2, DPusher::p_current); + AdjustPusher (arg0, arg1, arg2, false); return true; } diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 700cf2c1e..834df5956 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -84,40 +84,11 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); EXTERN_CVAR(Bool, cl_predict_specials) -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; -} // killough 3/7/98: Initialize generalized scrolling 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 @@ -1631,345 +1602,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 129e7f03b..73f913a00 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -71,51 +71,6 @@ typedef enum // (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 From 263051a77bea81109cc309621d245bf7bf4eb32f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Mar 2016 22:20:25 +0200 Subject: [PATCH 10/34] - removed a few unnecessary #includes. --- src/fragglescript/t_func.cpp | 1 - src/g_level.cpp | 1 - src/g_shared/a_pickups.cpp | 1 - src/g_shared/shared_sbar.cpp | 1 - src/p_interaction.cpp | 3 +-- src/p_lnspec.cpp | 1 - src/r_draw.cpp | 1 - src/statistics.cpp | 1 - src/thingdef/thingdef.cpp | 1 - src/v_blend.cpp | 1 - 10 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 953297b31..bfda35ad4 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_level.cpp b/src/g_level.cpp index 1a626a40c..4fab2e28d 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -79,7 +79,6 @@ #include "v_palette.h" #include "menu/menu.h" #include "a_sharedglobal.h" -#include "a_strifeglobal.h" #include "r_data/colormaps.h" #include "farchive.h" #include "r_renderer.h" diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index b2a80cbc2..76f4bb033 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -11,7 +11,6 @@ #include "c_dispatch.h" #include "gstrings.h" #include "templates.h" -#include "a_strifeglobal.h" #include "a_morph.h" #include "a_specialspot.h" #include "thingdef/thingdef.h" diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index d0153a98f..16f6d7319 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/p_interaction.cpp b/src/p_interaction.cpp index 90b43323c..648b9f46c 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 849295c2c..6c3dccebd 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -51,7 +51,6 @@ #include "gi.h" #include "m_random.h" #include "p_conversation.h" -#include "a_strifeglobal.h" #include "r_data/r_translate.h" #include "p_3dmidtex.h" #include "d_net.h" diff --git a/src/r_draw.cpp b/src/r_draw.cpp index c16817691..a3cb0de03 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -33,7 +33,6 @@ #include "v_video.h" #include "doomstat.h" #include "st_stuff.h" -#include "a_hexenglobal.h" #include "g_game.h" #include "g_level.h" #include "r_data/r_translate.h" diff --git a/src/statistics.cpp b/src/statistics.cpp index 292d66f51..3676b8678 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/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 551dde93a..a24e8831c 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/v_blend.cpp b/src/v_blend.cpp index 3aff4a3a3..b8f3e713d 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" From 8c3c18b0085aeb486d6e13a774dde0b158defb42 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Mar 2016 22:25:12 +0200 Subject: [PATCH 11/34] - forgot to add this... --- src/p_pusher.cpp | 485 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 src/p_pusher.cpp diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp new file mode 100644 index 000000000..722e1dc0c --- /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); + } + } +} From a92de84cf795af39b1387417761cf432d87bba16 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Mar 2016 22:53:10 +0200 Subject: [PATCH 12/34] - this stuff should not have been saved... --- src/g_level.cpp | 1 + src/g_shared/a_pickups.cpp | 1 + src/p_lnspec.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/src/g_level.cpp b/src/g_level.cpp index 4fab2e28d..1a626a40c 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -79,6 +79,7 @@ #include "v_palette.h" #include "menu/menu.h" #include "a_sharedglobal.h" +#include "a_strifeglobal.h" #include "r_data/colormaps.h" #include "farchive.h" #include "r_renderer.h" diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 76f4bb033..b2a80cbc2 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -11,6 +11,7 @@ #include "c_dispatch.h" #include "gstrings.h" #include "templates.h" +#include "a_strifeglobal.h" #include "a_morph.h" #include "a_specialspot.h" #include "thingdef/thingdef.h" diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 6c3dccebd..849295c2c 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -51,6 +51,7 @@ #include "gi.h" #include "m_random.h" #include "p_conversation.h" +#include "a_strifeglobal.h" #include "r_data/r_translate.h" #include "p_3dmidtex.h" #include "d_net.h" From e2711a74e784a0980f7ca2085fe2af1406df1f34 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 21:48:57 -0500 Subject: [PATCH 13/34] Add float user vars for DECORATE - PClass::Extend now takes alignment into consideration. --- src/dobjtype.cpp | 15 ++++++++------- src/dobjtype.h | 3 ++- src/thingdef/thingdef_parse.cpp | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 609ec1721..121c0fb27 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2403,22 +2403,23 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) //========================================================================== // -// PClass:: Extend +// PClass :: Extend // -// Add bytes to the end of this class. Returns the previous -// size of the class. +// Add bytes to the end of this class and possibly more to meet +// alignment restrictions. Returns the start of the extended block. // //========================================================================== -unsigned int PClass::Extend(unsigned int extension) +unsigned int PClass::Extend(unsigned int extension, unsigned int alignment) { assert(this->bRuntimeClass); unsigned int oldsize = Size; - Size += extension; + unsigned int padto = (oldsize + alignment - 1) & ~(alignment - 1); + Size = padto + extension; Defaults = (BYTE *)M_Realloc(Defaults, Size); - memset(Defaults + oldsize, 0, extension); - return oldsize; + memset(Defaults + oldsize, 0, Size - oldsize); + return padto; } //========================================================================== diff --git a/src/dobjtype.h b/src/dobjtype.h index b482efe45..05097795b 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -660,7 +660,8 @@ public: void InsertIntoHash(); DObject *CreateNew() const; PClass *CreateDerivedClass(FName name, unsigned int size); - unsigned int Extend(unsigned int extension); + unsigned int Extend(unsigned int extension, unsigned int alignment); + unsigned int Extend(const PType *type) { return Extend(type->Size, type->Align); } void InitializeActorInfo(); void BuildFlatPointers(); const PClass *NativeClass() const; diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 97e22dc8c..cc2c71e34 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 @@ -585,7 +585,7 @@ 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); + sym->Offset = cls->Extend(type); if (symt->AddSymbol(sym) == NULL) { delete sym; From feb5ab31cc920cdc1053d35cef907c83ec5e8592 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 22:05:25 -0500 Subject: [PATCH 14/34] Add double variants of SetValue() for numeric PTypes --- src/dobjtype.cpp | 29 ++++++++++++++++++++++++++++- src/dobjtype.h | 5 +++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 121c0fb27..0a964917c 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -392,7 +392,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"); } //========================================================================== @@ -682,6 +687,11 @@ void PInt::SetValue(void *addr, int val) } } +void PInt::SetValue(void *addr, double val) +{ + SetValue(addr, (int)val); +} + //========================================================================== // // PInt :: GetValueInt @@ -927,6 +937,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) @@ -1112,6 +1127,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 @@ -1173,6 +1194,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 diff --git a/src/dobjtype.h b/src/dobjtype.h index 05097795b..a5e2ab2a2 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -188,6 +188,7 @@ public: // 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; @@ -320,6 +321,7 @@ public: PInt(unsigned int size, bool unsign); virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; @@ -344,6 +346,7 @@ public: PFloat(unsigned int size); virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; @@ -410,6 +413,7 @@ public: PFixed(); virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; @@ -422,6 +426,7 @@ public: PAngle(); virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; virtual int GetStoreOp() const; virtual int GetLoadOp() const; From 299019ea158d794e599e5bdf739d98285e64d9f6 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 22:24:59 -0500 Subject: [PATCH 15/34] Add GetValueFloat() for numeric PTypes --- src/dobjtype.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++-- src/dobjtype.h | 5 +++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 0a964917c..552b8ee68 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -412,6 +412,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 @@ -724,6 +730,17 @@ int PInt::GetValueInt(void *addr) const } } +//========================================================================== +// +// PInt :: GetValueFloat +// +//========================================================================== + +double PInt::GetValueFloat(void *addr) const +{ + return GetValueInt(addr); +} + //========================================================================== // // PInt :: GetStoreOp @@ -962,16 +979,27 @@ void PFloat::SetValue(void *addr, double 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; } } @@ -1145,6 +1173,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 @@ -1212,6 +1252,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 diff --git a/src/dobjtype.h b/src/dobjtype.h index a5e2ab2a2..e9ff98245 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -192,6 +192,7 @@ public: // 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; @@ -323,6 +324,7 @@ public: 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; @@ -348,6 +350,7 @@ public: 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; @@ -415,6 +418,7 @@ public: 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; }; @@ -428,6 +432,7 @@ public: 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; }; From b6e3358b1cf62e39bd4f6c7d54ffacf546bd3cec Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 22:14:31 -0500 Subject: [PATCH 16/34] Add A_SetUserVarFloat and A_SetUserArrayFloat --- src/thingdef/thingdef_codeptr.cpp | 86 ++++++++++++++++++++++++------- wadsrc/static/actors/actor.txt | 2 + 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6efbeb167..9fa547f46 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4681,22 +4681,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 +4730,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 +4740,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; } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index e3bb06fe7..7548cd6c9 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -275,6 +275,8 @@ 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, int falloff = 0, int highpoint = 0); From 35121544b4eb5d8dbdad1aedd9889b12db539cc7 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 22:33:10 -0500 Subject: [PATCH 17/34] Add float support to ACS's Get/SetUserVariable functions - "Support" means that setting one will convert from fixed point to floating point, and reading one will do the reverse. --- src/p_acs.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index fe10921d0..42048c159 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4567,7 +4567,14 @@ static void SetUserVariable(AActor *self, FName varname, int index, int value) if (GetVarAddrType(self, varname, index, addr, type)) { - type->SetValue(addr, value); + if (!type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + type->SetValue(addr, value); + } + else + { + type->SetValue(addr, FIXED2DBL(value)); + } } } @@ -4578,7 +4585,14 @@ static int GetUserVariable(AActor *self, FName varname, int index) if (GetVarAddrType(self, varname, index, addr, type)) { - return type->GetValueInt(addr); + if (!type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + return type->GetValueInt(addr); + } + else + { + return FLOAT2FIXED(type->GetValueFloat(addr)); + } } return 0; } From b37ef48e99a5729a21356e30ab36f1f671eebd5a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 22:36:26 -0500 Subject: [PATCH 18/34] Allow ACS's GetUserVariable to access non-user variables - Since DECORATE already allows reading all declared variables in a class, where's the utility in keeping this restriction in ACS? - Variables must still be numeric types. - SetUserVariable is still restricted to user variables only. --- src/p_acs.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 42048c159..9dbbd5eea 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 allownative) { PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); PArray *arraytype; - if (var == NULL || (var->Flags & VARF_Native)) + if (var == NULL || (!allownative && (var->Flags & VARF_Native))) { return false; } @@ -4557,6 +4557,12 @@ 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))) + { + return false; + } return true; } @@ -4565,7 +4571,7 @@ 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)) { if (!type->IsKindOf(RUNTIME_CLASS(PFloat))) { @@ -4583,7 +4589,7 @@ 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)) { if (!type->IsKindOf(RUNTIME_CLASS(PFloat))) { From 1648a71e45c00296094f0505c79f8d8b2f7f8fa6 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 29 Mar 2016 22:49:12 -0500 Subject: [PATCH 19/34] Add support for Name and String types to ACS's GetUserVariable - Reading one of these types will copy its value into the global ACS string table and return the index. --- src/p_acs.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 9dbbd5eea..143683602 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 allownative) +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 || (!allownative && (var->Flags & VARF_Native))) + if (var == NULL || (!readonly && (var->Flags & VARF_Native))) { return false; } @@ -4561,6 +4561,11 @@ bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType * // 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; @@ -4591,13 +4596,21 @@ static int GetUserVariable(AActor *self, FName varname, int index) if (GetVarAddrType(self, varname, index, addr, type, true)) { - if (!type->IsKindOf(RUNTIME_CLASS(PFloat))) + if (type->IsKindOf(RUNTIME_CLASS(PFloat))) { - return type->GetValueInt(addr); + 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 FLOAT2FIXED(type->GetValueFloat(addr)); + return type->GetValueInt(addr); } } return 0; From 81f29556bf3493da8e290442082ae01c627aefc3 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 2 Apr 2016 23:11:59 -0400 Subject: [PATCH 20/34] - Refactored SBarInfo flow control so negatable commands are handled statically. - Made TArray movable and TDeletingArray a move only type. --- src/g_shared/sbarinfo.cpp | 112 ++++++++++++--------- src/g_shared/sbarinfo_commands.cpp | 151 ++++++++--------------------- src/tarray.h | 34 ++++++- src/templates.h | 3 +- 4 files changed, 137 insertions(+), 163 deletions(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 23733efcf..2fcc29b05 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 e08fb1a20..dcb0cd544 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,31 @@ 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; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tarray.h b/src/tarray.h index 7b9d5970b..3ef4f2d99 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -39,6 +39,7 @@ #include #include #include +#include #if !defined(_WIN32) #include // for intptr_t @@ -137,11 +138,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 +164,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 +439,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 4f75a961a..0737fc4e9 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__ From 42edd7db2264622b034c21db32e762957ef64972 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 2 Apr 2016 23:47:15 -0400 Subject: [PATCH 21/34] - Added IfInvulnerable SBARINFO command (modified from Blue-Shadow's pull request) --- src/g_shared/sbarinfo_commands.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index dcb0cd544..a471ff8b2 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -3424,6 +3424,23 @@ class CommandIfHealth : public SBarInfoNegatableFlowControl //////////////////////////////////////////////////////////////////////////////// +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); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + static const char *SBarInfoCommandNames[] = { "drawimage", "drawnumber", "drawswitchableimage", @@ -3434,6 +3451,7 @@ static const char *SBarInfoCommandNames[] = "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", "weaponammo", "ininventory", "alpha", "ifhealth", + "ifinvulnerable", NULL }; @@ -3447,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) @@ -3480,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); From 6d441e25dbb2eab32a55272776d8850c0ac6d68f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 3 Apr 2016 22:31:45 +0200 Subject: [PATCH 22/34] - partially undid the 'repeated flash state' fix, because it didn't work with the stock weapons. I'm still not sure if a compatibility setting is needed. --- src/g_doom/a_doomweaps.cpp | 6 ++++++ src/g_strife/a_strifeweapons.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index b43b5a1d5..b47518f69 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_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index 8129a8bfc..90a934b82 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) { From 1011e26eb98393a8b5e582df4e1d58d70012e5ee Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 3 Apr 2016 22:45:54 +0200 Subject: [PATCH 23/34] - fixed: P_RadiusAttack should not call P_DamageMobj for a damage value of 0. This could happen if the damage calculations resulted in a value between 0 and 1, which for the actual check was multiplied with the damage parameter of P_RadiusAttack which inflated the fractional value to something that looked like actual damage but was later truncated. --- src/p_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index aafe2d125..c6439d966 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; From 0cc2705b99f641012d2622d7f8c36de2692ba390 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 15:36:23 -0500 Subject: [PATCH 24/34] Added A_LogFloat --- src/thingdef/thingdef_codeptr.cpp | 16 ++++++++++++++++ wadsrc/static/actors/actor.txt | 1 + 2 files changed, 17 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 9fa547f46..c09615067 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 diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 7548cd6c9..aa751e332 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 From 3357af32e59ef8be735685a941c5c5a44cb5fd7e Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 16:07:51 -0500 Subject: [PATCH 25/34] Generalize FxArrayElement to work with all numeric elements (not just 32-bit ints) --- src/thingdef/thingdef_expression.cpp | 32 ++++++++++++++++++++-------- src/thingdef/thingdef_parse.cpp | 1 - 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 7ecc3c3c4..0312126d4 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 (1 << 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 cc2c71e34..311930508 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -588,7 +588,6 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl sym->Offset = cls->Extend(type); if (symt->AddSymbol(sym) == NULL) { - delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; From 806d9d7a953cb8d935656cd41c63e1cb75abcd2f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 16:18:59 -0500 Subject: [PATCH 26/34] Add an AddField() override to PClass that extends the default instance --- src/dobjtype.cpp | 18 ++++++++++++++++++ src/dobjtype.h | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 552b8ee68..028b9e82b 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2501,6 +2501,24 @@ unsigned int PClass::Extend(unsigned int extension, unsigned int alignment) return padto; } +//========================================================================== +// +// PClass :: AddField +// +//========================================================================== + +PField *PClass::AddField(FName name, PType *type, DWORD flags) +{ + 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); + } + return field; +} + //========================================================================== // // PClass :: FindClassTentative diff --git a/src/dobjtype.h b/src/dobjtype.h index e9ff98245..a1fcd13e5 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -585,7 +585,7 @@ 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(); protected: @@ -672,6 +672,7 @@ public: PClass *CreateDerivedClass(FName name, unsigned int size); unsigned int Extend(unsigned int extension, unsigned int alignment); unsigned int Extend(const PType *type) { return Extend(type->Size, type->Align); } + PField *AddField(FName name, PType *type, DWORD flags=0) override; void InitializeActorInfo(); void BuildFlatPointers(); const PClass *NativeClass() const; From da496bbe62602ccdc7aae006f0409dcd909f558d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 15:48:09 -0500 Subject: [PATCH 27/34] Use AddField() to add user variables in DECORATE --- src/thingdef/thingdef_parse.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 311930508..f5359a7d8 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -584,9 +584,8 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl } sc.MustGetToken(';'); - PField *sym = new PField(symname, type, 0); - sym->Offset = cls->Extend(type); - if (symt->AddSymbol(sym) == NULL) + PField *sym = cls->AddField(symname, type, 0); + if (cls == NULL) { sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); From 85c82184417c1cb9fe4919d16304fbda67b576cc Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Apr 2016 23:10:43 -0500 Subject: [PATCH 28/34] Added methods for PTypes to serialize their values - Values are tagged to allow for some measure of changing variable types without automatically breaking savegames. - Use these new methods to serialize the non-native variables in an object. This allows for achiving non-ints. --- src/dobject.cpp | 45 +-- src/dobjtype.cpp | 894 +++++++++++++++++++++++++++++++++++++++++++++++ src/dobjtype.h | 85 +++++ src/farchive.cpp | 36 +- src/farchive.h | 7 + src/version.h | 2 +- 6 files changed, 1027 insertions(+), 42 deletions(-) diff --git a/src/dobject.cpp b/src/dobject.cpp index efb437072..d7e23bdb0 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -417,46 +417,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/dobjtype.cpp b/src/dobjtype.cpp index 028b9e82b..1d781c02c 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,121 @@ 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 :: SetValue @@ -662,6 +778,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 @@ -947,6 +1234,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 @@ -1080,6 +1458,45 @@ 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; +} + /* PName ******************************************************************/ IMPLEMENT_CLASS(PName) @@ -1096,6 +1513,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) @@ -1112,6 +1569,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) @@ -1143,6 +1642,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 @@ -1222,6 +1760,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 @@ -1334,6 +1911,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) @@ -1421,6 +2029,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 @@ -1626,6 +2273,61 @@ 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; +} + //========================================================================== // // NewArray @@ -1879,6 +2581,95 @@ PStruct::PStruct(FName name, DObject *outer) { } +//========================================================================== +// +// 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 @@ -2091,6 +2882,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 @@ -2354,6 +3228,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 diff --git a/src/dobjtype.h b/src/dobjtype.h index a1fcd13e5..5ad5757f3 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -186,6 +186,20 @@ 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 value of a variable of this type at (addr) virtual void SetValue(void *addr, int val); virtual void SetValue(void *addr, double val); @@ -321,6 +335,9 @@ 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; @@ -347,6 +364,9 @@ 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; @@ -381,6 +401,9 @@ public: PString(); virtual int GetRegType() const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; }; // Variations of integer types ---------------------------------------------- @@ -390,6 +413,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 @@ -397,6 +423,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 @@ -415,6 +444,9 @@ 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; @@ -429,6 +461,9 @@ 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; @@ -445,6 +480,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; @@ -465,6 +503,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(); }; @@ -531,6 +573,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: PArray(); }; @@ -588,6 +634,12 @@ public: 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; + + static void WriteFields(FArchive &ar, const void *addr, const TArray &fields); + bool ReadFields(FArchive &ar, void *addr) const; protected: PStruct(); }; @@ -650,6 +702,9 @@ 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(); @@ -694,6 +749,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)); } @@ -844,4 +902,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/farchive.cpp b/src/farchive.cpp index 8fd647fd2..1f805ae8e 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 b646827de..2324c6b2a 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/version.h b/src/version.h index 17fd288b1..17e4fa3f1 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 4534 +#define SAVEVER 4535 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From 15208188de87bbd34c9a648a275d0758f7519eec Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 16:25:08 -0500 Subject: [PATCH 29/34] Remove PClass::Extend() --- src/dobjtype.cpp | 21 --------------------- src/dobjtype.h | 2 -- 2 files changed, 23 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 1d781c02c..11fd04e82 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -3374,27 +3374,6 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) return type; } -//========================================================================== -// -// PClass :: Extend -// -// Add bytes to the end of this class and possibly more to meet -// alignment restrictions. Returns the start of the extended block. -// -//========================================================================== - -unsigned int PClass::Extend(unsigned int extension, unsigned int alignment) -{ - assert(this->bRuntimeClass); - - unsigned int oldsize = Size; - unsigned int padto = (oldsize + alignment - 1) & ~(alignment - 1); - Size = padto + extension; - Defaults = (BYTE *)M_Realloc(Defaults, Size); - memset(Defaults + oldsize, 0, Size - oldsize); - return padto; -} - //========================================================================== // // PClass :: AddField diff --git a/src/dobjtype.h b/src/dobjtype.h index 5ad5757f3..679804566 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -725,8 +725,6 @@ public: void InsertIntoHash(); DObject *CreateNew() const; PClass *CreateDerivedClass(FName name, unsigned int size); - unsigned int Extend(unsigned int extension, unsigned int alignment); - unsigned int Extend(const PType *type) { return Extend(type->Size, type->Align); } PField *AddField(FName name, PType *type, DWORD flags=0) override; void InitializeActorInfo(); void BuildFlatPointers(); From 7c8cff64e682cb6ce39b4b111937a094b9571945 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 17:45:04 -0500 Subject: [PATCH 30/34] Added code to initialize and destroy string variables in classes - Will require being able to add strings to non-native classes to actually test this. --- src/dobject.cpp | 56 ++++++----- src/dobjtype.cpp | 144 +++++++++++++++++++++++++++ src/dobjtype.h | 27 +++++ src/tarray.h | 14 ++- src/thingdef/thingdef_expression.cpp | 2 +- 5 files changed, 212 insertions(+), 31 deletions(-) diff --git a/src/dobject.cpp b/src/dobject.cpp index d7e23bdb0..511f3e5f0 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); } } diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 11fd04e82..fbc3ceb73 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -500,6 +500,36 @@ void PType::SkipValue(FArchive &ar, int tag) } } +//========================================================================== +// +// 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 @@ -1497,6 +1527,43 @@ bool PString::ReadValue(FArchive &ar, void *addr) const 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) @@ -2328,6 +2395,20 @@ bool PArray::ReadValue(FArchive &ar, void *addr) const 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 @@ -2581,6 +2662,23 @@ 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 @@ -3287,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 @@ -3388,6 +3527,11 @@ PField *PClass::AddField(FName name, PType *type, DWORD flags) { 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 679804566..fe04bc634 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 @@ -200,6 +202,22 @@ public: 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); @@ -404,6 +422,9 @@ public: 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 ---------------------------------------------- @@ -577,6 +598,8 @@ public: 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(); }; @@ -637,6 +660,7 @@ public: 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; @@ -697,7 +721,9 @@ 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; @@ -728,6 +754,7 @@ public: 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. diff --git a/src/tarray.h b/src/tarray.h index 3ef4f2d99..bd28d86ab 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -94,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]; diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 0312126d4..e1a9e653e 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3039,7 +3039,7 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { ExpEmit indexv(index->Emit(build)); int shiftbits = 0; - while (1 << shiftbits < arraytype->ElementSize) + while (1u << shiftbits < arraytype->ElementSize) { shiftbits++; } From c90a1c0c9627d2cb4d5ad105ae7020582c8b2cab Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 18:10:09 -0500 Subject: [PATCH 31/34] Add "support" for user string variables in DECORATE - This is "support" in the very most basic sense. You can declare them, but you can't actually do anything with them, since the decorate parser can't handle expressions when it's parsing string arguments. However, they seem to be getting properly initialized and destroyed, which is what this was added to test. If it doesn't look like too much trouble, I might try to turn them into something actually worth something. --- src/thingdef/thingdef_parse.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index f5359a7d8..bf929128b 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -533,12 +533,17 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl // Read the type and make sure it's acceptable. sc.MustGetAnyToken(); - if (sc.TokenType != TK_Int && sc.TokenType != TK_Float) + switch (sc.TokenType) { - sc.ScriptMessage("User variables must be of type 'int' or 'float'"); + case TK_Int: type = TypeSInt32; break; + case TK_Float: type = TypeFloat64; break; + case TK_String: type = TypeString; break; + default: + type = TypeError; + sc.ScriptMessage("User variables must be of type 'int' or 'float' or 'string'"); FScriptPosition::ErrorCounter++; + break; } - 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 From 17972b5d06213f48a1b5bd57c11ebf8831631738 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 19:15:00 -0500 Subject: [PATCH 32/34] Revert "Add "support" for user string variables in DECORATE" - This reverts commit c90a1c0c9627d2cb4d5ad105ae7020582c8b2cab. - DECORATE looks to be very dependant on functions that take strings as parameters receiving those strings as constants and not as expressions, so being able to declare string variables with DECORATE is pretty much useless. --- src/thingdef/thingdef_parse.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index bf929128b..f5359a7d8 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -533,17 +533,12 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl // Read the type and make sure it's acceptable. sc.MustGetAnyToken(); - switch (sc.TokenType) + if (sc.TokenType != TK_Int && sc.TokenType != TK_Float) { - case TK_Int: type = TypeSInt32; break; - case TK_Float: type = TypeFloat64; break; - case TK_String: type = TypeString; break; - default: - type = TypeError; - sc.ScriptMessage("User variables must be of type 'int' or 'float' or 'string'"); + sc.ScriptMessage("User variables must be of type 'int' or 'float'"); FScriptPosition::ErrorCounter++; - break; } + 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 From 77f9643c8ff167fe738ba4111e985aa664738582 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 20:25:07 -0500 Subject: [PATCH 33/34] How did this end up wrong? --- src/thingdef/thingdef_parse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index f5359a7d8..70f2c050d 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -585,7 +585,7 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl sc.MustGetToken(';'); PField *sym = cls->AddField(symname, type, 0); - if (cls == NULL) + if (sym == NULL) { sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); From 7de8c2b5eb0fc9c62a008f475efa325f67d4cbdb Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 21:35:44 -0500 Subject: [PATCH 34/34] Fixed: || should be && --- src/p_acs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 143683602..2668fd4a1 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4559,7 +4559,7 @@ bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType * 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))) + 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))))