5 // © 2014 Underground Software
7 // A simple action RPG; a crass appeal to sentimentalism ;-)
9 // JLH = James Hammons <jlhamm@acm.org>
12 // --- ---------- ------------------------------------------------------------
13 // JLH 08/13/2014 Created this file
23 #include <SDL2/SDL_image.h>
35 // Debug and misc. defines
37 //#define SOFT_SWITCH_DEBUGGING
41 //bool powerStateChangeRequested = false;
45 //uint8_t lastKeyPressed = 0;
47 static bool running = true; // Machine running state flag...
48 static uint32_t startTicks;
49 static uint32_t frameCount;
51 // Local functions (technically, they're global...)
54 //bool LoadImg(char * filename, uint8_t * ram, int size);
56 // Local timer callback functions
58 //static void FrameCallback(void);
59 //static void BlinkTimer(void);
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
70 int main(int /*argc*/, char * /*argv*/[])
72 InitLog("./legend.log");
73 srand(time(NULL)); // Initialize RNG
75 WriteLog("About to initialize video...\n");
79 printf("Aborting!\n");
83 if (!InitTileHandler())
85 printf("Aborting!\n");
90 if (!InitSpriteHandler())
92 printf("Aborting!\n");
97 running = true; // Set running status...
98 WriteLog("Entering main loop...\n");
112 startTicks = SDL_GetTicks();
114 SDL_Texture * map1 = CreateMapTexture();
115 SDL_Texture * screen = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888,
116 SDL_TEXTUREACCESS_TARGET, 16 * 16, 11 * 16);
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;
135 while (SDL_PollEvent(&event))
140 // Use ALT+Q to exit, as well as the usual window decoration
142 // if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
143 if (event.key.keysym.sym == SDLK_ESCAPE)
145 else if (event.key.keysym.sym == SDLK_z)
147 else if (event.key.keysym.sym == SDLK_c)
148 playerInput |= Right;
149 else if (event.key.keysym.sym == SDLK_s)
151 else if (event.key.keysym.sym == SDLK_x)
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)
163 else if (event.key.keysym.sym == SDLK_x)
164 playerInput &= ~Down;
170 uint8_t tile[2], tileUnder[4];
173 // Handle player input
174 if (playerInput & Left)
179 else if (playerInput & Right)
181 playerFacing = Right;
184 else if (playerInput & Up)
189 else if (playerInput & Down)
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);
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;
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
221 // Check to see if way forward is clear...
223 && ((tileYRem == 0) || ((tileYRem != 0) && walk2)))
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)
229 // We didn't cross the tile boundary, so move normally.
230 if (newTileX == tileX)
232 // We're trying to cross the boundary, so clamp it to the edge.
234 px = ((tileX + (dx == 1 ? 1 : 0)) * 16) << 7;
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;
248 // Check to see if way forward is clear...
250 && ((tileXRem == 0) || ((tileXRem != 0) && walk2)))
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)
256 // We didn't cross the tile boundary, so move normally.
257 if (newTileY == tileY)
259 // We're trying to cross the boundary, so clamp it to the edge.
261 py = ((tileY + (dy == 1 ? 1 : 0)) * 16) << 7;
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;
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);
281 // See which one of the 4 tiles the player is closest to
282 tileXRem = (px >> 7) % 16, tileYRem = (py >> 7) % 16;
285 if ((tileXRem < 7) && (tileYRem < 7))
287 else if ((tileXRem > 9) && (tileYRem < 7))
289 else if ((tileXRem < 7) && (tileYRem > 9))
291 else if ((tileXRem > 9) && (tileYRem > 9))
294 // Special tile handling
295 if ((tileUnder[tileNum] == '2') && (dy == -1))
297 mapSave = currentMap;
298 pxSave = px & 0xF800;
299 pySave = py & 0xF800;
300 uint16_t roomToEnter = currentMap->special;
302 RenderMap((Map *)rooms[roomToEnter], map1);
303 px = ((7 * 16) + 8) << 7;
304 py = ((10 * 16) - 0) << 7;
307 // Check for moving to the next map screen handling
308 if ((dy == 1) && (py >= ((10 * 16) << 7)))
313 RenderMap(mapSave, map1);
314 px = pxSave, py = pySave;
318 uint16_t dir = currentMap->s;
319 RenderMap((Map *)maps[dir], map1);
323 else if ((dy == -1) && ((py & 0xFF80) == 0))
325 uint16_t dir = currentMap->n;
326 RenderMap((Map *)maps[dir], map1);
329 else if ((dx == 1) && (px >= ((15 * 16) << 7)))
331 uint16_t dir = currentMap->e;
332 RenderMap((Map *)maps[dir], map1);
335 else if ((dx == -1) && ((px & 0xFF80) == 0))
337 uint16_t dir = currentMap->w;
338 RenderMap((Map *)maps[dir], map1);
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);
354 // Animation handling...
355 currentFrame = (currentFrame + 1) % 24;
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);
362 // We'll try to keep this running at 60 Hz...
363 while ((SDL_GetTicks() - startTicks) < interval)
366 startTicks = SDL_GetTicks();
369 SDL_DestroyTexture(map1);