Skip to content

Commit

Permalink
Added the map viewer tool provided by Valdis
Browse files Browse the repository at this point in the history
* Modified the core engine to be able to dump a json file of a format
  which is readable by the tool.
* Modified tool slightly to allow for display of multiple levels
  (manually).
* Removed a bunch of commented out code from the files provided by
  Valdis.
  • Loading branch information
jt-traub committed Jul 1, 2024
1 parent 3072317 commit db6a1b9
Show file tree
Hide file tree
Showing 10 changed files with 3,490 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ players.*
game.*
names.*
orders.*
map_viewer/hexmap.json
map_viewer/.yarn
map_viewer/.pnp.*
map_viewer/.parcel-cache
9 changes: 8 additions & 1 deletion aregion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,14 @@ void ARegion::SetName(char const *c)
name = new AString(c);
}

Production *ARegion::get_production_for_skill(int item, int skill) {
int ARegion::produces_item(int item)
{
if(products.size() == 0) return 0;
auto p = find_if(products.begin(), products.end(), [item](Production *p) { return p->itemtype == item; });
return (p != products.end()) ? (*p)->amount : 0;
}

Production *ARegion::get_production_for_skill(int item, int skill) {
if (products.size() == 0) return nullptr;
auto p = find_if(
products.begin(),
Expand Down
2 changes: 2 additions & 0 deletions aregion.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ class ARegion : public AListElem

// find a production for a certain skill.
Production *get_production_for_skill(int item, int skill);
int produces_item(int item);

// Editing functions
void UpdateEditRegion();
void SetupEditRegion();
Expand Down
35 changes: 35 additions & 0 deletions game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ int Game::ViewMap(const AString & typestr,const AString & mapfile)
if (typestr == "lair") type = 2;
if (typestr == "gate") type = 3;
if (typestr == "cities") type = 4;
if (typestr == "hex") type = 5;

ofstream f(mapfile.const_str(), ios::out|ios::ate);
if (!f.is_open()) return(0);
Expand Down Expand Up @@ -270,6 +271,40 @@ int Game::ViewMap(const AString & typestr,const AString & mapfile)
return(1);
}

if (type == 5) {
json worldmap;

for (auto i = 0; i < regions.numLevels; i++) {
ARegionArray *pArr = regions.pRegionArrays[i];
if (pArr->levelType == ARegionArray::LEVEL_NEXUS) continue;
string label = (pArr->strName ? (pArr->strName->const_str() + to_string(i-1)) : "surface");
worldmap[label] = json::array();

for (int y = 0; y < pArr->y; y++) {
for (int x = 0; x < pArr->x; x++) {
ARegion *reg = pArr->GetRegion(x, y);
if (!reg) continue;
json data = reg->basic_region_data();
json hexout = {
{ "x", x }, { "y", y }, { "z", i },
{ "terrain", data["terrain"] },
{ "yew", reg->produces_item(I_YEW) },
{ "mithril", reg->produces_item(I_MITHRIL) },
{ "admantium", reg->produces_item(I_ADMANTIUM) },
{ "floater", reg->produces_item(I_FLOATER) }

};
if (reg->town) {
hexout["town"] = data["settlement"]["size"];
}
worldmap[label].push_back(hexout);
}
}
}
f << worldmap.dump(2);
return(1);
}

int i;
for (i = 0; i < regions.numLevels; i++) {
f << '\n';
Expand Down
28 changes: 28 additions & 0 deletions map_viewer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Developer Tool for Atlantis Map Visualization

This is a very simple map viewer, initially put together by
[Valdis Zobelia](https://github.com/valdisz). Most, if not all, of the code in the hex.ts file comes from a Hex Map [tutorial](https://www.redblobgames.com/grids/hexagons/) and [implementation](https://www.redblobgames.com/grids/hexagons/implementation.html) written by [Amit Patel](http://www-cs-students.stanford.edu/~amitp/) and [Red Blob Games](https://www.redblobgames.com/)

## To use:
* run `<game> map hex hexmap.json`
* copy the `hexmap.json` file into this directory
* If this is your first time using this tool: run `yarn` to install everything.
* run `yarn start`
* This will spawn a webserver serving the content on port 1234
* Open a web browser to `http://localhost:1234`


## Known issues
* Terrain hexes show up as red.
* Fix: add a color for it to the TERRAIN mapping in `index.ts`
* Maps overlap
* Fix: add some amount to either the MAP_W or MAP_H constants.
* Better: rewrite this code to figure out multiple levels dynamically.
* Level not showing up
* Fix: Add a call to drawHexMap for the correct level with a good column/row
offset.
* Better: Rewrite this code to lay out all levels dynamically.
* The resources don't show up
* Fix: Change the code to colorize based on the resource rather than terrain.
* Better: Right the code to have a way to overlay things and a nice ui.

272 changes: 272 additions & 0 deletions map_viewer/hex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
export class Point
{
constructor (public x:number, public y:number) {}
}

export class Hex
{
constructor (public q:number, public r:number, public s:number) {
if (Math.round(q + r + s) !== 0) throw "q + r + s must be 0";
}

public add(b:Hex):Hex
{
return new Hex(this.q + b.q, this.r + b.r, this.s + b.s);
}


public subtract(b:Hex):Hex
{
return new Hex(this.q - b.q, this.r - b.r, this.s - b.s);
}


public scale(k:number):Hex
{
return new Hex(this.q * k, this.r * k, this.s * k);
}


public rotateLeft():Hex
{
return new Hex(-this.s, -this.q, -this.r);
}


public rotateRight():Hex
{
return new Hex(-this.r, -this.s, -this.q);
}

public static directions:Hex[] = [new Hex(1, 0, -1), new Hex(1, -1, 0), new Hex(0, -1, 1), new Hex(-1, 0, 1), new Hex(-1, 1, 0), new Hex(0, 1, -1)];

public static direction(direction:number):Hex
{
return Hex.directions[direction];
}


public neighbor(direction:number):Hex
{
return this.add(Hex.direction(direction));
}

public static diagonals:Hex[] = [new Hex(2, -1, -1), new Hex(1, -2, 1), new Hex(-1, -1, 2), new Hex(-2, 1, 1), new Hex(-1, 2, -1), new Hex(1, 1, -2)];

public diagonalNeighbor(direction:number):Hex
{
return this.add(Hex.diagonals[direction]);
}


public len():number
{
return (Math.abs(this.q) + Math.abs(this.r) + Math.abs(this.s)) / 2;
}


public distance(b:Hex):number
{
return this.subtract(b).len();
}


public round():Hex
{
var qi:number = Math.round(this.q);
var ri:number = Math.round(this.r);
var si:number = Math.round(this.s);
var q_diff:number = Math.abs(qi - this.q);
var r_diff:number = Math.abs(ri - this.r);
var s_diff:number = Math.abs(si - this.s);
if (q_diff > r_diff && q_diff > s_diff)
{
qi = -ri - si;
}
else
if (r_diff > s_diff)
{
ri = -qi - si;
}
else
{
si = -qi - ri;
}
return new Hex(qi, ri, si);
}


public lerp(b:Hex, t:number):Hex
{
return new Hex(this.q * (1.0 - t) + b.q * t, this.r * (1.0 - t) + b.r * t, this.s * (1.0 - t) + b.s * t);
}


public linedraw(b:Hex):Hex[]
{
var N:number = this.distance(b);
var a_nudge:Hex = new Hex(this.q + 1e-06, this.r + 1e-06, this.s - 2e-06);
var b_nudge:Hex = new Hex(b.q + 1e-06, b.r + 1e-06, b.s - 2e-06);
var results:Hex[] = [];
var step:number = 1.0 / Math.max(N, 1);
for (var i = 0; i <= N; i++)
{
results.push(a_nudge.lerp(b_nudge, step * i).round());
}
return results;
}

}

export class OffsetCoord
{
constructor (public col:number, public row:number) {}
public static EVEN:number = 1;
public static ODD:number = -1;

public static qoffsetFromCube(offset:number, h:Hex):OffsetCoord
{
var col:number = h.q;
var row:number = h.r + (h.q + offset * (h.q & 1)) / 2;
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD)
{
throw "offset must be EVEN (+1) or ODD (-1)";
}
return new OffsetCoord(col, row);
}


public static qoffsetToCube(offset:number, h:OffsetCoord):Hex
{
var q:number = h.col;
var r:number = h.row - (h.col + offset * (h.col & 1)) / 2;
var s:number = -q - r;
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD)
{
throw "offset must be EVEN (+1) or ODD (-1)";
}
return new Hex(q, r, s);
}


public static roffsetFromCube(offset:number, h:Hex):OffsetCoord
{
var col:number = h.q + (h.r + offset * (h.r & 1)) / 2;
var row:number = h.r;
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD)
{
throw "offset must be EVEN (+1) or ODD (-1)";
}
return new OffsetCoord(col, row);
}


public static roffsetToCube(offset:number, h:OffsetCoord):Hex
{
var q:number = h.col - (h.row + offset * (h.row & 1)) / 2;
var r:number = h.row;
var s:number = -q - r;
if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD)
{
throw "offset must be EVEN (+1) or ODD (-1)";
}
return new Hex(q, r, s);
}

}

