diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 1d17ba4ab4..5a141e7205 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -337,6 +337,20 @@ Note: All fields default to false unless mentioned otherwise. healthfloorgroup = ; // ID of destructible object to synchronize hitpoints (optional, default is 0) healthceiling = ; // Amount of hitpoints for this sector (includes ceiling and top-outside linedef sides) healthceilinggroup = ; // ID of destructible object to synchronize hitpoints (optional, default is 0) + + xscrollfloor = ; // X map units per frame to scroll the floor. + yscrollfloor = ; // Y map units per frame to scroll the floor. + scrollfloormode = ; // Floor scroll mode bit mask (1 = scroll textures, 2 = carry static objects, 4 = carry players, 8 = carry monsters. + xscrollceiling = ; // X map units per frame to scroll the ceiling. + yscrollceiling = ; // Y map units per frame to scroll the ceiling. + scrollceilingmode = ; // ceiling scroll mode bit mask (1 = scroll textures, 2 = carry static objects, 4 = carry players, 8 = carry monsters. + + scroll_ceil_x = ; // deprecated Eternity based alternatives for the above. + scroll_ceil_y = ; // Due to using unintuitive units of measurement and a more limited feature set they should not be used anymore. + scroll_ceil_type = ; + scroll_floor_x = ; + scroll_floor_y = ; + scroll_floor_type = ; * Note about dropactors diff --git a/src/maploader/maploader.h b/src/maploader/maploader.h index 8390aca041..675311d0ed 100644 --- a/src/maploader/maploader.h +++ b/src/maploader/maploader.h @@ -194,7 +194,7 @@ private: void SpawnSpecials(); void InitSectorSpecial(sector_t *sector, int special); void SpawnLights(sector_t *sector); - void CreateScroller(EScroll type, double dx, double dy, sector_t *affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + void CreateScroller(EScroll type, double dx, double dy, sector_t *affectee, int accel, EScrollPos scrollpos = EScrollPos::scw_all, int scrollmode = 15/*SCROLL_All*/); void SpawnScrollers(); void SpawnFriction(); void SpawnPushers(); diff --git a/src/maploader/specials.cpp b/src/maploader/specials.cpp index 6becec6e9a..8a38f3e7ca 100644 --- a/src/maploader/specials.cpp +++ b/src/maploader/specials.cpp @@ -1466,7 +1466,7 @@ void MapLoader::SpawnScrollers() } -void MapLoader::CreateScroller(EScroll type, double dx, double dy, sector_t *affectee, int accel, EScrollPos scrollpos) +void MapLoader::CreateScroller(EScroll type, double dx, double dy, sector_t *affectee, int accel, EScrollPos scrollpos, int scrollmode) { - Level->CreateThinker(type, dx, dy, nullptr, affectee, nullptr, accel, scrollpos); + Level->CreateThinker(type, dx, dy, nullptr, affectee, nullptr, accel, scrollpos, scrollmode); } diff --git a/src/maploader/udmf.cpp b/src/maploader/udmf.cpp index bb6f43d595..a25bf88fd4 100644 --- a/src/maploader/udmf.cpp +++ b/src/maploader/udmf.cpp @@ -52,6 +52,7 @@ #include "xlat/xlat.h" #include "maploader.h" #include "texturemanager.h" +#include "a_scroll.h" //=========================================================================== // @@ -431,7 +432,7 @@ struct UDMFScroll bool ceiling; int index; double x, y; - FName type; + int scrolltype; }; class UDMFParser : public UDMFParserBase @@ -1589,11 +1590,13 @@ public: // Brand new UDMF scroller properties double scroll_ceil_x = 0; double scroll_ceil_y = 0; - FName scroll_ceil_type = NAME_None; + int scroll_ceil_type = 0; double scroll_floor_x = 0; double scroll_floor_y = 0; - FName scroll_floor_type = NAME_None; + int scroll_floor_type = 0; + + const double scrollfactor = 1 / 3.2; // I hope this is correct, it's just a guess taken from Eternity's code. memset(sec, 0, sizeof(*sec)); sec->Level = Level; @@ -2007,29 +2010,65 @@ public: break; case NAME_scroll_ceil_x: + scroll_ceil_x = CheckFloat(key) * scrollfactor; + break; + + case NAME_xscrollceiling: scroll_ceil_x = CheckFloat(key); break; case NAME_scroll_ceil_y: + scroll_ceil_y = CheckFloat(key) * scrollfactor; + break; + + case NAME_yscrollceiling: scroll_ceil_y = CheckFloat(key); break; - case NAME_scroll_ceil_type: - scroll_ceil_type = CheckString(key); + case NAME_scrollceilingmode: + scroll_ceil_type = CheckInt(key); break; + case NAME_scroll_ceil_type: + { + const char* val = CheckString(key); + if (!stricmp(val, "both")) scroll_ceil_type = SCROLL_All; + else if (!stricmp(val, "visual")) scroll_ceil_type = SCROLL_Textures; + if (!stricmp(val, "physical")) scroll_ceil_type = SCROLL_All & ~SCROLL_Textures; + else scroll_ceil_type = 0; + break; + } + case NAME_scroll_floor_x: + scroll_floor_x = CheckFloat(key) * scrollfactor; + break; + + case NAME_xscrollfloor: scroll_floor_x = CheckFloat(key); break; case NAME_scroll_floor_y: + scroll_floor_y = CheckFloat(key) * scrollfactor; + break; + + case NAME_yscrollfloor: scroll_floor_y = CheckFloat(key); break; - case NAME_scroll_floor_type: - scroll_floor_type = CheckString(key); + case NAME_scrollfloormode: + scroll_floor_type = CheckInt(key); break; + case NAME_scroll_floor_type: + { + const char* val = CheckString(key); + if (!stricmp(val, "both")) scroll_floor_type = SCROLL_All; + else if (!stricmp(val, "visual")) scroll_floor_type = SCROLL_Textures; + if (!stricmp(val, "physical")) scroll_floor_type = SCROLL_All & ~SCROLL_Textures; + else scroll_floor_type = 0; + break; + } + // These two are used by Eternity for something I do not understand. //case NAME_portal_ceil_useglobaltex: //case NAME_portal_floor_useglobaltex: @@ -2470,15 +2509,13 @@ public: // Now create the scrollers. for (auto &scroll : UDMFScrollers) { - const double scrollfactor = 1 / 3.2; // I hope this is correct, it's just a guess taken from Eternity's code. - if (scroll.type == NAME_Both || scroll.type == NAME_Visual) + if (scroll.scrolltype & SCROLL_Textures) { - loader->CreateScroller(scroll.ceiling ? EScroll::sc_ceiling : EScroll::sc_floor, scroll.x * scrollfactor, scroll.y * scrollfactor, &Level->sectors[scroll.index], 0); + loader->CreateScroller(scroll.ceiling ? EScroll::sc_ceiling : EScroll::sc_floor, -scroll.x, scroll.y, &Level->sectors[scroll.index], 0); } - if (scroll.type == NAME_Both || scroll.type == NAME_Physical) + if (scroll.scrolltype & (SCROLL_StaticObjects | SCROLL_Players | SCROLL_Monsters)) { - // sc_carry_ceiling doesn't do anything yet. - loader->CreateScroller(scroll.ceiling ? EScroll::sc_carry_ceiling : EScroll::sc_carry, scroll.x * scrollfactor, scroll.y * scrollfactor, &Level->sectors[scroll.index], 0); + loader->CreateScroller(scroll.ceiling ? EScroll::sc_carry_ceiling : EScroll::sc_carry, scroll.x, scroll.y, &Level->sectors[scroll.index], 0, scw_all, scroll.scrolltype); } } diff --git a/src/playsim/mapthinkers/a_scroll.cpp b/src/playsim/mapthinkers/a_scroll.cpp index e0a561ad84..9ac20ccfb9 100644 --- a/src/playsim/mapthinkers/a_scroll.cpp +++ b/src/playsim/mapthinkers/a_scroll.cpp @@ -105,8 +105,11 @@ void DScroller::Serialize(FSerializer &arc) ("vdx", m_vdx) ("vdy", m_vdy) ("accel", m_Accel) + ("affect", m_Affect) .Enum("parts", m_Parts) .Array("interpolations", m_Interpolations, 3); + + if (arc.isReading() && m_Affect == 0) m_Affect = SCROLL_All; } //----------------------------------------------------------------------------- @@ -218,11 +221,55 @@ void DScroller::Tick () // mark all potentially affected things here so that the very expensive calculation loop in AActor::Tick does not need to run for actors which do not touch a scrolling sector. for (auto n = m_Sector->touching_thinglist; n; n = n->m_snext) { + AActor* actor = n->m_thing; + if (actor->player) + { + if (!(m_Affect & SCROLL_Players)) + continue; + } + else if (actor->flags3 & MF3_ISMONSTER) + { + if (!(m_Affect & SCROLL_Monsters)) + continue; + } + else if (!(m_Affect & SCROLL_StaticObjects)) + continue; + n->m_thing->flags8 |= MF8_INSCROLLSEC; } break; - case EScroll::sc_carry_ceiling: // to be added later + case EScroll::sc_carry_ceiling: + // this just copies DSDA's implementation. Usability is limited. + for (auto n = m_Sector->touching_thinglist; n; n = n->m_snext) + { + AActor* actor = n->m_thing; + + if ( + !(actor->flags & MF_NOCLIP) && + actor->flags & MF_SPAWNCEILING && + actor->flags & MF_NOGRAVITY && + actor->Top() == m_Sector->ceilingplane.ZatPoint(actor->Pos().XY()) + ) + { + + if (actor->player) + { + if (!(m_Affect & SCROLL_Players)) + continue; + } + else if (actor->flags3 & MF3_ISMONSTER) + { + if (!(m_Affect & SCROLL_Monsters)) + continue; + } + else if (!(m_Affect & SCROLL_StaticObjects)) + continue; + + n->m_thing->Vel.X = m_dx; + n->m_thing->Vel.Y = m_dy; + } + } break; } } @@ -247,7 +294,7 @@ void DScroller::Tick () // //----------------------------------------------------------------------------- -void DScroller::Construct (EScroll type, double dx, double dy, sector_t *ctrl, sector_t *sec, side_t *side, int accel, EScrollPos scrollpos) +void DScroller::Construct (EScroll type, double dx, double dy, sector_t *ctrl, sector_t *sec, side_t *side, int accel, EScrollPos scrollpos, int aff) { m_Type = type; m_dx = dx; @@ -256,6 +303,7 @@ void DScroller::Construct (EScroll type, double dx, double dy, sector_t *ctrl, m_Parts = scrollpos; m_vdx = m_vdy = 0; m_LastHeight = 0; + m_Affect = aff; if ((m_Controller = ctrl) != nullptr) { m_LastHeight = m_Controller->CenterFloor() + m_Controller->CenterCeiling(); @@ -344,6 +392,7 @@ void DScroller::Construct(double dx, double dy, const line_t *l, sector_t * cont m_Accel = accel; m_Parts = scrollpos; m_LastHeight = 0; + m_Affect = SCROLL_All; // not really relevant, so use the default. if ((m_Controller = control) != nullptr) { m_LastHeight = m_Controller->CenterFloor() + m_Controller->CenterCeiling(); diff --git a/src/playsim/mapthinkers/a_scroll.h b/src/playsim/mapthinkers/a_scroll.h index 95d985117b..f4d7c37966 100644 --- a/src/playsim/mapthinkers/a_scroll.h +++ b/src/playsim/mapthinkers/a_scroll.h @@ -1,5 +1,14 @@ #pragma once +enum EScrollAffect +{ + SCROLL_Textures = 1, + SCROLL_StaticObjects = 2, + SCROLL_Players = 4, + SCROLL_Monsters = 8, + SCROLL_All = 15 +}; + //----------------------------------------------------------------------------- // // killough 3/7/98: Add generalized scroll effects @@ -13,7 +22,7 @@ class DScroller : public DThinker public: static const int DEFAULT_STAT = STAT_SCROLLER; - void Construct(EScroll type, double dx, double dy, sector_t *control, sector_t *sec, side_t *side, int accel, EScrollPos scrollpos = EScrollPos::scw_all); + void Construct(EScroll type, double dx, double dy, sector_t *control, sector_t *sec, side_t *side, int accel, EScrollPos scrollpos = EScrollPos::scw_all, int aff = SCROLL_All); void Construct(double dx, double dy, const line_t *l, sector_t *control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); void OnDestroy() override; @@ -29,6 +38,7 @@ public: protected: EScroll m_Type; // Type of scroll effect + int m_Affect; double m_dx, m_dy; // (dx,dy) scroll speeds sector_t *m_Sector; // Affected sector side_t *m_Side; // ... or side