Skip to content

Commit 8501b0f

Browse files
Adopt s25client coordinate system
1 parent e94fd30 commit 8501b0f

5 files changed

Lines changed: 192 additions & 29 deletions

File tree

CIO/CFile.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,10 +570,15 @@ bobMAP* CFile::read_wld(FILE* fp)
570570
// go to texture information for UpSideDown-Triangles
571571
fseek(fp, 16, SEEK_CUR);
572572

573+
// Read usdTexture: file → memory (s25client convention).
574+
// memory(i, j) = file(i - !(j&1), j)
573575
for(int j = 0; j < myMap->height; j++)
574576
{
575577
for(int i = 0; i < myMap->width; i++)
576-
CHECK_READ(libendian::read(&myMap->getVertex(i, j).usdTexture, 1, fp));
578+
{
579+
const int dest_i = (j % 2 == 0) ? ((i + 1) % myMap->width) : i;
580+
CHECK_READ(libendian::read(&myMap->getVertex(dest_i, j).usdTexture, 1, fp));
581+
}
577582
}
578583

579584
// go to road data
@@ -833,10 +838,14 @@ bool CFile::save_wld(FILE* fp, void* data)
833838
// go to texture information for UpSideDown-Triangles
834839
libendian::write(map_data_header, fp);
835840

841+
// Write usdTexture: file(i, j) = memory(i + !(j&1), j)
836842
for(int j = 0; j < myMap->height; j++)
837843
{
838844
for(int i = 0; i < myMap->width; i++)
839-
libendian::write(&myMap->getVertex(i, j).usdTexture, 1, fp);
845+
{
846+
const int src_i = (j % 2 == 0) ? ((i + 1) % myMap->width) : i;
847+
libendian::write(&myMap->getVertex(src_i, j).usdTexture, 1, fp);
848+
}
840849
}
841850

842851
// go to road data

CMap.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "CIO/CFile.h"
99
#include "CIO/CFont.h"
1010
#include "CSurface.h"
11+
#include "Geometry.h"
1112
#include "callbacks.h"
1213
#include "globals.h"
1314
#include "gameData/LandscapeDesc.h"
@@ -264,11 +265,11 @@ std::unique_ptr<bobMAP> CMap::generateMap(int width, int height, MapType type, T
264265
if((j < border || myMap->height - j <= border) || (i < border || myMap->width - i <= border))
265266
{
266267
curVertex.rsuTexture = border_texture;
267-
curVertex.usdTexture = border_texture;
268+
myMap->getVertex(clientUsdVertexPos(i, j)).usdTexture = border_texture;
268269
} else
269270
{
270271
curVertex.rsuTexture = texture;
271-
curVertex.usdTexture = texture;
272+
myMap->getVertex(clientUsdVertexPos(i, j)).usdTexture = texture;
272273
}
273274

274275
// initialize all other blocks -- outcommented blocks are recalculated at map load
@@ -1862,13 +1863,13 @@ void CMap::modifyTexture(Position pos, bool rsu, bool usd)
18621863
if(rsu)
18631864
map->getVertex(pos.x, pos.y).rsuTexture = newContent;
18641865
if(usd)
1865-
map->getVertex(pos.x, pos.y).usdTexture = newContent;
1866+
map->getVertex(clientUsdVertexPos(pos)).usdTexture = newContent;
18661867
} else
18671868
{
18681869
if(rsu)
18691870
map->getVertex(pos.x, pos.y).rsuTexture = modeContent;
18701871
if(usd)
1871-
map->getVertex(pos.x, pos.y).usdTexture = modeContent;
1872+
map->getVertex(clientUsdVertexPos(pos)).usdTexture = modeContent;
18721873
}
18731874

18741875
// at least setup the possible building and the resources at the vertex and 1 section/2 sections around
@@ -2088,7 +2089,7 @@ void CMap::modifyBuild(Position pos)
20882089
const Uint8 height = curVertex.h;
20892090
std::array<const MapNode*, 7> mapVertices;
20902091
for(unsigned i = 0; i < mapVertices.size(); i++)
2091-
mapVertices[i] = &map->getVertex(tempVertices[i]);
2092+
mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y));
20922093