export class DoubledCoord
{
constructor (public col:number, public row:number) {}

public static qdoubledFromCube(h:Hex):DoubledCoord
{
var col:number = h.q;
var row:number = 2 * h.r + h.q;
return new DoubledCoord(col, row);
}


public qdoubledToCube():Hex
{
var q:number = this.col;
var r:number = (this.row - this.col) / 2;
var s:number = -q - r;
return new Hex(q, r, s);
}


public static rdoubledFromCube(h:Hex):DoubledCoord
{
var col:number = 2 * h.q + h.r;
var row:number = h.r;
return new DoubledCoord(col, row);
}


public rdoubledToCube():Hex
{
var q:number = (this.col - this.row) / 2;
var r:number = this.row;
var s:number = -q - r;
return new Hex(q, r, s);
}

}

export class Orientation
{
constructor (public f0:number, public f1:number, public f2:number, public f3:number, public b0:number, public b1:number, public b2:number, public b3:number, public start_angle:number) {}
}

export class Layout
{
constructor (public orientation:Orientation, public size:Point, public origin:Point) {}
public static pointy:Orientation = new Orientation(Math.sqrt(3.0), Math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, Math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5);
public static flat:Orientation = new Orientation(3.0 / 2.0, 0.0, Math.sqrt(3.0) / 2.0, Math.sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, Math.sqrt(3.0) / 3.0, 0.0);

public hexToPixel(h:Hex):Point
{
var M:Orientation = this.orientation;
var size:Point = this.size;
var origin:Point = this.origin;
var x:number = (M.f0 * h.q + M.f1 * h.r) * size.x;
var y:number = (M.f2 * h.q + M.f3 * h.r) * size.y;
return new Point(x + origin.x, y + origin.y);
}


public pixelToHex(p:Point):Hex
{
var M:Orientation = this.orientation;
var size:Point = this.size;
var origin:Point = this.origin;
var pt:Point = new Point((p.x - origin.x) / size.x, (p.y - origin.y) / size.y);
var q:number = M.b0 * pt.x + M.b1 * pt.y;
var r:number = M.b2 * pt.x + M.b3 * pt.y;
return new Hex(q, r, -q - r);
}


public hexCornerOffset(corner:number):Point
{
var M:Orientation = this.orientation;
var size:Point = this.size;
var angle:number = 2.0 * Math.PI * (M.start_angle - corner) / 6.0;
return new Point(size.x * Math.cos(angle), size.y * Math.sin(angle));
}


public polygonCorners(h:Hex):Point[]
{
var corners:Point[] = [];
var center:Point = this.hexToPixel(h);
for (var i = 0; i < 6; i++)
{
var offset:Point = this.hexCornerOffset(i);
corners.push(new Point(center.x + offset.x, center.y + offset.y));
}
return corners;
}

}
7 changes: 7 additions & 0 deletions map_viewer/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<html>
<head></head>
<body>
<canvas id="map" width="2048" height="2048"></canvas>
<script type="module" src="./index.ts"></script>
</body>
</html>
Loading

0 comments on commit db6a1b9

Please sign in to comment.