Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the map viewer tool provided by Valdis #196

Merged
merged 2 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading