]> Shamusworld >> Repos - legend/blob - src/legend.cpp
Initial commit for the Legend of A... project!
[legend] / src / legend.cpp
1 //
2 // Legend of ?
3 //
4 // by James Hammons
5 // © 2014 Underground Software
6 //
7 // A simple action RPG; a crass appeal to sentimentalism ;-)
8 //
9 // JLH = James Hammons <jlhamm@acm.org>
10 //
11 // WHO  WHEN        WHAT
12 // ---  ----------  ------------------------------------------------------------
13 // JLH  08/13/2014  Created this file
14 //
15
16 // STILL TO DO:
17 //
18 //
19
20 #include "legend.h"
21
22 #include <SDL2/SDL.h>
23 #include <SDL2/SDL_image.h>
24 #include <fstream>
25 #include <string>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <time.h>
29 #include "log.h"
30 #include "sprite.h"
31 #include "tile.h"
32 #include "sound.h"
33 #include "video.h"
34
35 // Debug and misc. defines
36
37 //#define SOFT_SWITCH_DEBUGGING
38
39 // Global variables
40
41 //bool powerStateChangeRequested = false;
42
43 // Local variables
44
45 //uint8_t lastKeyPressed = 0;
46
47 static bool running = true;                                     // Machine running state flag...
48 static uint32_t startTicks;
49 static uint32_t frameCount;
50
51 // Local functions (technically, they're global...)
52
53 void DoGame(void);
54 //bool LoadImg(char * filename, uint8_t * ram, int size);
55
56 // Local timer callback functions
57
58 //static void FrameCallback(void);
59 //static void BlinkTimer(void);
60
61
62 // Points of interest:
63 // screen is 16 x 11, 16x16 tiles with status bar on top of screen
64 // game follows main sprite's feet, main sprite can overlap background
65
66
67 //
68 // Main loop
69 //
70 int main(int /*argc*/, char * /*argv*/[])
71 {
72         InitLog("./legend.log");
73         srand(time(NULL));                                                                      // Initialize RNG
74
75         WriteLog("About to initialize video...\n");
76
77         if (!InitVideo())
78         {
79                 printf("Aborting!\n");
80                 return -1;
81         }
82
83         if (!InitTileHandler())
84         {
85                 printf("Aborting!\n");
86                 VideoDone();
87                 return -1;
88         }
89
90         if (!InitSpriteHandler())
91         {
92                 printf("Aborting!\n");
93                 VideoDone();
94                 return -1;
95         }
96
97         running = true;                                                         // Set running status...
98         WriteLog("Entering main loop...\n");
99
100         DoGame();
101
102         SoundDone();
103         VideoDone();
104         LogDone();
105
106         return 0;
107 }
108
109
110 void DoGame(void)
111 {
112         startTicks = SDL_GetTicks();
113         bool running = true;
114         SDL_Texture * map1 = CreateMapTexture();
115         SDL_Texture * screen = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888,
116                 SDL_TEXTUREACCESS_TARGET, 16 * 16, 11 * 16);
117
118         bool inRoom = false;
119         Direction playerFacing = Down;
120         int playerInput = Stopped;
121         uint16_t px = (8 * 16) << 7;
122         uint16_t py = (5 * 16) << 7;
123         int currentFrame = 0;
124         int frame[] = { 1,
125                 2, 2, 2, 2, 2, 2,
126                 1, 1, 1, 1, 1, 1,
127                 0, 0, 0, 0, 0, 0,
128                 1, 1, 1, 1, 1,
129         };
130
131         while (running)
132         {
133                 SDL_Event event;
134
135                 while (SDL_PollEvent(&event))
136                 {
137                         switch (event.type)
138                         {
139                         case SDL_KEYDOWN:
140                                 // Use ALT+Q to exit, as well as the usual window decoration
141                                 // method
142 //                              if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
143                                 if (event.key.keysym.sym == SDLK_ESCAPE)
144                                         running = false;
145                                 else if (event.key.keysym.sym == SDLK_z)
146                                         playerInput |= Left;
147                                 else if (event.key.keysym.sym == SDLK_c)
148                                         playerInput |= Right;
149                                 else if (event.key.keysym.sym == SDLK_s)
150                                         playerInput |= Up;
151                                 else if (event.key.keysym.sym == SDLK_x)
152                                         playerInput |= Down;
153
154                                 break;
155
156                         case SDL_KEYUP:
157                                 if (event.key.keysym.sym == SDLK_z)
158                                         playerInput &= ~Left;
159                                 else if (event.key.keysym.sym == SDLK_c)
160                                         playerInput &= ~Right;
161                                 else if (event.key.keysym.sym == SDLK_s)
162                                         playerInput &= ~Up;
163                                 else if (event.key.keysym.sym == SDLK_x)
164                                         playerInput &= ~Down;
165
166                                 break;
167                         }
168                 }
169
170                 uint8_t tile[2], tileUnder[4];
171                 int dx = 0, dy = 0;
172
173                 // Handle player input
174                 if (playerInput & Left)
175                 {
176                         playerFacing = Left;
177                         dx = -1;
178                 }
179                 else if (playerInput & Right)
180                 {
181                         playerFacing = Right;
182                         dx = +1;
183                 }
184                 else if (playerInput & Up)
185                 {
186                         playerFacing = Up;
187                         dy = -1;
188                 }
189                 else if (playerInput & Down)
190                 {
191                         playerFacing = Down;
192                         dy = +1;
193                 }
194                 else
195                 {
196                         currentFrame = 0;
197                 }
198
199                 // Get the tiles the player is moving towards
200                 GetTiles2(px >> 7, py >> 7, playerFacing, tile);
201                 bool walk1 = (tile[0] == '0' || tile[0] == '4' || tile[0] == '2' || tile[0] == '6' ? true : false);
202                 bool walk2 = (tile[1] == '0' || tile[1] == '4' || tile[1] == '2' || tile[1] == '6' ? true : false);
203
204                 int curPx = px >> 7, curPy = py >> 7;
205                 int tileX = curPx / 16, tileY = curPy / 16;
206                 int tileXRem = curPx % 16, tileYRem = curPy % 16;
207                 uint16_t newPx = px + (uint16_t)(dx * (3 << 6));
208                 uint16_t newPy = py + (uint16_t)(dy * (3 << 6));
209                 int newTileX = (newPx >> 7) / 16;
210                 int newTileY = (newPy >> 7) / 16;
211
212                 // Collision detection...
213                 // Check to see if we're stepping on 2 tiles in the direction we're
214                 // moving. If so, check to see what distance remains between where the
215                 // player is and where he wants to be. If that distance is < the move
216                 // delta, see if the tile he wants to move into can be moved into. If
217                 // not, truncate the move so that he moves *exactly* into the next
218                 // single space.
219                 if (dx != 0)
220                 {
221                         // Check to see if way forward is clear...
222                         if (walk1
223                                 && ((tileYRem == 0) || ((tileYRem != 0) && walk2)))
224                                 px = newPx;
225                         // It's blocked ahead, so see if there's still room to move forward.
226                         // (only if we're standing on 2 tiles instead of 1!)
227                         else if (tileXRem != 0)
228                         {
229                                 // We didn't cross the tile boundary, so move normally.
230                                 if (newTileX == tileX)
231                                         px = newPx;
232                                 // We're trying to cross the boundary, so clamp it to the edge.
233                                 else
234                                         px = ((tileX + (dx == 1 ? 1 : 0)) * 16) << 7;
235                         }
236                         else
237                         {
238                                 // Player is stuck at tileXRem == 0, so see if we can nudge
239                                 // them up or down...
240                                 if (walk1 && (tileYRem < 8))
241                                         py = (py - (1 << 7)) & 0xFF80;
242                                 else if (walk2 && (tileYRem > 7))
243                                         py = (py + (1 << 7)) & 0xFF80;
244                         }
245                 }
246                 else if (dy != 0)
247                 {
248                         // Check to see if way forward is clear...
249                         if (walk1
250                                 && ((tileXRem == 0) || ((tileXRem != 0) && walk2)))
251                                 py = newPy;
252                         // It's blocked ahead, so see if there's still room to move forward.
253                         // (only if we're standing on 2 tiles instead of 1!)
254                         else if (tileYRem != 0)
255                         {
256                                 // We didn't cross the tile boundary, so move normally.
257                                 if (newTileY == tileY)
258                                         py = newPy;
259                                 // We're trying to cross the boundary, so clamp it to the edge.
260                                 else
261                                         py = ((tileY + (dy == 1 ? 1 : 0)) * 16) << 7;
262                         }
263                         else
264                         {
265                                 // Player is stuck at tileYRem == 0, so see if we can nudge
266                                 // them left or right...
267                                 if (walk1 && (tileXRem < 8))
268                                         px = (px - (1 << 7)) & 0xFF80;
269                                 else if (walk2 && (tileXRem > 7))
270                                         px = (px + (1 << 7)) & 0xFF80;
271                         }
272                 }
273
274                 // Now that we've moved, see what we're standing on
275                 // Overlap should be taken into account so we don't have the 'Touch It
276                 // And Die' (TIAD) syndrome of pixel perfect collisions...
277                 GetTiles(px >> 7, py >> 7, tileUnder);
278
279                 // *.  .*  ..  ..
280                 // ..  ..  *.  .*
281                 // See which one of the 4 tiles the player is closest to
282                 tileXRem = (px >> 7) % 16, tileYRem = (py >> 7) % 16;
283                 int tileNum = 0;
284
285                 if ((tileXRem < 7) && (tileYRem < 7))
286                         tileNum = 0;
287                 else if ((tileXRem > 9) && (tileYRem < 7))
288                         tileNum = 1;
289                 else if ((tileXRem < 7) && (tileYRem > 9))
290                         tileNum = 2;
291                 else if ((tileXRem > 9) && (tileYRem > 9))
292                         tileNum = 3;
293
294                 // Special tile handling
295                 if ((tileUnder[tileNum] == '2') && (dy == -1))
296                 {
297                         mapSave = currentMap;
298                         pxSave = px & 0xF800;
299                         pySave = py & 0xF800;
300                         uint16_t roomToEnter = currentMap->special;
301                         inRoom = true;
302                         RenderMap((Map *)rooms[roomToEnter], map1);
303                         px = ((7 * 16) + 8) << 7;
304                         py = ((10 * 16) - 0) << 7;
305                 }
306
307                 // Check for moving to the next map screen handling
308                 if ((dy == 1) && (py >= ((10 * 16) << 7)))
309                 {
310                         if (inRoom)
311                         {
312                                 inRoom = false;
313                                 RenderMap(mapSave, map1);
314                                 px = pxSave, py = pySave;
315                         }
316                         else
317                         {
318                                 uint16_t dir = currentMap->s;
319                                 RenderMap((Map *)maps[dir], map1);
320                                 py = 0;
321                         }
322                 }
323                 else if ((dy == -1) && ((py & 0xFF80) == 0))
324                 {
325                         uint16_t dir = currentMap->n;
326                         RenderMap((Map *)maps[dir], map1);
327                         py = (10 * 16) << 7;
328                 }
329                 else if ((dx == 1) && (px >= ((15 * 16) << 7)))
330                 {
331                         uint16_t dir = currentMap->e;
332                         RenderMap((Map *)maps[dir], map1);
333                         px = 0;
334                 }
335                 else if ((dx == -1) && ((px & 0xFF80) == 0))
336                 {
337                         uint16_t dir = currentMap->w;
338                         RenderMap((Map *)maps[dir], map1);
339                         px = (15 * 16) << 7;
340                 }
341
342                 // Do rendering...
343                 SDL_SetRenderTarget(sdlRenderer, screen);
344                 // This should be limited to the visible window...
345                 SDL_RenderCopy(sdlRenderer, map1, NULL, NULL);
346 //              DrawPlayerSprite(playerFacing, px, py);
347                 DrawUnderlay(px >> 7, py >> 7, playerFacing);
348                 DrawPlayerSprite2(px >> 7, py >> 7, playerFacing, frame[currentFrame]);
349                 // Reset main renderer to main window
350                 SDL_SetRenderTarget(sdlRenderer, NULL);
351                 SDL_RenderCopy(sdlRenderer, screen, NULL, NULL);
352                 SDL_RenderPresent(sdlRenderer);
353
354                 // Animation handling...
355                 currentFrame = (currentFrame + 1) % 24;
356
357                 // In order to get our delay to come out approximately 60 Hz, we need to
358                 // add in an extra ms every 3 frames. So this handles that problem. :-)
359                 frameCount = (frameCount + 1) % 3;
360                 uint32_t interval = (frameCount == 0 ? 16 : 17);
361
362                 // We'll try to keep this running at 60 Hz...
363                 while ((SDL_GetTicks() - startTicks) < interval)
364                         SDL_Delay(1);
365
366                 startTicks = SDL_GetTicks();
367         }
368
369         SDL_DestroyTexture(map1);
370 }
371
372