@@ -16,15 +16,23 @@ public static class MapZIndex {
1616 public static readonly int FogOfWar = 100 ;
1717 }
1818
19+ public enum HorizontalWrapState {
20+ Left , // beyond left edge of the map is visible
21+ Right , // beyond right edge of the map is visible
22+ None , // camera is entirely over the map
23+ }
24+
1925 public partial class MapView : Node2D {
2026 private string [ , ] terrain ;
2127 private TileMap terrainTilemap ;
22- private TileMap terrainTilemapShadow ;
28+ private TileMap wrappingTerrainTilemap ;
2329 private TileSet terrainTileset ;
2430 private TileMap tilemap ;
31+ private TileMap wrappingTilemap ;
2532 private TileSet tileset ;
26- private Vector2I tileSize = new Vector2I ( 128 , 64 ) ;
33+ public Vector2I tileSize { get ; private set ; } = new Vector2I ( 128 , 64 ) ;
2734 private ILogger log = LogManager . ForContext < MapView > ( ) ;
35+ public bool wrapHorizontally { get ; private set ; }
2836 public int worldEdgeRight { get ; private set ; }
2937 public int worldEdgeLeft { get ; private set ; }
3038 private int width ;
@@ -40,12 +48,13 @@ private void setShowGrid(bool value) {
4048 public void toggleGrid ( ) {
4149 setShowGrid ( ! showGrid ) ;
4250 }
51+ public int pixelWidth { get ; private set ; }
4352 private Game game ;
4453 private GameData data ;
4554 private GameMap gameMap ;
4655
4756 private Dictionary < MapUnit , UnitSprite > unitSprites = new Dictionary < MapUnit , UnitSprite > ( ) ;
48- private Dictionary < City , CityScene > cityScenes = new Dictionary < City , CityScene > ( ) ;
57+ private Dictionary < Tile , CityScene > cityScenes = new Dictionary < Tile , CityScene > ( ) ;
4958 private CursorSprite cursor ;
5059
5160 private UnitSprite spriteFor ( MapUnit unit ) {
@@ -69,10 +78,18 @@ public void addCity(City city, Tile tile) {
6978 CityScene scene = new CityScene ( city , tile ) ;
7079 scene . Position = tilemap . MapToLocal ( stackedCoords ( tile ) ) ;
7180 AddChild ( scene ) ;
72- cityScenes . Add ( city , scene ) ;
81+ cityScenes . Add ( tile , scene ) ;
82+ }
83+
84+ private Vector2I horizontalWrapOffset ( HorizontalWrapState wrap ) {
85+ return wrap switch {
86+ HorizontalWrapState . Left => Vector2I . Left ,
87+ HorizontalWrapState . Right => Vector2I . Right ,
88+ _ => Vector2I . Zero ,
89+ } * pixelWidth ;
7390 }
7491
75- private void animateUnit ( Tile tile , MapUnit unit ) {
92+ private void animateUnit ( Tile tile , MapUnit unit , HorizontalWrapState wrap ) {
7693 // TODO: simplify AnimationManager and drawing animations it is unnecessarily complex
7794 // - also investigate if the custom offset tracking and SetFrame can be replaced by
7895 // engine functionality
@@ -91,8 +108,13 @@ private void animateUnit(Tile tile, MapUnit unit) {
91108 sprite . SetFrame ( frame ) ;
92109 sprite . Show ( ) ;
93110
111+ Vector2 wrapOffset = horizontalWrapOffset ( wrap ) ;
112+ sprite . Translate ( wrapOffset ) ;
113+
94114 if ( unit == game . CurrentlySelectedUnit ) {
95- cursor . Position = position ;
115+ // TODO: just noticed cursor position maybe should not be
116+ // on sprite position which has a potential offset?
117+ cursor . Position = position + wrapOffset ;
96118 cursor . Show ( ) ;
97119 }
98120 }
@@ -118,15 +140,20 @@ private MapUnit selectUnitToDisplay(List<MapUnit> units) {
118140 return selected ?? interesting ?? bestDefender ;
119141 }
120142
121- public List < Tile > getVisibleTiles ( ) {
122- List < Tile > tiles = new List < Tile > ( ) ;
143+ public List < ( Tile , HorizontalWrapState ) > getVisibleTiles ( ) {
144+ List < ( Tile , HorizontalWrapState ) > tiles = new List < ( Tile , HorizontalWrapState ) > ( ) ;
123145 Rect2 bounds = game . camera . getVisibleWorld ( ) ;
124146 Vector2I topLeft = tilemap . LocalToMap ( ToLocal ( bounds . Position ) ) ;
125147 Vector2I bottomRight = tilemap . LocalToMap ( ToLocal ( bounds . End ) ) ;
126148 for ( int x = topLeft . X - 1 ; x < bottomRight . X + 1 ; x ++ ) {
127149 for ( int y = topLeft . Y - 1 ; y < bottomRight . Y + 1 ; y ++ ) {
128150 ( int usX , int usY ) = unstackedCoords ( new Vector2I ( x , y ) ) ;
129- tiles . Add ( data . map . tileAt ( usX , usY ) ) ;
151+ HorizontalWrapState wrap = x switch {
152+ _ when x < 0 => HorizontalWrapState . Left ,
153+ _ when x >= width => HorizontalWrapState . Right ,
154+ _ => HorizontalWrapState . None ,
155+ } ;
156+ tiles . Add ( ( data . map . tileAt ( usX , usY ) , wrap ) ) ;
130157 }
131158 }
132159 return tiles ;
@@ -137,44 +164,65 @@ public void updateAnimations() {
137164 s . Hide ( ) ;
138165 }
139166 cursor . Hide ( ) ;
140- foreach ( Tile tile in getVisibleTiles ( ) ) {
167+ foreach ( ( Tile tile , HorizontalWrapState wrap ) in getVisibleTiles ( ) ) {
141168 MapUnit unit = selectUnitToDisplay ( tile . unitsOnTile ) ;
142169 if ( unit != MapUnit . NONE ) {
143- animateUnit ( tile , unit ) ;
170+ animateUnit ( tile , unit , wrap ) ;
171+ }
172+ if ( cityScenes . ContainsKey ( tile ) ) {
173+ CityScene scene = cityScenes [ tile ] ;
174+ Vector2 position = tilemap . MapToLocal ( stackedCoords ( tile ) ) + horizontalWrapOffset ( wrap ) ;
175+ scene . Position = position ;
144176 }
145177 }
146178 }
147179
180+ public void setHorizontalWrap ( HorizontalWrapState state ) {
181+ if ( state != HorizontalWrapState . None ) {
182+ Vector2I offset = horizontalWrapOffset ( state ) ;
183+ wrappingTerrainTilemap . Position = terrainTilemap . Position + offset ;
184+ wrappingTilemap . Position = tilemap . Position + offset ;
185+ }
186+ }
187+
148188 private void initializeTileMap ( ) {
149189 terrainTilemap = new TileMap ( ) ;
150- terrainTilemapShadow = new TileMap ( ) ;
190+ wrappingTerrainTilemap = new TileMap ( ) ;
151191 terrainTileset = Civ3TerrainTileSet . Generate ( ) ;
152192 terrainTilemap . TileSet = terrainTileset ;
153193 terrainTilemap . Position += Vector2I . Right * ( tileSize . X / 2 ) ;
154- terrainTilemapShadow . TileSet = terrainTileset ;
155- terrainTilemapShadow . Position = terrainTilemap . Position + ( Vector2I . Left * tileSize . X * width ) ;
194+ wrappingTerrainTilemap . TileSet = terrainTileset ;
156195
157196 tilemap = new TileMap { YSortEnabled = true } ;
197+ wrappingTilemap = new TileMap { YSortEnabled = true } ;
198+
158199 tileset = TileSetLoader . LoadCiv3TileSet ( ) ;
159200 tilemap . TileSet = tileset ;
201+ wrappingTilemap . TileSet = tileset ;
160202
161203 // create tilemap layers
162204 foreach ( Layer layer in Enum . GetValues ( typeof ( Layer ) ) ) {
163205 if ( layer != Layer . Invalid ) {
164206 tilemap . AddLayer ( layer . Index ( ) ) ;
165- tilemap . SetLayerYSortEnabled ( layer . Index ( ) , true ) ;
207+ wrappingTilemap . AddLayer ( layer . Index ( ) ) ;
166208 if ( layer != Layer . FogOfWar ) {
167209 tilemap . SetLayerYSortEnabled ( layer . Index ( ) , true ) ;
210+ wrappingTilemap . SetLayerYSortEnabled ( layer . Index ( ) , true ) ;
168211 } else {
169212 tilemap . SetLayerZIndex ( layer . Index ( ) , MapZIndex . FogOfWar ) ;
213+ wrappingTilemap . SetLayerZIndex ( layer . Index ( ) , MapZIndex . FogOfWar ) ;
170214 }
171215 }
172216 }
173217
174- tilemap . ZIndex = MapZIndex . Tiles ;
218+ setHorizontalWrap ( HorizontalWrapState . Right ) ; // just put it somewhere
219+
220+ tilemap . ZIndex = 10 ; // need to figure out a good way to order z indices
221+ wrappingTilemap . ZIndex = 10 ;
175222 AddChild ( tilemap ) ;
223+ AddChild ( wrappingTilemap ) ;
176224 AddChild ( terrainTilemap ) ;
177- // AddChild(terrainTilemapShadow );
225+ AddChild ( wrappingTerrainTilemap ) ;
178226 }
179227
180228 private void setTerrainTiles ( ) {
@@ -211,7 +259,7 @@ void lookupAndSetTerrainTile(int x, int y, int cellX, int cellY) {
211259
212260 void setTerrainTile ( Vector2I cell , int atlas , Vector2I texCoords ) {
213261 terrainTilemap . SetCell ( 0 , cell , atlas , texCoords ) ;
214- terrainTilemapShadow . SetCell ( 0 , cell , atlas , texCoords ) ;
262+ wrappingTerrainTilemap . SetCell ( 0 , cell , atlas , texCoords ) ;
215263 }
216264
217265 private Vector2I stackedCoords ( Tile tile ) {
@@ -235,8 +283,10 @@ public MapView(Game game, GameData data) {
235283 cursor = new CursorSprite ( ) ;
236284 AddChild ( cursor ) ;
237285 width = gameMap . numTilesWide / 2 ;
286+ pixelWidth = width * tileSize . X ;
238287 height = gameMap . numTilesTall ;
239288 initializeTileMap ( ) ;
289+ wrapHorizontally = gameMap . wrapHorizontally ;
240290 terrain = new string [ width , height ] ;
241291 worldEdgeRight = ( int ) ToGlobal ( tilemap . MapToLocal ( new Vector2I ( width - 1 , 1 ) ) ) . X + tileSize . X / 2 ;
242292 worldEdgeLeft = ( int ) ToGlobal ( tilemap . MapToLocal ( new Vector2I ( 0 , 0 ) ) ) . X - tileSize . X / 2 ;
@@ -276,10 +326,12 @@ private void setCell(Layer layer, Atlas atlas, Tile tile, Vector2I atlasCoords)
276326 log . Warning ( $ "atlas id { atlas } does not have tile at { atlasCoords } ") ;
277327 }
278328 tilemap . SetCell ( layer . Index ( ) , stackedCoords ( tile ) , atlas . Index ( ) , atlasCoords ) ;
329+ wrappingTilemap . SetCell ( layer . Index ( ) , stackedCoords ( tile ) , atlas . Index ( ) , atlasCoords ) ;
279330 }
280331
281332 private void eraseCell ( Layer layer , Tile tile ) {
282333 tilemap . EraseCell ( layer . Index ( ) , stackedCoords ( tile ) ) ;
334+ wrappingTilemap . EraseCell ( layer . Index ( ) , stackedCoords ( tile ) ) ;
283335 }
284336
285337 private void updateRoadLayer ( Tile tile , bool center ) {
0 commit comments