+//
+// Legend of ?
+//
+// by James Hammons
+// © 2014 Underground Software
+//
+// A simple action RPG; a crass appeal to sentimentalism ;-)
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 08/13/2014 Created this file
+//
+
+// STILL TO DO:
+//
+//
+
+#include "legend.h"
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <fstream>
+#include <string>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "log.h"
+#include "sprite.h"
+#include "tile.h"
+#include "sound.h"
+#include "video.h"
+
+// Debug and misc. defines
+
+//#define SOFT_SWITCH_DEBUGGING
+
+// Global variables
+
+//bool powerStateChangeRequested = false;
+
+// Local variables
+
+//uint8_t lastKeyPressed = 0;
+
+static bool running = true; // Machine running state flag...
+static uint32_t startTicks;
+static uint32_t frameCount;
+
+// Local functions (technically, they're global...)
+
+void DoGame(void);
+//bool LoadImg(char * filename, uint8_t * ram, int size);
+
+// Local timer callback functions
+
+//static void FrameCallback(void);
+//static void BlinkTimer(void);
+
+
+// Points of interest:
+// screen is 16 x 11, 16x16 tiles with status bar on top of screen
+// game follows main sprite's feet, main sprite can overlap background
+
+
+//
+// Main loop
+//
+int main(int /*argc*/, char * /*argv*/[])
+{
+ InitLog("./legend.log");
+ srand(time(NULL)); // Initialize RNG
+
+ WriteLog("About to initialize video...\n");
+
+ if (!InitVideo())
+ {
+ printf("Aborting!\n");
+ return -1;
+ }
+
+ if (!InitTileHandler())
+ {
+ printf("Aborting!\n");
+ VideoDone();
+ return -1;
+ }
+
+ if (!InitSpriteHandler())
+ {
+ printf("Aborting!\n");
+ VideoDone();
+ return -1;
+ }
+
+ running = true; // Set running status...
+ WriteLog("Entering main loop...\n");
+
+ DoGame();
+
+ SoundDone();
+ VideoDone();
+ LogDone();
+
+ return 0;
+}
+
+
+void DoGame(void)
+{
+ startTicks = SDL_GetTicks();
+ bool running = true;
+ SDL_Texture * map1 = CreateMapTexture();
+ SDL_Texture * screen = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888,
+ SDL_TEXTUREACCESS_TARGET, 16 * 16, 11 * 16);
+
+ bool inRoom = false;
+ Direction playerFacing = Down;
+ int playerInput = Stopped;
+ uint16_t px = (8 * 16) << 7;
+ uint16_t py = (5 * 16) << 7;
+ int currentFrame = 0;
+ int frame[] = { 1,
+ 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1,
+ };
+
+ while (running)
+ {
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_KEYDOWN:
+ // Use ALT+Q to exit, as well as the usual window decoration
+ // method
+// if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
+ if (event.key.keysym.sym == SDLK_ESCAPE)
+ running = false;
+ else if (event.key.keysym.sym == SDLK_z)
+ playerInput |= Left;
+ else if (event.key.keysym.sym == SDLK_c)
+ playerInput |= Right;
+ else if (event.key.keysym.sym == SDLK_s)
+ playerInput |= Up;
+ else if (event.key.keysym.sym == SDLK_x)
+ playerInput |= Down;
+
+ break;
+
+ case SDL_KEYUP:
+ if (event.key.keysym.sym == SDLK_z)
+ playerInput &= ~Left;
+ else if (event.key.keysym.sym == SDLK_c)
+ playerInput &= ~Right;
+ else if (event.key.keysym.sym == SDLK_s)
+ playerInput &= ~Up;
+ else if (event.key.keysym.sym == SDLK_x)
+ playerInput &= ~Down;
+
+ break;
+ }
+ }
+
+ uint8_t tile[2], tileUnder[4];
+ int dx = 0, dy = 0;
+
+ // Handle player input
+ if (playerInput & Left)
+ {
+ playerFacing = Left;
+ dx = -1;
+ }
+ else if (playerInput & Right)
+ {
+ playerFacing = Right;
+ dx = +1;
+ }
+ else if (playerInput & Up)
+ {
+ playerFacing = Up;
+ dy = -1;
+ }
+ else if (playerInput & Down)
+ {
+ playerFacing = Down;
+ dy = +1;
+ }
+ else
+ {
+ currentFrame = 0;
+ }
+
+ // Get the tiles the player is moving towards
+ GetTiles2(px >> 7, py >> 7, playerFacing, tile);
+ bool walk1 = (tile[0] == '0' || tile[0] == '4' || tile[0] == '2' || tile[0] == '6' ? true : false);
+ bool walk2 = (tile[1] == '0' || tile[1] == '4' || tile[1] == '2' || tile[1] == '6' ? true : false);
+
+ int curPx = px >> 7, curPy = py >> 7;
+ int tileX = curPx / 16, tileY = curPy / 16;
+ int tileXRem = curPx % 16, tileYRem = curPy % 16;
+ uint16_t newPx = px + (uint16_t)(dx * (3 << 6));
+ uint16_t newPy = py + (uint16_t)(dy * (3 << 6));
+ int newTileX = (newPx >> 7) / 16;
+ int newTileY = (newPy >> 7) / 16;
+
+ // Collision detection...
+ // Check to see if we're stepping on 2 tiles in the direction we're
+ // moving. If so, check to see what distance remains between where the
+ // player is and where he wants to be. If that distance is < the move
+ // delta, see if the tile he wants to move into can be moved into. If
+ // not, truncate the move so that he moves *exactly* into the next
+ // single space.
+ if (dx != 0)
+ {
+ // Check to see if way forward is clear...
+ if (walk1
+ && ((tileYRem == 0) || ((tileYRem != 0) && walk2)))
+ px = newPx;
+ // It's blocked ahead, so see if there's still room to move forward.
+ // (only if we're standing on 2 tiles instead of 1!)
+ else if (tileXRem != 0)
+ {
+ // We didn't cross the tile boundary, so move normally.
+ if (newTileX == tileX)
+ px = newPx;
+ // We're trying to cross the boundary, so clamp it to the edge.
+ else
+ px = ((tileX + (dx == 1 ? 1 : 0)) * 16) << 7;
+ }
+ else
+ {
+ // Player is stuck at tileXRem == 0, so see if we can nudge
+ // them up or down...
+ if (walk1 && (tileYRem < 8))
+ py = (py - (1 << 7)) & 0xFF80;
+ else if (walk2 && (tileYRem > 7))
+ py = (py + (1 << 7)) & 0xFF80;
+ }
+ }
+ else if (dy != 0)
+ {
+ // Check to see if way forward is clear...
+ if (walk1
+ && ((tileXRem == 0) || ((tileXRem != 0) && walk2)))
+ py = newPy;
+ // It's blocked ahead, so see if there's still room to move forward.
+ // (only if we're standing on 2 tiles instead of 1!)
+ else if (tileYRem != 0)
+ {
+ // We didn't cross the tile boundary, so move normally.
+ if (newTileY == tileY)
+ py = newPy;
+ // We're trying to cross the boundary, so clamp it to the edge.
+ else
+ py = ((tileY + (dy == 1 ? 1 : 0)) * 16) << 7;
+ }
+ else
+ {
+ // Player is stuck at tileYRem == 0, so see if we can nudge
+ // them left or right...
+ if (walk1 && (tileXRem < 8))
+ px = (px - (1 << 7)) & 0xFF80;
+ else if (walk2 && (tileXRem > 7))
+ px = (px + (1 << 7)) & 0xFF80;
+ }
+ }
+
+ // Now that we've moved, see what we're standing on
+ // Overlap should be taken into account so we don't have the 'Touch It
+ // And Die' (TIAD) syndrome of pixel perfect collisions...
+ GetTiles(px >> 7, py >> 7, tileUnder);
+
+ // *. .* .. ..
+ // .. .. *. .*
+ // See which one of the 4 tiles the player is closest to
+ tileXRem = (px >> 7) % 16, tileYRem = (py >> 7) % 16;
+ int tileNum = 0;
+
+ if ((tileXRem < 7) && (tileYRem < 7))
+ tileNum = 0;
+ else if ((tileXRem > 9) && (tileYRem < 7))
+ tileNum = 1;
+ else if ((tileXRem < 7) && (tileYRem > 9))
+ tileNum = 2;
+ else if ((tileXRem > 9) && (tileYRem > 9))
+ tileNum = 3;
+
+ // Special tile handling
+ if ((tileUnder[tileNum] == '2') && (dy == -1))
+ {
+ mapSave = currentMap;
+ pxSave = px & 0xF800;
+ pySave = py & 0xF800;
+ uint16_t roomToEnter = currentMap->special;
+ inRoom = true;
+ RenderMap((Map *)rooms[roomToEnter], map1);
+ px = ((7 * 16) + 8) << 7;
+ py = ((10 * 16) - 0) << 7;
+ }
+
+ // Check for moving to the next map screen handling
+ if ((dy == 1) && (py >= ((10 * 16) << 7)))
+ {
+ if (inRoom)
+ {
+ inRoom = false;
+ RenderMap(mapSave, map1);
+ px = pxSave, py = pySave;
+ }
+ else
+ {
+ uint16_t dir = currentMap->s;
+ RenderMap((Map *)maps[dir], map1);
+ py = 0;
+ }
+ }
+ else if ((dy == -1) && ((py & 0xFF80) == 0))
+ {
+ uint16_t dir = currentMap->n;
+ RenderMap((Map *)maps[dir], map1);
+ py = (10 * 16) << 7;
+ }
+ else if ((dx == 1) && (px >= ((15 * 16) << 7)))
+ {
+ uint16_t dir = currentMap->e;
+ RenderMap((Map *)maps[dir], map1);
+ px = 0;
+ }
+ else if ((dx == -1) && ((px & 0xFF80) == 0))
+ {
+ uint16_t dir = currentMap->w;
+ RenderMap((Map *)maps[dir], map1);
+ px = (15 * 16) << 7;
+ }
+
+ // Do rendering...
+ SDL_SetRenderTarget(sdlRenderer, screen);
+ // This should be limited to the visible window...
+ SDL_RenderCopy(sdlRenderer, map1, NULL, NULL);
+// DrawPlayerSprite(playerFacing, px, py);
+ DrawUnderlay(px >> 7, py >> 7, playerFacing);
+ DrawPlayerSprite2(px >> 7, py >> 7, playerFacing, frame[currentFrame]);
+ // Reset main renderer to main window
+ SDL_SetRenderTarget(sdlRenderer, NULL);
+ SDL_RenderCopy(sdlRenderer, screen, NULL, NULL);
+ SDL_RenderPresent(sdlRenderer);
+
+ // Animation handling...
+ currentFrame = (currentFrame + 1) % 24;
+
+ // In order to get our delay to come out approximately 60 Hz, we need to
+ // add in an extra ms every 3 frames. So this handles that problem. :-)
+ frameCount = (frameCount + 1) % 3;
+ uint32_t interval = (frameCount == 0 ? 16 : 17);
+
+ // We'll try to keep this running at 60 Hz...
+ while ((SDL_GetTicks() - startTicks) < interval)
+ SDL_Delay(1);
+
+ startTicks = SDL_GetTicks();
+ }
+
+ SDL_DestroyTexture(map1);
+}
+
+