1 module gamemap;
2 
3 import nudsfml.graphics;
4 
5 import std.algorithm;
6 import std.conv;
7 import std.string;
8 import std.format;
9 
10 import gameentity;
11 
12 enum TileTypes {
13     None = -1,
14     Floor = 0,
15     Wall = 1,
16     WallShadow = 2,
17 }
18 
19 struct Tile {
20     int id;
21     int type = TileTypes.None;
22     Vector2f offset;
23     Vector2f pos;
24 }
25 
26 class GameMap {
27     Tile [] tiles;
28     IntRect [] tileRects;
29 
30     int width; 
31     int height;
32     int gridWidth;
33     int gridHeight;
34     int tileWidth;
35     int tileHeight;
36 
37     float frameChange =  1f / 15f;
38     float currentFrame = 0f;
39     int currentFrameIndex = 0;
40     int currentMaxFrameIndex = 30;
41 
42     Entity [] entities;
43 
44     Texture tex;
45 
46     AppleEntity addApple(int x, int y) {
47         AppleEntity apple = new AppleEntity(tex);
48         apple.mapLocation = Vector2i(x, y);
49         apple.position = Vector2f(x*gridWidth, y*gridHeight);
50         entities ~= apple;
51         sortEntities();
52         return apple;
53     }
54 
55     void addSnake(ref SnakeEntity snakepart){
56         entities ~= snakepart;
57         sortEntities();
58     }
59 
60     void sortEntities() {
61         entities.sort!((a,b) => a.position.y < b.position.y);
62     }
63 
64     void  update(float deltaTime){
65         foreach(ref entity; entities) {
66             entity.update(deltaTime);
67         }
68         currentFrame += deltaTime;;
69         if(currentFrame > frameChange){
70             currentFrame = 0f;
71             currentFrameIndex++;
72             if(currentFrameIndex > currentMaxFrameIndex){
73                 currentFrameIndex = 0;
74             }
75 
76         }
77     }
78 
79 
80 
81     enum Direction {
82         Up = 1,
83         Right = 2,
84         Down = 4,
85         Left = 8
86     }
87 
88 
89 
90     this(){
91         width = 32;
92         height = 24;
93         gridWidth = 32;
94         gridHeight = 32;
95         tileWidth = 32;
96         tileHeight = 48;
97 
98         tiles.length = (width * height *2);
99 
100         for (int y = 0; y < 4; y++){
101             for (int x = 0; x < 16; x++){
102                 tileRects ~= IntRect(x * tileWidth, y * tileHeight, tileWidth, tileHeight);
103             }
104         }
105 
106         tex = new Texture;
107         tex.loadFromFile("data/snaketiles.png");
108 
109         createMap();
110     }
111 
112     int getIndex(int x, int y, int layer = 0){
113         int index = (layer * width * height) + (y * width) + x;
114 
115         if ( index < 0 || index > (width * height * 2)){
116             return -1;
117         }
118         return index;
119     }
120 
121     void createMap(){
122         for(int i = 0; i < width; i++){
123             for(int j = 0; j < height; j++){
124                 if(i == 0 || i == width - 1 || j == 0 || j == height - 1){
125                     tiles[i + j * width].type = TileTypes.Wall;
126                 }
127                 else{
128                     tiles[i + j * width].type = TileTypes.Floor;
129                 }
130                 getTile(i,j).pos = Vector2f(i * gridWidth,j * gridHeight);
131                 getTile(i, j).offset = Vector2f(0,-16);
132             }
133         }
134 
135         calcWallIndexes();
136         calcWallShadows();
137 
138 
139     }
140 
141     void calcWallShadows(){
142         //calc shadows 
143         for(int y = 1 ; y < height; y++){
144             for(int x = 0; x < width; x++){
145                 int index = getIndex(x,y - 1);
146                 if(tiles[index].type == TileTypes.Wall && tiles[getIndex(x,y)].type == TileTypes.Floor){
147                     tiles[getIndex(x,y,1)].id = 16*3+1; // using magic numbers
148                     tiles[getIndex(x,y,1)].type = TileTypes.WallShadow;
149                 } else {
150                     tiles[getIndex(x,y,1)].type = TileTypes.None;
151                     tiles[getIndex(x,y,1)].offset =  Vector2f(0,-16);
152                 }
153             }
154         }
155     }
156 
157 
158 
159     void calcWallIndexes(){
160         //convert forloop to a function to make it easier to read
161         for(int y = 0 ; y < height; y++){
162             for(int x = 0; x < width; x++){
163                 int index = getIndex(x,y);
164                 if(tiles[index].type == TileTypes.Wall){
165                     tiles[index].id = 0 + calcTileNeighbors(x,y);
166                 } else {
167                     tiles[index].id = 32; // + random_number(0,3);
168                 }
169             }
170         }
171     }
172 
173     int addToTileset(IntRect rect) {
174         int id =  tileRects.length.to!int;
175         tileRects ~= rect;
176 
177         return id;
178     }
179 
180     int calcTileNeighbors(int x, int y) {
181         int neighbors = 0;
182         auto t = getTile(x,y);
183         if (x > 0) {
184             if (getTile(x - 1, y).type == t.type) {
185                 neighbors += Direction.Left;
186             }
187         }
188         if (x < width - 1) {
189             if (getTile(x + 1, y).type == t.type) {
190                 neighbors += Direction.Right;
191             }
192         }
193         if (y > 0) {
194             if (getTile(x, y - 1).type == t.type) {
195                 neighbors += Direction.Up;
196             }
197         }
198         if (y < height - 1) {
199             if (getTile(x,y + 1).type == t.type)  {
200                 neighbors += Direction.Down;
201             }
202         }
203         return neighbors;
204     }
205 
206     ref Tile getTile(int x, int y) {
207         return tiles[x + y * width];
208     }
209 
210     void draw(Vector2f pos, RenderTarget target) {
211         import std.stdio;
212         RectangleShape rect = new RectangleShape();
213         rect.fillColor = Color.White;
214         rect.size = Vector2f(tileWidth, tileHeight);
215         rect.setTexture(tex);
216         int entityIndex = 0;
217         for (int y = 0; y < height; y++) {
218             for (int x = 0; x < width; x++) {
219                 for(int l = 0; l < 2; l++){
220                     int index = getIndex(x,y,l);
221                     if(tiles[index].type != TileTypes.None){
222                         rect.position = pos  + Vector2f(gridWidth * x, gridHeight * y ) + tiles[index].offset;
223 
224                         rect.textureRect = tileRects[tiles[index].id];
225                         target.draw(rect);
226                     }
227                 }
228             }
229             //writeln("Y: ", y);
230             if (entityIndex < entities.length ) {
231                 while (entities[entityIndex].position.y < ((y - 1) * gridHeight)) {
232                    // writeln(format("%d pos(%f,%f)", entityIndex, entities[entityIndex].position.x, entities[entityIndex].position.y));
233                     entities[entityIndex].draw(target);
234                     entityIndex++;
235                     if(entityIndex >= entities.length){
236                         break;
237                     }
238                 }
239             }   
240         }
241     }
242 }