1 module vindinium.game_objs;
2 
3 import stdx.data.json;
4 import std.conv;
5 import std.bitmanip;
6 
7 struct Pos {
8     uint x, y;
9 
10     void update_from_json(JSONValue json) {
11         auto pos = json.get!(JSONValue[string]);
12 
13         this.x = pos["x"].get!double.to!uint;
14         this.y = pos["y"].get!double.to!uint;
15     }
16 }
17 
18 struct Hero {
19     int id;
20     string name;
21     string user_id;
22     uint elo;
23     Pos pos;
24     uint life;
25     uint gold;
26     uint mine_count;
27     Pos spawn;
28     bool crashed;
29 
30     void update_from_json(JSONValue json) {
31         auto hero = json.get!(JSONValue[string]);
32 
33         this.id         = hero["id"].get!double.to!int;
34         this.name       = hero["name"].get!string;
35         this.pos.update_from_json(hero["pos"]);
36         this.life       = hero["life"].get!double.to!uint;
37         this.gold       = hero["gold"].get!double.to!uint;
38         this.mine_count = hero["mineCount"].get!double.to!uint;
39         this.spawn.update_from_json(hero["spawnPos"]);
40         this.crashed    = hero["crashed"].get!bool;
41 
42         // might not be in the JSON response, as they're training dummies
43         if("elo" in hero)    this.elo = hero["elo"].get!double.to!uint;
44         if("userId" in hero) this.user_id    = hero["userId"].get!string;
45     }
46 }
47 
48 struct Board {
49     struct Tile {
50         enum Type : ubyte {
51             Empty,
52             Wood,
53             Hero,
54             Tavern,
55             Mine
56         }
57 
58     private:
59         mixin(bitfields!(
60             // type of the item
61             Type, "_type", 3,
62             // is the tavern neutral?
63             bool, "_neutral", 1,
64             // hero ID
65             uint, "_id", 4));
66 
67     public:
68         Type type() { return _type; }
69         bool neutral() {
70             assert(type == Type.Mine);
71             return _neutral;
72         }
73 
74         uint id() {
75             assert(type == Type.Hero || type == Type.Mine);
76             return  _id;
77         }
78 
79         this(Type type, uint id) {
80             assert(type == Type.Hero || type == Type.Mine);
81             this._type = type;
82             this._id   = id;
83             this._neutral = false;
84         }
85 
86         this(Type type) {
87             this._type = type;
88             this._neutral = true;
89         }
90     }
91 
92     // board dimentions
93     uint size;
94 
95     // [x][y]
96     Tile[][] tiles;
97 
98     void update_from_json(JSONValue json) {
99         auto board = json.get!(JSONValue[string]);
100 
101         immutable size = board["size"].get!double.to!uint;
102         auto tiles_str = board["tiles"].get!string;
103 
104         assert((size * size) == (tiles_str.length/2),
105             "Size vs actual tile string length mismatch!");
106 
107         if(this.size != size) {
108             // reallocate tiles
109             this.tiles = new Tile[][](size, size);
110         }
111         this.size = size;
112 
113         uint idx = 0;
114         foreach(y; 0..size) {
115         foreach(x; 0..size) {
116             Tile t;
117             string tile = tiles_str[idx .. idx + 2];
118 
119             if(tile == "  ") {
120                 t = Tile(Tile.Type.Empty);
121             }
122             else if(tile == "##") {
123                 t = Tile(Tile.Type.Wood);
124             }
125             else if(tile[0] == '@') {
126                 t = Tile(Tile.Type.Hero, tile[1 .. 2].to!uint);
127             }
128             else if(tile == "[]") {
129                 t = Tile(Tile.Type.Tavern);
130             }
131             else if(tile[0] == '$') {
132                 if(tile[1] == '-') {
133                     t = Tile(Tile.Type.Mine);
134                 }
135                 else {
136                     t = Tile(Tile.Type.Mine, tile[1 .. 2].to!uint);
137                 }
138             }
139             else {
140                 assert(false, "Invalid tile on board: `" ~ tile ~ "`");
141             }
142 
143             this.tiles[x][y] = t;
144             idx += 2;
145         }
146         }
147     }
148 }
149 
150 struct Game {
151     string id;
152     uint turn;
153     uint max_turns;
154     Hero[] heros;
155     Board board;
156     bool finished;
157 
158     void update_from_json(JSONValue json) {
159         auto game = json.get!(JSONValue[string]);
160 
161         this.id          = game["id"].get!string;
162         this.turn        = game["turn"].get!double.to!uint;
163         this.max_turns   = game["maxTurns"].get!double.to!uint;
164 
165         auto heroes_json = game["heroes"].get!(JSONValue[]);
166         if(!this.heros.length) {
167             this.heros = new Hero[](heroes_json.length);
168         }
169 
170         foreach(i, hero_json; heroes_json) {
171             this.heros[i].update_from_json(hero_json);
172         }
173 
174         this.board.update_from_json(game["board"]);
175         this.finished = game["finished"].get!bool;
176     }
177 }
178 
179 struct GameResponse {
180     Game game; /// initial game state
181     Hero hero; /// the player's hero
182     string token;
183     string view_url;
184     string play_url;
185 
186     void update_from_json(JSONValue json) {
187         auto gr = json.get!(JSONValue[string]);
188 
189         this.game.update_from_json(gr["game"]);
190         this.hero.update_from_json(gr["hero"]);
191         this.token    = gr["token"].get!string;
192         this.view_url = gr["viewUrl"].get!string;
193         this.play_url = gr["playUrl"].get!string;
194     }
195 }