20932094
// calculate the building using the height of the vertices
20942095
// this building is a mine
@@ -2355,7 +2356,7 @@ void CMap::modifyResource(Position pos)
23552356
MapNode& curVertex = map->getVertex(pos.x, pos.y);
23562357
std::array<const MapNode*, 7> mapVertices;
23572358
for(unsigned i = 0; i < mapVertices.size(); i++)
2358-
mapVertices[i] = &map->getVertex(tempVertices[i]);
2359+
mapVertices[i] = &map->getVertex(clientUsdVertexPos(tempVertices[i].x, tempVertices[i].y));
23592360

23602361
// SPECIAL CASE: test if we should set water only
23612362
// test if vertex is surrounded by meadow and meadow-like textures

CSurface.cpp

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "CSurface.h"
77
#include "CGame.h"
88
#include "CMap.h"
9+
#include "Geometry.h"
910
#include "Rect.h"
1011
#include "SGE/sge_blib.h"
1112
#include "SGE/sge_rotation.h"
@@ -333,47 +334,67 @@ void CSurface::DrawTriangleField(SDL_Surface* display, const DisplayRectangle& d
333334
{
334335
if(y % 2 == 0)
335336
{
336-
// first RightSideUp
337+
// first RightSideUp (edge wrap)
337338
tempP2 = myMap.getVertex(width - 1, y + 1);
338339
tempP2.x = 0;
339340
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(0, y), tempP2,
340341
myMap.getVertex(0, y + 1));
341-
for(unsigned x = std::max(col_start, 1); x < width && x <= static_cast<unsigned>(col_end); x++)
342+
// first UpSideDown (s25client convention, visual column 0)
343+
const auto usd0 = clientUsdTriangleVertices(0, y);
344+
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd0.p1x, usd0.p1y),
345+
myMap.getVertex(usd0.p2x, usd0.p2y), myMap.getVertex(usd0.p3x, usd0.p3y));
346+
for(unsigned x = std::max(col_start, 1); x < width - 1u && x <= static_cast<unsigned>(col_end); x++)
342347
{
343-
// RightSideUp
348+
// RightSideUp (same in both conventions)
344349
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y),
345350
myMap.getVertex(x - 1, y + 1), myMap.getVertex(x, y + 1));
346-
// UpSideDown
347-
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x - 1, y + 1),
348-
myMap.getVertex(x - 1, y), myMap.getVertex(x, y));
351+
// UpSideDown (s25client convention)
352+
const auto usd = clientUsdTriangleVertices(x, y);
353+
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd.p1x, usd.p1y),
354+
myMap.getVertex(usd.p2x, usd.p2y), myMap.getVertex(usd.p3x, usd.p3y));
349355
}
350-
// last UpSideDown
351-
tempP3 = myMap.getVertex(0, y);
352-
tempP3.x = myMap.getVertex(width - 1, y).x + triangleWidth;
353-
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(width - 1, y + 1),
354-
myMap.getVertex(width - 1, y), tempP3);
356+
// last RightSideUp
357+
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(static_cast<int>(width) - 1, y),
358+
myMap.getVertex(static_cast<int>(width) - 2, static_cast<int>(y) + 1),
359+
myMap.getVertex(static_cast<int>(width) - 1, static_cast<int>(y) + 1));
360+
// last UpSideDown (wrap edge, s25client convention)
361+
// Visual column width-1, source vertex = width-1, P3 wraps to 0
362+
const int sx = clientUsdX(static_cast<int>(width) - 1, static_cast<int>(y));
363+
const int sx_p3 = (sx + 1 >= static_cast<int>(width)) ? sx + 1 - static_cast<int>(width) : sx + 1;
364+
MapNode tP3 = myMap.getVertex(sx_p3, y);
365+
tP3.x = myMap.getVertex(sx, y).x + triangleWidth;
366+
DrawTriangle(display, displayRect, myMap, type,
367+
myMap.getVertex(sx, y + 1), // P1 = bottom-left (even y: y&1=0)
368+
myMap.getVertex(sx, y), // P2 = top
369+
tP3); // P3 = bottom-right, wrapped to (0,y)
355370
} else
356371
{
357372
for(unsigned x = col_start; x < width - 1u && x <= static_cast<unsigned>(col_end); x++)
358373
{
359-
// RightSideUp
374+
// RightSideUp (same in both conventions)
360375
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x, y),
361376
myMap.getVertex(x, y + 1), myMap.getVertex(x + 1, y + 1));
362-
// UpSideDown
363-
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(x + 1, y + 1),
364-
myMap.getVertex(x, y), myMap.getVertex(x + 1, y));
377+
// UpSideDown (s25client convention)
378+
const auto usd = clientUsdTriangleVertices(x, y);
379+
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(usd.p1x, usd.p1y),
380+
myMap.getVertex(usd.p2x, usd.p2y), myMap.getVertex(usd.p3x, usd.p3y));
365381
}
366382
// last RightSideUp
367383
tempP3 = myMap.getVertex(0, y + 1);
368384
tempP3.x = myMap.getVertex(width - 1, y + 1).x + triangleWidth;
369385
DrawTriangle(display, displayRect, myMap, type, myMap.getVertex(width - 1, y),
370386
myMap.getVertex(width - 1, y + 1), tempP3);
371-
// last UpSideDown
372-
tempP1 = myMap.getVertex(0, y + 1);
373-
tempP1.x = myMap.getVertex(width - 1, y + 1).x + triangleWidth;
374-
tempP3 = myMap.getVertex(0, y);
375-
tempP3.x = myMap.getVertex(width - 1, y).x + triangleWidth;
376-
DrawTriangle(display, displayRect, myMap, type, tempP1, myMap.getVertex(width - 1, y), tempP3);
387+
// last UpSideDown (wrap edge, s25client convention)
388+
const int sx = clientUsdX(static_cast<int>(width) - 1, static_cast<int>(y));
389+
const int sx1_w = (sx + 1 >= static_cast<int>(width)) ? sx + 1 - static_cast<int>(width) : sx + 1;
390+
MapNode tP1 = myMap.getVertex(sx1_w, y + 1);
391+
tP1.x = myMap.getVertex(sx, y + 1).x + triangleWidth;
392+
MapNode tP3 = myMap.getVertex(sx1_w, y);
393+
tP3.x = myMap.getVertex(sx, y).x + triangleWidth;
394+
DrawTriangle(display, displayRect, myMap, type,
395+
tP1, // P1 = bottom-left (odd y: y&1=1)
396+
myMap.getVertex(sx, y), // P2 = top
397+
tP3); // P3 = bottom-right, wrapped
377398
}
378399
}
379400

