1515#include < algorithm>
1616#include < cassert>
1717#include < cmath>
18+ #include < cstring>
1819
1920// Disable SGE's internal surface locking once at startup; terrain drawing is
2021// the only remaining consumer of SGE functions and the caller already handles
2122// locking. Was originally called in CGame::Init().
2223static bool sgeLockOff = (sge_Lock_OFF(), true );
2324
25+ static Uint16 tilesetIdxForMapType (MapType mapType, bool want32bit);
26+
2427namespace {
2528const TerrainDesc* getTerrainDesc (const bobMAP& map, Uint8 rawTextureId)
2629{
@@ -623,11 +626,10 @@ bool GetAdjustedPoints(const DisplayRectangle& displayRect, const bobMAP& myMap,
623626}
624627} // namespace
625628
626- void CSurface::GetTerrainTextureCoords (MapType mapType, TriangleTerrainType texture, bool isRSU, int texture_move,
627- Point16& upper, Point16& left, Point16& right, Point16& upper2, Point16& left2,
628- Point16& right2)
629+ void CSurface::GetTerrainTextureCoords (MapType mapType, TriangleTerrainType texture, bool isRSU, Point16& upper,
630+ Point16& left, Point16& right, Point16& upper2, Point16& left2, Point16& right2)
629631{
630- const auto animOffset = Point16 (-texture_move, texture_move);
632+ // Offset-shifting animation replaced by palette cycling animation (UpdatePaletteAnimations)
631633 switch (texture)
632634 {
633635 // in case of USD-Triangle "upper.x" and "upper.y" means "lowerX" and "lowerY"
@@ -657,14 +659,14 @@ void CSurface::GetTerrainTextureCoords(MapType mapType, TriangleTerrainType text
657659 {
658660 if (isRSU)
659661 {
660- upper2 = Point16 (231 , 61 ) + animOffset ;
661- left2 = Point16 (207 , 62 ) + animOffset ;
662- right2 = Point16 (223 , 78 ) + animOffset ;
662+ upper2 = Point16 (231 , 61 );
663+ left2 = Point16 (207 , 62 );
664+ right2 = Point16 (223 , 78 );
663665 } else
664666 {
665- upper2 = Point16 (224 , 79 ) + animOffset ;
666- left2 = Point16 (232 , 62 ) + animOffset ;
667- right2 = Point16 (245 , 76 ) + animOffset ;
667+ upper2 = Point16 (224 , 79 );
668+ left2 = Point16 (232 , 62 );
669+ right2 = Point16 (245 , 76 );
668670 }
669671 }
670672 break ;
@@ -676,14 +678,14 @@ void CSurface::GetTerrainTextureCoords(MapType mapType, TriangleTerrainType text
676678 {
677679 if (isRSU)
678680 {
679- upper2 = Point16 (231 , 61 ) + animOffset ;
680- left2 = Point16 (207 , 62 ) + animOffset ;
681- right2 = Point16 (223 , 78 ) + animOffset ;
681+ upper2 = Point16 (231 , 61 );
682+ left2 = Point16 (207 , 62 );
683+ right2 = Point16 (223 , 78 );
682684 } else
683685 {
684- upper2 = Point16 (224 , 79 ) + animOffset ;
685- left2 = Point16 (232 , 62 ) + animOffset ;
686- right2 = Point16 (245 , 76 ) + animOffset ;
686+ upper2 = Point16 (224 , 79 );
687+ left2 = Point16 (232 , 62 );
688+ right2 = Point16 (245 , 76 );
687689 }
688690 }
689691 break ;
@@ -700,14 +702,14 @@ void CSurface::GetTerrainTextureCoords(MapType mapType, TriangleTerrainType text
700702 case TRIANGLE_TEXTURE_WATER__ :
701703 if (isRSU)
702704 {
703- upper = Point16 (231 , 61 ) + animOffset ;
704- left = Point16 (207 , 62 ) + animOffset ;
705- right = Point16 (223 , 78 ) + animOffset ;
705+ upper = Point16 (231 , 61 );
706+ left = Point16 (207 , 62 );
707+ right = Point16 (223 , 78 );
706708 } else
707709 {
708- upper = Point16 (224 , 79 ) + animOffset ;
709- left = Point16 (232 , 62 ) + animOffset ;
710- right = Point16 (245 , 76 ) + animOffset ;
710+ upper = Point16 (224 , 79 );
711+ left = Point16 (232 , 62 );
712+ right = Point16 (245 , 76 );
711713 }
712714 break ;
713715 case TRIANGLE_TEXTURE_MEADOW1 :
@@ -753,14 +755,14 @@ void CSurface::GetTerrainTextureCoords(MapType mapType, TriangleTerrainType text
753755 case TRIANGLE_TEXTURE_LAVA :
754756 if (isRSU)
755757 {
756- upper = Point16 (231 , 117 ) + animOffset ;
757- left = Point16 (207 , 118 ) + animOffset ;
758- right = Point16 (223 , 134 ) + animOffset ;
758+ upper = Point16 (231 , 117 );
759+ left = Point16 (207 , 118 );
760+ right = Point16 (223 , 134 );
759761 } else
760762 {
761- upper = Point16 (224 , 135 ) + animOffset ;
762- left = Point16 (232 , 118 ) + animOffset ;
763- right = Point16 (245 , 132 ) + animOffset ;
763+ upper = Point16 (224 , 135 );
764+ left = Point16 (232 , 118 );
765+ right = Point16 (245 , 132 );
764766 }
765767 break ;
766768 case TRIANGLE_TEXTURE_MINING_MEADOW :
@@ -793,10 +795,8 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa
793795 // from it's surrounded water, i use this color keys below. These are the color values for the water texture.
794796 // I wrote a special SGE-Function that uses these color keys and ignores them in the Surf_Tileset.
795797 static std::array<Uint32, 5 > colorkeys = {14191 , 14195 , 13167 , 13159 , 11119 };
796- static int texture_move = 0 ;
797798 static int roundCount = 0 ;
798799 static Uint32 roundTimeObjects = SDL_GetTicks ();
799- static Uint32 roundTimeTextures = SDL_GetTicks ();
800800 if (SDL_GetTicks () - roundTimeObjects > 30 )
801801 {
802802 roundTimeObjects = SDL_GetTicks ();
@@ -805,34 +805,10 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa
805805 else
806806 roundCount++;
807807 }
808- if (SDL_GetTicks () - roundTimeTextures > 170 )
809- {
810- roundTimeTextures = SDL_GetTicks ();
811- texture_move++;
812- if (texture_move > 14 )
813- texture_move = 0 ;
814- }
815808
816809 SDL_Surface* Surf_Tileset;
817- switch (type)
818- {
819- case MAP_GREENLAND :
820- default :
821- Surf_Tileset = global::bmpArray[global::s2->getMapObj ()->getBitsPerPixel () == 8 ? TILESET_GREENLAND_8BPP :
822- TILESET_GREENLAND_32BPP ]
823- .surface .get ();
824- break ;
825- case MAP_WASTELAND :
826- Surf_Tileset = global::bmpArray[global::s2->getMapObj ()->getBitsPerPixel () == 8 ? TILESET_WASTELAND_8BPP :
827- TILESET_WASTELAND_32BPP ]
828- .surface .get ();
829- break ;
830- case MAP_WINTERLAND :
831- Surf_Tileset = global::bmpArray[global::s2->getMapObj ()->getBitsPerPixel () == 8 ? TILESET_WINTERLAND_8BPP :
832- TILESET_WINTERLAND_32BPP ]
833- .surface .get ();
834- break ;
835- }
810+ const bool want32 = global::s2->getMapObj ()->getBitsPerPixel () != 8 ;
811+ Surf_Tileset = global::bmpArray[tilesetIdxForMapType (type, want32)].surface .get ();
836812
837813 bool const isRSU = p1.y < p2.y ;
838814
@@ -842,7 +818,7 @@ void CSurface::DrawTriangle(SDL_Surface* display, const DisplayRectangle& displa
842818 Point16 upper, left, right, upper2, left2, right2;
843819 auto const texture =
844820 TriangleTerrainType ((isRSU ? P1 .rsuTexture : P2 .usdTexture ) & ~0x40 ); // Mask out harbor bit
845- GetTerrainTextureCoords (type, texture, isRSU, texture_move, upper, left, right, upper2, left2, right2);
821+ GetTerrainTextureCoords (type, texture, isRSU, upper, left, right, upper2, left2, right2);
846822
847823 // draw the triangle
848824 // do not shade water and lava
@@ -1534,3 +1510,101 @@ float CSurface::absf(float a)
15341510 else
15351511 return a * (-1 );
15361512}
1513+
1514+ // / Map a MapType to the 8-bit or 32-bit tileset bmpArray slot
1515+ static Uint16 tilesetIdxForMapType (MapType mapType, bool want32bit)
1516+ {
1517+ switch (mapType)
1518+ {
1519+ case MAP_GREENLAND :
1520+ default : return want32bit ? TILESET_GREENLAND_32BPP : TILESET_GREENLAND_8BPP ;
1521+ case MAP_WASTELAND : return want32bit ? TILESET_WASTELAND_32BPP : TILESET_WASTELAND_8BPP ;
1522+ case MAP_WINTERLAND : return want32bit ? TILESET_WINTERLAND_32BPP : TILESET_WINTERLAND_8BPP ;
1523+ }
1524+ }
1525+
1526+ // / Apply a delta rotation to a single palette range
1527+ static void rotatePaletteRange (SDL_Palette* pal, uint8_t firstClr, int colorCount, int deltaOffset, bool moveUp)
1528+ {
1529+ std::array<SDL_Color, 256 > rotated;
1530+ memcpy (rotated.data (), pal->colors , sizeof (SDL_Color) * 256 );
1531+ for (int i = 0 ; i < colorCount; i++)
1532+ {
1533+ int srcIdx = moveUp ? (i - deltaOffset + colorCount) % colorCount : (i + deltaOffset) % colorCount;
1534+ rotated[firstClr + i] = pal->colors [firstClr + srcIdx];
1535+ }
1536+ SDL_SetPaletteColors (pal, rotated.data () + firstClr, firstClr, colorCount);
1537+ }
1538+
1539+ void CSurface::UpdatePaletteAnimations (MapType mapType)
1540+ {
1541+ const Uint16 tilesetIdx8 = tilesetIdxForMapType (mapType, false );
1542+ auto animIt = global::paletteAnimations.find (tilesetIdx8);
1543+ if (animIt == global::paletteAnimations.end () || animIt->second .empty ())
1544+ return ;
1545+
1546+ auto & animMap = animIt->second ;
1547+ auto * surf8 = global::bmpArray[tilesetIdx8].surface .get ();
1548+ if (!surf8 || !surf8->format ->palette )
1549+ return ;
1550+
1551+ SDL_Palette* mapPal =
1552+ (global::s2 && global::s2->getMapObj ()) ? global::s2->getMapObj ()->getSurfacePalette () : nullptr ;
1553+
1554+ // 32-bit tileset surface for blitting after palette updates
1555+ const Uint16 tilesetIdx32 = tilesetIdxForMapType (mapType, true );
1556+ auto * surf32 = global::bmpArray[tilesetIdx32].surface .get ();
1557+
1558+ const Uint32 now = SDL_GetTicks ();
1559+ bool anyUpdate = false ;
1560+
1561+ for (auto & [palAnimIdx, anim] : animMap)
1562+ {
1563+ if (!anim.isActive || anim.rate == 0 )
1564+ continue ;
1565+
1566+ const int colorCount = anim.lastClr - anim.firstClr + 1 ;
1567+ if (colorCount <= 1 )
1568+ continue ;
1569+
1570+ // Time per step: (8192/30) / rate seconds, converted to ms
1571+ const float intervalMs = (8192 .0f / 30 .0f ) * 1000 .0f / anim.rate ;
1572+
1573+ const uint32_t elapsed = now - anim.lastUpdateTime ;
1574+ if (elapsed < static_cast <uint32_t >(intervalMs))
1575+ continue ;
1576+
1577+ int steps = static_cast <int >(elapsed / intervalMs);
1578+ anim.lastUpdateTime += static_cast <uint32_t >(steps * intervalMs);
1579+
1580+ int newOffset = anim.moveUp ? (anim.currentOffset + steps) % colorCount :
1581+ (anim.currentOffset - steps + colorCount * steps) % colorCount;
1582+ int deltaOffset = (newOffset - anim.lastAppliedOffset + colorCount) % colorCount;
1583+ if (deltaOffset == 0 )
1584+ continue ;
1585+ anim.lastAppliedOffset = newOffset;
1586+ anim.currentOffset = newOffset;
1587+
1588+ rotatePaletteRange (surf8->format ->palette , anim.firstClr , colorCount, deltaOffset, anim.moveUp );
1589+ if (mapPal)
1590+ rotatePaletteRange (mapPal, anim.firstClr , colorCount, deltaOffset, anim.moveUp );
1591+ anyUpdate = true ;
1592+ }
1593+
1594+ if (!anyUpdate)
1595+ return ;
1596+
1597+ // Blit 8-bit tileset to 32-bit so the rotated palette takes effect in 32bpp mode
1598+ if (surf32 && surf8)
1599+ {
1600+ if (SDL_MUSTLOCK (surf32))
1601+ SDL_LockSurface (surf32);
1602+ if (SDL_MUSTLOCK (surf8))
1603+ SDL_LockSurface (surf8);
1604+ SDL_BlitSurface (surf8, nullptr , surf32, nullptr );
1605+ if (SDL_MUSTLOCK (surf8))
1606+ SDL_UnlockSurface (surf8);
1607+ if (SDL_MUSTLOCK (surf32))
1608+ SDL_UnlockSurface (surf32);
1609+ }
1610+ }
0 commit comments