GEOMETRY.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!--
2+
Copyright (C) 2009 - 2025 Settlers Freaks <sf-team at siedler25.org>
3+
4+
SPDX-License-Identifier: GPL-3.0-or-later
5+
-->
6+
7+
# Map geometry alignment with s25client
8+
9+
The editor and s25client both use a staggered hexagonal grid. RSU triangles are
10+
identical, but the **USD triangle label convention differs on even rows**.
11+
12+
## The mapping
13+
14+
Same physical triangle, different visual label:
15+
16+
```
17+
s25client USD(x, y) ≡ editor USD(x + !(y & 1), y)
18+
editor USD(x, y) ≡ s25client USD(x - !(y & 1), y)
19+
```
20+
21+
Odd rows (`y & 1 == 1`) are identical.
22+
23+
## In-memory convention: s25client
24+
25+
bobMAP now uses s25client convention throughout:
26+
`vertex(x,y).usdTexture` ≡ texture for visual USD(x, y) (s25client naming).
27+
28+
| Component | Change |
29+
|-----------|--------|
30+
| **Rendering** (`CSurface.cpp`) | `DrawTriangleField` uses `clientUsdTriangleVertices(x, y)` — reads `vertex(x,y).usdTexture` for visual USD(x,y) |
31+
| **File load** (`CIO/CFile.cpp`) | Read each file byte at (i,j); for even rows store at `memory(i+1, j)`, for odd at `memory(i, j)`. Formula: `memory(i, j) = file(i - !(j&1), j)` |
32+
| **File save** (`CIO/CFile.cpp`) | For even rows read from `memory(i+1, j)` and write sequentially; for odd read from `memory(i, j)`. Formula: `file(i, j) = memory(i - !(j&1), j)` |
33+
| **Map generation** (`CMap.cpp`) | Already correct — border check is vertex-position-based, matching s25client's identity mapping |
34+
35+
## Wrappers (`include/Geometry.h`)
36+
37+
| Function | Returns | Use |
38+
|----------|---------|-----|
39+
| `clientUsdX(x, y)` | `x` | Native — matches in-memory s25client storage |
40+
| `editorUsdX(x, y)` | `x - !(y & 1)` | Backward compat for non-updated code |
41+
| `clientUsdTriangleVertices(x, y)` | USD vertex coords | Native — s25client label → vertices |
42+
| `editorUsdTriangleVertices(x, y)` | USD vertex coords | Backward compat — editor label → vertices |

include/Geometry.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (C) 2009 - 2025 Settlers Freaks <sf-team at siedler25.org>
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include "defines.h"
8+
9+
/// ---------------------------------------------------------------------------
10+
/// Two USD triangle coordinate conventions.
11+
///
12+
/// s25client (new, "client"):
13+
/// visual USD(x, y) ≡ vertex (x, y).usdTexture
14+
///
15+
/// s25edit (old, "editor"):
16+
/// visual USD(x, y) ≡ vertex (x - !(y & 1), y).usdTexture
17+
///
18+
/// RSU triangles are identical in both.
19+
///
20+
/// During the transition:
21+
/// - Updated call sites use the client* coordinate wrappers or access
22+
/// .usdTexture directly (identity convention).
23+
/// - Non-updated call sites use the editor* wrappers.
24+
/// - When all call sites are migrated, the editor* wrappers can be deleted.
25+
/// ---------------------------------------------------------------------------
26+
27+
// ---------------------------------------------------------------------------
28+
// Per-convention USD source-vertex mapping
29+
// ---------------------------------------------------------------------------
30+
31+
/// s25client convention: USD(x,y) reads vertex (x, y).usdTexture
32+
inline int clientUsdX(int triX, int /*triY*/)
33+
{
34+
return triX;
35+
}
36+
37+
/// Source vertex position for a visual USD position (s25client convention).
38+
inline Position clientUsdVertexPos(Position visualPos)
39+
{
40+
return {clientUsdX(visualPos.x, visualPos.y), visualPos.y};
41+
}
42+
43+
/// Source vertex position for a visual USD position given as (x,y).
44+
inline Position clientUsdVertexPos(int x, int y)
45+
{
46+
return {clientUsdX(x, y), y};
47+
}
48+
49+
/// Editor convention: USD(x,y) reads vertex (x - !(y&1), y).usdTexture
50+
inline int editorUsdX(int triX, int triY)
51+
{
52+
return triX - !(triY & 1);
53+
}
54+
55+
// ---------------------------------------------------------------------------
56+
// Per-convention USD triangle vertex sets
57+
// ---------------------------------------------------------------------------
58+
59+
/// Three vertices of a USD triangle in DrawTriangle parameter order:
60+
/// P1 = bottom-left, P2 = top (reads usdTexture), P3 = bottom-right.
61+
struct UsdVertices
62+
{
63+
int p1x, p1y;
64+
int p2x, p2y;
65+
int p3x, p3y;
66+
};
67+
68+
/// USD triangle vertices for s25client convention (identity mapping).
69+
inline UsdVertices clientUsdTriangleVertices(int triX, int triY)
70+
{
71+
// sx = triX
72+
return {triX + (triY & 1),
73+
triY + 1, // P1 = bottom-left
74+
triX,
75+
triY, // P2 = top
76+
triX + 1,
77+
triY}; // P3 = bottom-right
78+
}
79+
80+
/// USD triangle vertices for editor convention (backward compat).
81+
inline UsdVertices editorUsdTriangleVertices(int triX, int triY)
82+
{
83+
int sx = editorUsdX(triX, triY); // sx = triX - !(triY&1)
84+
return {sx + (triY & 1),
85+
triY + 1, // P1 = bottom-left
86+
sx,
87+
triY, // P2 = top
88+
sx + 1,
89+
triY}; // P3 = bottom-right
90+
}

0 commit comments

Comments
 (0)