Created
February 1, 2020 19:59
-
-
Save etscrivner/23aad5e2457a005f2f1fe495313d0e3d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SDL.h> | |
#include "language_layer.h" | |
#include "memory_arena.h" | |
#define STB_TRUETYPE_IMPLEMENTATION | |
#include "stb_truetype.h" | |
#define WINDOW_WIDTH 1920 | |
#define WINDOW_HEIGHT 1080 | |
#define TOP_BAR_SIZE 100 | |
#define TILES_PER_WINDOW_WIDTH 20 | |
#define FONT_FILE_NAME "manaspc.ttf" | |
// Map structure | |
#define MAP_TILE_TYPE_ground '_' | |
#define MAP_TILE_TYPE_wall 'X' | |
#define MAP_TILE_TYPE_water '~' | |
// Text rendering | |
#define RENDER_TEXT_X_CENTERED (1 << 0) | |
#define RENDER_TEXT_Y_CENTERED (1 << 1) | |
const char* Dialog[] = { | |
"You awaken in a dungeon\n\nThe air is humid...\n\nNearby you hear something shuffle across\nthe cold floor", | |
0 | |
}; | |
typedef struct font_t { | |
u8* Data; | |
stbtt_fontinfo Font; | |
i32 Ascent; | |
i32 Descent; | |
i32 LineGap; | |
} font; | |
typedef enum direction_t { | |
DIRECTION_right, | |
DIRECTION_up, | |
DIRECTION_left, | |
DIRECTION_down, | |
DIRECTION_MAX | |
} direction; | |
typedef struct map_t { | |
v2i Dim; | |
char Tiles[20][20]; | |
} map; | |
// Anything with a key press: | |
// Up => Down => Repeat => Up | |
// The transition from Up => Down adds 1 to HalfTransitionCount | |
// The transition from Down => Up adds 1 to HalfTransitionCount | |
// | |
// State | HalfTransitionCount | Meaning | |
// ================================================== | |
// Up | 0 | Still unpressed | |
// Up | 1 | Was pressed | |
// Down | 0 | Still pressed | |
// Down | 1 | Just pressed | |
typedef struct key_state_t { | |
b32 EndedDown; | |
u32 HalfTransitionCount; | |
} key_state; | |
typedef struct enemy_t { | |
i32 HP; | |
i32 MaxHP; | |
v2i Pos; | |
v4 Rect; | |
f32 MoveTimeSecs; | |
f32 MoveTimer; | |
} enemy; | |
typedef struct app_t { | |
v2 Window; | |
b32 Running; | |
memory_arena PermArena; | |
memory_arena TempArena; | |
struct { | |
v2 Pos; | |
v2 Rem; | |
v4 Color; | |
v4 WeaponColor; | |
v2 WeaponDim; | |
direction Dir; | |
} Player; | |
u32 NumEnemies; | |
enemy Enemies[10]; | |
v2i TileSize; | |
map Map; | |
u32 NumCollisionRects; | |
v4 CollisionRects[150]; | |
font Font; | |
f32 FadeInSecs; | |
f32 TotalFadeInSecs; | |
u32 DialogIndex; | |
} app; | |
typedef struct input_t { | |
f32 DeltaTimeSecs; | |
union { | |
struct { | |
key_state MoveLeft; | |
key_state MoveRight; | |
key_state MoveUp; | |
key_state MoveDown; | |
key_state ActionDown; | |
key_state ActionRight; | |
key_state Shift; | |
}; | |
key_state Keys[6]; | |
}; | |
} input; | |
internal bool FontLoad(const char* FileName, memory_arena* Arena, font* Result) { | |
FILE* TTFFile = fopen(FileName, "r"); | |
if (!TTFFile) { | |
return(false); | |
} | |
fseek(TTFFile, 0, SEEK_END); | |
long FileSize = ftell(TTFFile); | |
fseek(TTFFile, 0, SEEK_SET); | |
Result->Data = (u8*)ArenaAlloc(Arena, FileSize); | |
fread(Result->Data, 1, FileSize, TTFFile); | |
fclose(TTFFile); | |
stbtt_InitFont(&Result->Font, Result->Data, stbtt_GetFontOffsetForIndex(Result->Data, 0)); | |
stbtt_GetFontVMetrics(&Result->Font, &Result->Ascent, &Result->Descent, &Result->LineGap); | |
return(true); | |
} | |
internal f32 FontLineHeight(app* App, f32 FontSizePixels) { | |
// Calculate line height | |
float Scale = stbtt_ScaleForPixelHeight(&App->Font.Font, FontSizePixels); | |
return(Scale * App->Font.Ascent); | |
} | |
internal f32 FontTextWidthPixels(app* App, f32 FontSizePixels, const char* Text, u32 StartCh, u32 EndCh) { | |
char* Ch = (char*)Text + StartCh; | |
float XPos = 0; | |
int Advance, LSB; | |
float Scale = stbtt_ScaleForPixelHeight(&App->Font.Font, FontSizePixels); | |
while (*Ch && StartCh < EndCh) { | |
stbtt_GetCodepointHMetrics(&App->Font.Font, *Ch, &Advance, &LSB); | |
XPos += (Advance * Scale); | |
if (StartCh + 1 < EndCh) { | |
XPos += Scale * stbtt_GetCodepointKernAdvance(&App->Font.Font, *Ch, *(Ch+1)); | |
} | |
++StartCh; | |
++Ch; | |
} | |
return(XPos); | |
} | |
internal f32 FontTextWidthPixels(app* App, f32 FontSizePixels, const char* Text) { | |
char* Ch = (char*)Text; | |
float XPos = 0; | |
int Advance, LSB; | |
float Scale = stbtt_ScaleForPixelHeight(&App->Font.Font, FontSizePixels); | |
while (*Ch) { | |
stbtt_GetCodepointHMetrics(&App->Font.Font, *Ch, &Advance, &LSB); | |
XPos += (Advance * Scale); | |
if (*(Ch + 1)) { | |
XPos += Scale * stbtt_GetCodepointKernAdvance(&App->Font.Font, *Ch, *(Ch+1)); | |
} | |
Ch++; | |
} | |
return(XPos); | |
} | |
inline direction RandomDirection() { | |
switch (rand() % 4) | |
{ | |
case 0: | |
return(DIRECTION_right); | |
case 1: | |
return(DIRECTION_left); | |
case 2: | |
return(DIRECTION_up); | |
case 3: | |
return(DIRECTION_down); | |
default: | |
return(DIRECTION_right); | |
} | |
} | |
inline b32 IsPressed(key_state State) | |
{ | |
return (State.EndedDown); | |
} | |
inline b32 WasPressed(key_state State) | |
{ | |
return (!State.EndedDown && State.HalfTransitionCount > 0); | |
} | |
internal void LinuxProcessKeyDown(key_state* State, b32 IsDown) | |
{ | |
Assert(IsDown != State->EndedDown); | |
State->EndedDown = IsDown; | |
++State->HalfTransitionCount; | |
} | |
void SetDrawColor(SDL_Renderer* Renderer, v4 Color) | |
{ | |
SDL_SetRenderDrawColor(Renderer, 255 * Color.R, 255 * Color.G, 255 * Color.B, 255 * Color.A); | |
} | |
void DrawTextRevised(SDL_Renderer* Renderer, app* App, const char* Text, float FontSizePixels, v2i Pos) { | |
// Iterate through the text and find all of the newlines. | |
u32 NewlineCount = 0; | |
u32 NewlinePos[100]; // NOTE(eric): No more than 100 newlines | |
for (u32 I = 0; I < strlen(Text); ++I) | |
{ | |
if (Text[I] == '\n') { | |
NewlinePos[NewlineCount++] = I; | |
} | |
} | |
// Add one final virtual newline for the end of the text. | |
NewlinePos[NewlineCount++] = strlen(Text); | |
// Set up rendering constants | |
f32 Scale = stbtt_ScaleForPixelHeight(&App->Font.Font, FontSizePixels); | |
int BitmapHeight = FontSizePixels - (Scale * App->Font.Descent); | |
int Ascent = (int)round(Scale * App->Font.Ascent); | |
u32 StartPos = 0; | |
for (u32 I = 0; I < NewlineCount; ++I) | |
{ | |
int BitmapWidth = (int)FontTextWidthPixels(App, FontSizePixels, Text, StartPos, NewlinePos[I]); | |
u8* Bitmap = (u8*)calloc(1, BitmapWidth * BitmapHeight); | |
// Render characters into bitmap | |
int FinalWidth = 0; | |
for (u32 Pos = StartPos; Pos < NewlinePos[I]; ++Pos) { | |
int CX1, CY1, CX2, CY2; | |
stbtt_GetCodepointBitmapBox(&App->Font.Font, Text[Pos], Scale, Scale, &CX1, &CY1, &CX2, &CY2); | |
int ChY = Ascent + CY1; | |
int ByteOffset = FinalWidth + (ChY * BitmapWidth); | |
stbtt_MakeCodepointBitmap(&App->Font.Font, Bitmap + ByteOffset, CX2 - CX1, CY2 - CY1, BitmapWidth, Scale, Scale, Text[Pos]); | |
int XOffs; | |
stbtt_GetCodepointHMetrics(&App->Font.Font, Text[Pos], &XOffs, 0); | |
FinalWidth += Scale * XOffs; | |
int Kern; | |
Kern = stbtt_GetCodepointKernAdvance(&App->Font.Font, Text[Pos], Text[Pos + 1]); | |
FinalWidth += Scale * Kern; | |
} | |
// Copy bitmap into SDL-appropriate bmp | |
u8* Pixels = (u8*)calloc(1, sizeof(u32) * BitmapWidth * BitmapHeight); | |
u8* Src = Bitmap; | |
u8* DstRow = Pixels; | |
for (i32 Y = 0; Y < BitmapHeight; ++Y) { | |
u32* Dest = (u32*)DstRow; | |
for (i32 X = 0; X < BitmapWidth; ++X) { | |
u8 Alpha = *Src++; | |
*Dest++ = ((Alpha << 24) | (Alpha << 16) | (Alpha << 8) | Alpha); | |
} | |
DstRow += (sizeof(u32) * FinalWidth); | |
} | |
// Copy to SDL texture and render | |
SDL_Texture* FontTexture = SDL_CreateTexture( | |
Renderer, | |
SDL_PIXELFORMAT_RGBA32, | |
SDL_TEXTUREACCESS_STREAMING, | |
BitmapWidth, | |
BitmapHeight | |
); | |
SDL_UpdateTexture(FontTexture, NULL, Pixels, 4 * FinalWidth); | |
SDL_SetTextureBlendMode(FontTexture, SDL_BLENDMODE_BLEND); | |
SDL_Rect SrcR = { .x = 0, .y = 0, .w = FinalWidth, .h = BitmapHeight }; | |
SDL_Rect DstR = { .x = Pos.X, .y = Pos.Y, .w = FinalWidth, .h = BitmapHeight }; | |
SDL_RenderCopy(Renderer, FontTexture, &SrcR, &DstR); | |
SDL_DestroyTexture(FontTexture); | |
free(Pixels); | |
free(Bitmap); | |
StartPos = NewlinePos[I] + 1; | |
Pos.Y += BitmapHeight; | |
} | |
} | |
// Largely based on this code: | |
// https://github.com/justinmeiners/stb-truetype-example/blob/master/main.c | |
void DrawText(SDL_Renderer* Renderer, app* App, const char* Text, float FontSizePixels, v2i Pos) { | |
float scale = stbtt_ScaleForPixelHeight(&App->Font.Font, FontSizePixels); | |
// NOTE(eric): Total height in pixels will be the selected pixel font size | |
// plus additional room for chars that descend below the line. | |
int bitmap_height = FontSizePixels - (scale * App->Font.Descent); | |
int bitmap_width = FontTextWidthPixels(App, FontSizePixels, Text); | |
uint8_t* bitmap = (unsigned char*)malloc(bitmap_height * bitmap_width); | |
memset(bitmap, 0, bitmap_height * bitmap_width); | |
int ascent = (int)round(scale * App->Font.Ascent); | |
int final_width = 0; | |
for (char* at = (char*)Text; *at; ++at) { | |
int c_x1, c_y1, c_x2, c_y2; | |
stbtt_GetCodepointBitmapBox(&App->Font.Font, *at, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); | |
int ch_y = ascent + c_y1; | |
int byte_offset = final_width + (ch_y * bitmap_width); | |
stbtt_MakeCodepointBitmap(&App->Font.Font, bitmap + byte_offset, c_x2 - c_x1, c_y2 - c_y1, bitmap_width, scale, scale, *at); | |
int ax; | |
stbtt_GetCodepointHMetrics(&App->Font.Font, *at, &ax, 0); | |
final_width += scale * ax; | |
int kern; | |
kern = stbtt_GetCodepointKernAdvance(&App->Font.Font, *at, *(at + 1)); | |
final_width += kern * scale; | |
} | |
uint8_t pixels[sizeof(uint32_t) * bitmap_width * bitmap_height]; | |
uint8_t* src = bitmap; | |
uint8_t* dst_row = pixels; | |
for (int by = 0; by < bitmap_height; ++by) { | |
uint32_t* dest = (uint32_t*)dst_row; | |
for (int bx = 0; bx < bitmap_width; ++bx) { | |
u8 alpha = *src++; | |
*dest++ = ((alpha << 24) | (alpha << 16) | (alpha << 8) | alpha); | |
} | |
dst_row += (sizeof(uint32_t) * final_width); | |
} | |
SDL_Texture* font_texture = SDL_CreateTexture( | |
Renderer, | |
SDL_PIXELFORMAT_RGBA32, | |
SDL_TEXTUREACCESS_STREAMING, | |
bitmap_width, | |
bitmap_height | |
); | |
SDL_UpdateTexture(font_texture, NULL, pixels, 4 * final_width); | |
SDL_SetTextureBlendMode(font_texture, SDL_BLENDMODE_BLEND); | |
SDL_Rect srcr = { .x = 0, .y = 0, .w = final_width, .h = bitmap_height }; | |
SDL_Rect dstr = { .x = Pos.X, .y = Pos.Y, .w = final_width, .h = bitmap_height }; | |
SDL_RenderCopy(Renderer, font_texture, &srcr, &dstr); | |
SDL_DestroyTexture(font_texture); | |
free(bitmap); | |
} | |
void DrawText(SDL_Renderer* Renderer, app* App, const char* Text, unsigned int Flags, float FontSizePixels, v2i Pos) { | |
float LineHeight = FontLineHeight(App, FontSizePixels); | |
if (Flags & RENDER_TEXT_Y_CENTERED) { | |
Pos.Y += LineHeight / 2; | |
} | |
if (Flags & RENDER_TEXT_X_CENTERED) { | |
float TextWidthPixels = FontTextWidthPixels(App, FontSizePixels, Text); | |
Pos.X -= TextWidthPixels / 2; | |
} | |
DrawText(Renderer, App, Text, FontSizePixels, Pos); | |
} | |
void DrawPlayer(SDL_Renderer* Renderer, app* App) | |
{ | |
SDL_Rect PlayerRect = {}; | |
PlayerRect.x = App->Window.Width/2 - App->TileSize.Width/2;//App->Player.Pos.X; | |
PlayerRect.y = App->Window.Height/2 - App->TileSize.Height/2;//App->Player.Pos.Y; | |
PlayerRect.w = App->TileSize.Width; | |
PlayerRect.h = App->TileSize.Height; | |
// Player weapon | |
SetDrawColor(Renderer, App->Player.WeaponColor); | |
SDL_Rect WeaponRect = {}; | |
WeaponRect.w = App->Player.WeaponDim.Width; | |
WeaponRect.h = App->Player.WeaponDim.Height; | |
switch (App->Player.Dir) | |
{ | |
case DIRECTION_right: | |
{ | |
WeaponRect.x = PlayerRect.x + App->TileSize.Width / 2; | |
WeaponRect.y = PlayerRect.y + App->TileSize.Height / 2 - App->Player.WeaponDim.Height / 2; | |
} | |
break; | |
case DIRECTION_left: | |
{ | |
WeaponRect.x = PlayerRect.x - App->TileSize.Width / 2; | |
WeaponRect.y = PlayerRect.y + App->TileSize.Height / 2 - App->Player.WeaponDim.Height / 2; | |
} | |
break; | |
case DIRECTION_up: | |
{ | |
WeaponRect.h = App->Player.WeaponDim.Width; | |
WeaponRect.w = App->Player.WeaponDim.Height; | |
WeaponRect.x = PlayerRect.x + App->TileSize.Width / 2 - App->Player.WeaponDim.Height / 2; | |
WeaponRect.y = PlayerRect.y - App->TileSize.Height / 2; | |
} | |
break; | |
case DIRECTION_down: | |
{ | |
WeaponRect.h = App->Player.WeaponDim.Width; | |
WeaponRect.w = App->Player.WeaponDim.Height; | |
WeaponRect.x = PlayerRect.x + App->TileSize.Width / 2 - App->Player.WeaponDim.Height / 2; | |
WeaponRect.y = PlayerRect.y + App->TileSize.Height / 2; | |
} | |
break; | |
default: break; | |
} | |
SDL_RenderFillRect( | |
Renderer, | |
&WeaponRect | |
); | |
// Player body | |
SetDrawColor(Renderer, App->Player.Color); | |
SDL_RenderFillRect( | |
Renderer, | |
&PlayerRect | |
); | |
} | |
internal void PrintRect(v4* Rect) | |
{ | |
printf("(%f, %f, %f, %f)\n", Rect->X, Rect->Y, Rect->Width, Rect->Height); | |
} | |
void DrawMap(SDL_Renderer* Renderer, app* App) | |
{ | |
SDL_Rect TileRect = {}; | |
TileRect.w = App->TileSize.Width; | |
TileRect.h = App->TileSize.Height; | |
for (u32 Y = 0; Y < App->Map.Dim.H; ++Y) | |
{ | |
TileRect.y = Y * App->TileSize.H - App->Player.Pos.Y; | |
for (u32 X = 0; X < App->Map.Dim.W; ++X) | |
{ | |
TileRect.x = X * App->TileSize.W - App->Player.Pos.X; | |
switch (App->Map.Tiles[Y][X]) | |
{ | |
case MAP_TILE_TYPE_wall: | |
SetDrawColor(Renderer, V4(0.3, 0.3, 0.3, 1)); | |
break; | |
case MAP_TILE_TYPE_ground: | |
SetDrawColor(Renderer, V4(0.6, 0.6, 0.6, 1)); | |
break; | |
case MAP_TILE_TYPE_water: | |
SetDrawColor(Renderer, V4(0, 0, 1, 1)); | |
break; | |
default: | |
SetDrawColor(Renderer, V4(1.0, 0.8, 0.9, 1)); | |
break; | |
} | |
SDL_RenderFillRect( | |
Renderer, | |
&TileRect | |
); | |
} | |
} | |
} | |
void DrawRect(SDL_Renderer* Renderer, v4 InRect, v4 Color = V4(1, 1, 1, 1)) | |
{ | |
SDL_Rect Rect = {}; | |
Rect.x = InRect.X; | |
Rect.y = InRect.Y; | |
Rect.w = InRect.Width; | |
Rect.h = InRect.Height; | |
SetDrawColor(Renderer, Color); | |
#if 0 | |
SDL_RenderDrawLine( | |
Renderer, | |
Rect.x, | |
Rect.y, | |
Rect.x + Rect.w, | |
Rect.y | |
); | |
SDL_RenderDrawLine( | |
Renderer, | |
Rect.x + Rect.w, | |
Rect.y, | |
Rect.x + Rect.w, | |
Rect.y + Rect.h | |
); | |
SDL_RenderDrawLine( | |
Renderer, | |
Rect.x + Rect.w, | |
Rect.y + Rect.h, | |
Rect.x, | |
Rect.y + Rect.h | |
); | |
SDL_RenderDrawLine( | |
Renderer, | |
Rect.x, | |
Rect.y + Rect.h, | |
Rect.x, | |
Rect.y | |
); | |
#else | |
SDL_RenderFillRect( | |
Renderer, | |
&Rect | |
); | |
#endif | |
} | |
void DrawEnemies(SDL_Renderer* Renderer, app* App) | |
{ | |
for (u32 I = 0; I < App->NumEnemies; ++I) | |
{ | |
enemy* Enemy = App->Enemies + I; | |
SDL_Rect EnemyRect = {}; | |
EnemyRect.w = App->TileSize.W; | |
EnemyRect.h = App->TileSize.H; | |
f32 FilledPercent = Enemy->HP / (f32)Enemy->MaxHP; | |
EnemyRect.x = Enemy->Pos.X * EnemyRect.w - App->Player.Pos.X; | |
EnemyRect.y = Enemy->Pos.Y * EnemyRect.h - App->Player.Pos.Y; | |
// Enemy health background | |
SetDrawColor(Renderer, V4(0.3, 0.0, 0.0, 1)); | |
SDL_Rect BGRect = EnemyRect; | |
SDL_RenderFillRect( | |
Renderer, | |
&BGRect | |
); | |
// Enemy health | |
SetDrawColor(Renderer, V4(1, 0, 0, 1)); | |
EnemyRect.y += (1.0f - FilledPercent) * EnemyRect.h; | |
EnemyRect.h -= (1.0f - FilledPercent) * EnemyRect.h; | |
SDL_RenderFillRect( | |
Renderer, | |
&EnemyRect | |
); | |
} | |
} | |
void DrawTopBar(SDL_Renderer* Renderer, app* App) | |
{ | |
SDL_Rect BarRect = {}; | |
BarRect.w = App->Window.Width; | |
BarRect.h = TOP_BAR_SIZE; | |
BarRect.x = 0; | |
BarRect.y = 0; | |
SetDrawColor(Renderer, V4(0, 0, 0, 0.5)); | |
SDL_RenderFillRect( | |
Renderer, | |
&BarRect | |
); | |
char* Text = "abcdefghijklmnopqrstuvwxyz\nHello";//"HPj[()]"; | |
DrawTextRevised(Renderer, App, Text, 30, V2I(0, 0)); | |
SetDrawColor(Renderer, V4(1, 1, 1, 1)); | |
SDL_RenderDrawLine(Renderer, 0, TOP_BAR_SIZE, App->Window.Width, TOP_BAR_SIZE); | |
} | |
i32 Sign(f32 Value) | |
{ | |
return (Value > 0) ? 1 : -1; | |
} | |
b32 HasCollision(app* App, v4 Rect) | |
{ | |
for (u32 I = 0; I < App->NumCollisionRects; ++I) | |
{ | |
if (RectRectIntersect(Rect, App->CollisionRects[I])) | |
{ | |
#if 0 | |
printf( | |
"%f,%f,%f,%f - %f,%f,%f,%f\n", | |
Rect.X, Rect.Y, Rect.Z, Rect.W, | |
App->CollisionRects[I].X, App->CollisionRects[I].Y, App->CollisionRects[I].Z, App->CollisionRects[I].W | |
); | |
#endif | |
return(true); | |
} | |
} | |
for (u32 I = 0; I < App->NumEnemies; ++I) | |
{ | |
if (RectRectIntersect(Rect, App->Enemies[I].Rect)) | |
{ | |
return(true); | |
} | |
} | |
return(false); | |
} | |
internal enemy* WeaponHitEnemy(app* App) | |
{ | |
v4 WeaponRect = {}; | |
switch (App->Player.Dir) | |
{ | |
case DIRECTION_right: | |
{ | |
WeaponRect = V4( | |
App->Window.Width/2 - App->TileSize.Width / 2 + App->Player.Pos.X + App->TileSize.W, | |
App->Window.Height/2 + App->Player.Pos.Y - App->Player.WeaponDim.Height / 2, | |
App->Player.WeaponDim.Width, | |
App->Player.WeaponDim.Height | |
); | |
} | |
break; | |
case DIRECTION_left: | |
{ | |
WeaponRect = V4( | |
App->Window.Width/2 - App->TileSize.Width / 2 + App->Player.Pos.X - App->Player.WeaponDim.Width, | |
App->Window.Height/2 + App->Player.Pos.Y - App->Player.WeaponDim.Height / 2, | |
App->Player.WeaponDim.Width, | |
App->Player.WeaponDim.Height | |
); | |
} | |
break; | |
case DIRECTION_up: | |
{ | |
WeaponRect = V4( | |
App->Window.Width/2 - App->TileSize.Width / 2 + App->Player.Pos.X + App->TileSize.W/2 - App->Player.WeaponDim.Height/2, | |
App->Window.Height/2 - App->TileSize.Height / 2 + App->Player.Pos.Y - App->Player.WeaponDim.Width, | |
App->Player.WeaponDim.Height, | |
App->Player.WeaponDim.Width | |
); | |
} | |
break; | |
case DIRECTION_down: | |
{ | |
WeaponRect = V4( | |
App->Window.Width/2 - App->TileSize.Width / 2 + App->Player.Pos.X + App->TileSize.W/2 - App->Player.WeaponDim.Height/2, | |
App->Window.Height/2 + App->TileSize.Height / 2 + App->Player.Pos.Y, | |
App->Player.WeaponDim.Height, | |
App->Player.WeaponDim.Width | |
); | |
} | |
break; | |
default: break; | |
} | |
// NOTE(eric): Draw ephemeral debug rects for weapon and character. Will need | |
// to pass renderer to this method if you want to use. | |
#if 0 | |
DrawRect(Renderer, WeaponRect, V4(0, 1, 1, 1)); | |
DrawRect( | |
Renderer, | |
V4( | |
App->Window.Width/2 - App->TileSize.Width / 2 + App->Player.Pos.X, | |
App->Window.Height/2 - App->TileSize.Height/2 + App->Player.Pos.Y, | |
App->TileSize.Width, | |
App->TileSize.Height | |
), | |
V4(1, 0, 0, 1) | |
); | |
#endif | |
for (u32 I = 0; I < App->NumEnemies; ++I) | |
{ | |
enemy* Enemy = App->Enemies + I; | |
if (Enemy->HP <= 0) | |
{ | |
continue; | |
} | |
#if 0 | |
DrawRect(Renderer, Enemy->Rect, V4(0, 1, 1, 1)); | |
#endif | |
if (RectRectIntersect(WeaponRect, Enemy->Rect)) | |
{ | |
return(Enemy); | |
} | |
} | |
return(0); | |
} | |
void MovePlayerX(app* App, f32 DeltaX) | |
{ | |
App->Player.Rem.X += DeltaX; | |
i32 Move = (i32)round(App->Player.Rem.X); | |
if (Move != 0) | |
{ | |
App->Player.Rem.X -= Move; | |
int Sgn = Sign(Move); | |
v4 PlayerRect = V4( | |
App->Window.Width/2 - App->TileSize.W/2, | |
App->Window.Height/2 - App->TileSize.H/2 + 3*App->TileSize.H/4, | |
App->TileSize.W, | |
App->TileSize.H/4 | |
); | |
while (Move != 0) | |
{ | |
v4 ScreenSpaceAdjust = V4( | |
App->Player.Pos.X + Sgn, | |
App->Player.Pos.Y, | |
0, 0 | |
); | |
if (!HasCollision(App, PlayerRect + ScreenSpaceAdjust)) | |
{ | |
App->Player.Pos.X += Sgn; | |
Move -= Sgn; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
} | |
} | |
void MovePlayerY(app* App, f32 DeltaY) | |
{ | |
App->Player.Rem.Y += DeltaY; | |
i32 Move = (i32)round(App->Player.Rem.Y); | |
if (Move != 0) | |
{ | |
App->Player.Rem.Y -= Move; | |
int Sgn = Sign(Move); | |
while (Move != 0) | |
{ | |
v4 PlayerRect = V4( | |
App->Window.Width/2 - App->TileSize.Width/2, | |
App->Window.Height/2 - App->TileSize.Height/2, | |
App->TileSize.W, | |
App->TileSize.H/4 | |
); | |
v4 ScreenSpaceAdjust = V4( | |
App->Player.Pos.X, | |
App->Player.Pos.Y + 3*App->TileSize.H/4 + Sgn, | |
0, 0 | |
); | |
if (!HasCollision(App, PlayerRect + ScreenSpaceAdjust)) | |
{ | |
App->Player.Pos.Y += Sgn; | |
Move -= Sgn; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
} | |
} | |
inline direction DirectionLeft(direction CurrentDir) | |
{ | |
switch (CurrentDir) | |
{ | |
case DIRECTION_right: | |
return(DIRECTION_up); | |
break; | |
case DIRECTION_up: | |
return(DIRECTION_left); | |
break; | |
case DIRECTION_left: | |
return(DIRECTION_down); | |
break; | |
case DIRECTION_down: | |
return(DIRECTION_right); | |
break; | |
default: break; | |
} | |
return(DIRECTION_MAX); | |
} | |
inline direction DirectionRight(direction CurrentDir) | |
{ | |
switch (CurrentDir) | |
{ | |
case DIRECTION_right: | |
return(DIRECTION_down); | |
break; | |
case DIRECTION_up: | |
return(DIRECTION_right); | |
break; | |
case DIRECTION_left: | |
return(DIRECTION_up); | |
break; | |
case DIRECTION_down: | |
return(DIRECTION_left); | |
break; | |
default: break; | |
} | |
return(DIRECTION_MAX); | |
} | |
void TickEnemies(app* App, input* Input) | |
{ | |
for (u32 I = 0; I < App->NumEnemies; ++I) | |
{ | |
enemy* Enemy = App->Enemies + I; | |
if (Enemy->HP <= 0) | |
{ | |
continue; | |
} | |
Enemy->MoveTimer -= Input->DeltaTimeSecs; | |
if (Enemy->MoveTimer <= 0) | |
{ | |
Enemy->MoveTimer = Enemy->MoveTimeSecs; | |
direction MoveDir = RandomDirection(); | |
v2i NewPos = {}; | |
// Calculate new pos | |
switch (MoveDir) | |
{ | |
case DIRECTION_right: | |
{ | |
NewPos = Enemy->Pos + V2I(1, 0); | |
} | |
break; | |
case DIRECTION_left: | |
{ | |
NewPos = Enemy->Pos - V2I(1, 0); | |
} | |
break; | |
case DIRECTION_up: | |
{ | |
NewPos = Enemy->Pos - V2I(0, 1); | |
} | |
break; | |
case DIRECTION_down: | |
{ | |
NewPos = Enemy->Pos + V2I(0, 1); | |
} | |
default: break; | |
} | |
// Throw away new pos if out of bounds | |
if (NewPos.X < 0 || NewPos.X >= App->Map.Dim.Width || NewPos.Y < 0 || NewPos.Y >= App->Map.Dim.Height) | |
{ | |
continue; | |
} | |
v4 NewRect = V4(NewPos.X * App->TileSize.W, NewPos.Y * App->TileSize.H, App->TileSize.W, App->TileSize.H); | |
v4 PlayerRect = V4( | |
App->Window.Width/2 - App->TileSize.Width/2 + App->Player.Pos.X, | |
App->Window.Height/2 - App->TileSize.Height/2 + App->Player.Pos.Y, | |
App->TileSize.W, | |
App->TileSize.H | |
); | |
// Throw away new pos if collision on map | |
if (App->Map.Tiles[NewPos.Y][NewPos.X] == MAP_TILE_TYPE_wall || App->Map.Tiles[NewPos.Y][NewPos.X] == MAP_TILE_TYPE_water) | |
{ | |
continue; | |
} | |
// Throw away new pos if collision with another enemy | |
b32 WasIntersection = false; | |
for (u32 J = 0; J < App->NumEnemies; ++J) | |
{ | |
// Skip self-collision check | |
if (I == J) | |
{ | |
continue; | |
} | |
if (RectRectIntersect(NewRect, App->Enemies[J].Rect)) | |
{ | |
WasIntersection = true; | |
break; | |
} | |
} | |
if (WasIntersection) | |
{ | |
continue; | |
} | |
// Throw away new pos if collision with character | |
if (RectRectIntersect(PlayerRect, NewRect)) | |
{ | |
continue; | |
} | |
Enemy->Pos = NewPos; | |
Enemy->Rect = NewRect; | |
} | |
} | |
} | |
void UpdateDialogBox(SDL_Renderer* Renderer, app* App, input* Input) | |
{ | |
f32 DialogBoxWidth = 800.0f; | |
f32 DialogBoxHeight = 400.0f; | |
if (App->DialogIndex >= ArrayCount(Dialog) || Dialog[App->DialogIndex] == 0) { | |
return; | |
} | |
v4 DialogBoxRect = V4( | |
App->Window.Width / 2 - DialogBoxWidth/2, | |
App->Window.Height/2 - DialogBoxHeight/8, | |
DialogBoxWidth, | |
DialogBoxHeight | |
); | |
DrawRect( | |
Renderer, | |
DialogBoxRect, | |
V4(0, 0, 0, 0.5) | |
); | |
SetDrawColor(Renderer, V4(1, 1, 1, 1)); | |
SDL_RenderDrawLine(Renderer, DialogBoxRect.X, DialogBoxRect.Y, DialogBoxRect.X + DialogBoxRect.Width, DialogBoxRect.Y); | |
SDL_RenderDrawLine(Renderer, DialogBoxRect.X + DialogBoxRect.Width, DialogBoxRect.Y, DialogBoxRect.X + DialogBoxRect.Width, DialogBoxRect.Y + DialogBoxRect.Height); | |
SDL_RenderDrawLine(Renderer, DialogBoxRect.X + DialogBoxRect.Width, DialogBoxRect.Y + DialogBoxRect.Height, DialogBoxRect.X, DialogBoxRect.Y + DialogBoxRect.Height); | |
SDL_RenderDrawLine(Renderer, DialogBoxRect.X, DialogBoxRect.Y + DialogBoxRect.Height, DialogBoxRect.X, DialogBoxRect.Y); | |
DrawTextRevised(Renderer, App, Dialog[App->DialogIndex], 30, V2I(DialogBoxRect.X + 10, DialogBoxRect.Y + 10)); | |
if (WasPressed(Input->ActionRight)) | |
{ | |
if (App->DialogIndex < ArrayCount(Dialog)) | |
{ | |
App->DialogIndex++; | |
} | |
} | |
} | |
void Update(SDL_Renderer* Renderer, app* App, input* Input) | |
{ | |
// NOTE(eric): Player movement | |
f32 PlayerVel = 500.0f; | |
v2 dP = {}; | |
if (App->FadeInSecs <= 0) { | |
if (IsPressed(Input->Shift)) | |
{ | |
if (WasPressed(Input->MoveLeft)) | |
{ | |
App->Player.Dir = DirectionLeft(App->Player.Dir); | |
} | |
else if (WasPressed(Input->MoveRight)) | |
{ | |
App->Player.Dir = DirectionRight(App->Player.Dir); | |
} | |
} | |
else | |
{ | |
if (IsPressed(Input->MoveLeft)) | |
{ | |
dP.X = -Input->DeltaTimeSecs * PlayerVel; | |
} | |
else if (IsPressed(Input->MoveRight)) | |
{ | |
dP.X = Input->DeltaTimeSecs * PlayerVel; | |
} | |
if (IsPressed(Input->MoveUp)) | |
{ | |
dP.Y = -Input->DeltaTimeSecs * PlayerVel; | |
} | |
else if (IsPressed(Input->MoveDown)) | |
{ | |
dP.Y = Input->DeltaTimeSecs * PlayerVel; | |
} | |
} | |
} | |
MovePlayerX(App, dP.X); | |
MovePlayerY(App, dP.Y); | |
TickEnemies(App, Input); | |
if (WasPressed(Input->ActionDown) && App->FadeInSecs <= 0) | |
{ | |
enemy* HitEnemy = WeaponHitEnemy(App); | |
if (HitEnemy) | |
{ | |
HitEnemy->HP -= 2; | |
} | |
} | |
DrawMap(Renderer, App); | |
DrawEnemies(Renderer, App); | |
DrawPlayer(Renderer, App); | |
//DrawTopBar(Renderer, App); | |
if (App->FadeInSecs >= 0.0f) { | |
DrawRect(Renderer, V4(0, 0, App->Window.Width, App->Window.Height), V4(0, 0, 0, Lerp(0.0f, 1.0f, App->FadeInSecs / App->TotalFadeInSecs))); | |
App->FadeInSecs -= Input->DeltaTimeSecs; | |
} | |
if (App->FadeInSecs <= 0) { | |
UpdateDialogBox(Renderer, App, Input); | |
} | |
// NOTE(eric): Draw collision rects | |
#if 0 | |
v2 Pos = V2( | |
App->Window.Width/2 - App->TileSize.W/2, | |
App->Window.Height/2 - App->TileSize.H/2 | |
); | |
DrawRect(Renderer, V4(Pos.X, Pos.Y + 3*App->TileSize.H/4, App->TileSize.W, App->TileSize.H/4)); | |
for (u32 I = 0; I < App->NumCollisionRects; ++I) | |
{ | |
v4 UpdatedRect = App->CollisionRects[I]; | |
UpdatedRect.X -= App->Player.Pos.X; | |
UpdatedRect.Y -= App->Player.Pos.Y; | |
DrawRect(Renderer, UpdatedRect); | |
} | |
for (u32 I = 0; I < App->NumEnemies; ++I) | |
{ | |
v4 EnemyRect = App->Enemies[I].Rect; | |
EnemyRect.X -= App->Player.Pos.X; | |
EnemyRect.Y -= App->Player.Pos.Y; | |
DrawRect(Renderer, EnemyRect); | |
} | |
#endif | |
} | |
void BuildMapCollisionRects(char* Map, v2i MapDim) { | |
v4 Rects[100]; | |
u32 NumRects = 0; | |
for (i32 Y = 0; Y < MapDim.Height; ++Y) { | |
v4 CurrentRect = V4(-1, -1, -1, -1); | |
for (i32 X = 0; X < MapDim.Width; ++X) { | |
if (Map[X + Y*MapDim.Width] == 'X') { | |
if (CurrentRect.X == -1) { | |
CurrentRect.X = X; | |
CurrentRect.Y = Y; | |
CurrentRect.Height = 1; | |
CurrentRect.Width = 1; | |
} else { | |
CurrentRect.Width += 1; | |
} | |
} else if (CurrentRect.X != -1) { | |
Rects[NumRects++] = CurrentRect; | |
CurrentRect = V4(-1, -1, -1, -1); | |
} | |
} | |
if (CurrentRect.X != -1) { | |
Rects[NumRects++] = CurrentRect; | |
} | |
} | |
} | |
void AppInit(app* App) | |
{ | |
// NOTE(eric): Tile size derived from window size | |
i32 TileDim = App->Window.Width / TILES_PER_WINDOW_WIDTH; | |
App->TileSize = V2I(TileDim, TileDim); | |
App->FadeInSecs = 1.0f; | |
App->TotalFadeInSecs = 1.0f; | |
App->DialogIndex = 0; | |
// NOTE(eric): Load font | |
if (!FontLoad(//"Inconsolata-Regular.ttf", | |
FONT_FILE_NAME, | |
&App->PermArena, &App->Font)) { | |
fprintf(stderr, "error: Unable to load font '%s'\n", FONT_FILE_NAME); | |
} | |
App->Player.Pos = V2(240, 1236);//V2(0, 0);// {{App->Window.Width/2 - TileDim/2, App->Window.Height/2 - TileDim/2}}; | |
App->Player.Color = V4(1, 0, 1, 1); | |
App->Player.WeaponColor = V4(1, 1, 1, 1); | |
App->Player.WeaponDim = V2(App->TileSize.Width, App->TileSize.Height / 4); | |
App->Player.Dir = DIRECTION_right; | |
// NOTE(eric): Build out our map | |
App->Map.Dim = V2I(20, 20); | |
char Array[20][20] = { | |
{'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X', 'X', 'X', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '~', '~', '~', '_', 'X', 'X', 'X', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '~', '~', '~', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', 'X', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', 'X', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', '_', '_', '_', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', '_', 'X', '_', '_', '_', 'X'}, | |
{'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'}, | |
}; | |
memcpy(&App->Map.Tiles, Array, sizeof(char)*App->Map.Dim.X*App->Map.Dim.Y); | |
BuildMapCollisionRects((char*)App->Map.Tiles, App->Map.Dim); | |
// NOTE(eric): Place enemies | |
for (u32 I = 0; I < 5; ++I) | |
{ | |
enemy* Enemy = App->Enemies + App->NumEnemies; | |
// HP Initialization | |
Enemy->HP = 10; | |
Enemy->MaxHP = 10; | |
Enemy->MoveTimeSecs = 5; | |
Enemy->MoveTimer = 5; | |
// Find random position | |
b32 ValidPos = false; | |
v4 PlayerRect = V4( | |
App->Window.Width/2 - App->TileSize.Width/2 + App->Player.Pos.X, | |
App->Window.Height/2 - App->TileSize.Height/2 + App->Player.Pos.Y + 3*App->TileSize.H/4, | |
App->TileSize.W, | |
App->TileSize.H/4 | |
); | |
do { | |
Enemy->Pos = V2I(RandomI32(0, App->Map.Dim.W), RandomI32(0, App->Map.Dim.H)); | |
Enemy->Rect = V4(Enemy->Pos.X * App->TileSize.W, Enemy->Pos.Y * App->TileSize.H, App->TileSize.W, App->TileSize.H); | |
if (App->Map.Tiles[Enemy->Pos.Y][Enemy->Pos.X] == MAP_TILE_TYPE_ground) | |
{ | |
b32 AnotherEntityAtPos = false; | |
// Check enemy collision | |
for (u32 J = 0; J < App->NumEnemies; ++J) | |
{ | |
if (App->Enemies[J].Pos == Enemy->Pos) | |
{ | |
AnotherEntityAtPos = true; | |
} | |
} | |
// Check player collision | |
if (RectRectIntersect(Enemy->Rect, PlayerRect)) | |
{ | |
AnotherEntityAtPos = true; | |
} | |
ValidPos = !AnotherEntityAtPos; | |
} | |
} while (!ValidPos); | |
++App->NumEnemies; | |
} | |
// NOTE(eric): Build collision rects | |
for (u32 Y = 0; Y < App->Map.Dim.Height; ++Y) | |
{ | |
for (u32 X = 0; X < App->Map.Dim.Width; ++X) | |
{ | |
if (App->Map.Tiles[Y][X] == MAP_TILE_TYPE_wall || App->Map.Tiles[Y][X] == MAP_TILE_TYPE_water) | |
{ | |
App->CollisionRects[App->NumCollisionRects++] = {{ | |
(f32)X * App->TileSize.Width, | |
(f32)Y * App->TileSize.Height, | |
(f32)App->TileSize.Width, | |
(f32)App->TileSize.Height | |
}}; | |
} | |
} | |
} | |
printf("Collision Rects: %d\n", App->NumCollisionRects); | |
} | |
int main() | |
{ | |
{ | |
SDL_Init(SDL_INIT_EVERYTHING); | |
SDL_Window* Window = SDL_CreateWindow( | |
"Tiler", | |
SDL_WINDOWPOS_CENTERED, | |
SDL_WINDOWPOS_CENTERED, | |
WINDOW_WIDTH, | |
WINDOW_HEIGHT, | |
SDL_WINDOW_ALLOW_HIGHDPI | |
); | |
SDL_Renderer* Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); | |
// NOTE(eric): Handle High DPI displays where drawable size is possibly | |
// larger than window size. | |
int Width, Height; | |
SDL_GetRendererOutputSize(Renderer, &Width, &Height); | |
// NOTE(eric): Allocate storage arenas | |
u32 PermanentStorageSize = Megabytes(64); | |
u8* PermanentStorage = (u8*)malloc(PermanentStorageSize); | |
u32 TemporaryStorageSize = Megabytes(64); | |
u8* TemporaryStorage = (u8*)malloc(TemporaryStorageSize); | |
// NOTE(eric): Seed RNG before app initialization. Maybe move this into | |
// AppInit? | |
SeedRandomNumberGenerator(); | |
app App = {}; | |
App.Running = true; | |
App.Window = {{(f32)Width, (f32)Height}}; | |
App.PermArena = ArenaInit(PermanentStorage, PermanentStorageSize); | |
App.TempArena = ArenaInit(TemporaryStorage, TemporaryStorageSize); | |
AppInit(&App); | |
input Input = {}; | |
SDL_SetRenderDrawBlendMode(Renderer, SDL_BLENDMODE_BLEND); | |
SDL_Event Event; | |
u32 LastTime = SDL_GetTicks(); | |
while (App.Running) | |
{ | |
while (SDL_PollEvent(&Event)) | |
{ | |
if (Event.type == SDL_QUIT) | |
{ | |
App.Running = false; | |
} | |
else if (Event.type == SDL_KEYDOWN || Event.type == SDL_KEYUP) | |
{ | |
SDL_Keycode KeyCode = Event.key.keysym.sym; | |
b32 IsDown = (Event.key.state == SDL_PRESSED); | |
b32 WasDown = (Event.key.state == SDL_RELEASED && Event.key.repeat == 0); | |
if (KeyCode == SDLK_ESCAPE) | |
{ | |
App.Running = false; | |
} | |
else if (IsDown != WasDown) | |
{ | |
switch (KeyCode) | |
{ | |
case SDLK_LEFT: | |
LinuxProcessKeyDown(&Input.MoveLeft, IsDown); | |
break; | |
case SDLK_RIGHT: | |
LinuxProcessKeyDown(&Input.MoveRight, IsDown); | |
break; | |
case SDLK_UP: | |
LinuxProcessKeyDown(&Input.MoveUp, IsDown); | |
break; | |
case SDLK_DOWN: | |
LinuxProcessKeyDown(&Input.MoveDown, IsDown); | |
break; | |
case SDLK_RETURN: | |
LinuxProcessKeyDown(&Input.ActionRight, IsDown); | |
break; | |
case SDLK_SPACE: | |
LinuxProcessKeyDown(&Input.ActionDown, IsDown); | |
break; | |
case SDLK_LSHIFT: | |
case SDLK_RSHIFT: | |
LinuxProcessKeyDown(&Input.Shift, IsDown); | |
break; | |
default: break; | |
} | |
} | |
} | |
} | |
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); | |
SDL_RenderClear(Renderer); | |
u32 CurrentTime = SDL_GetTicks(); | |
//Input.DeltaTimeSecs = (CurrentTime - LastTime) / 1000.0f; | |
Input.DeltaTimeSecs = 1/60.0f; | |
Update(Renderer, &App, &Input); | |
LastTime = CurrentTime; | |
for (u32 I = 0; I < ArrayCount(Input.Keys); ++I) | |
{ | |
Input.Keys[I].HalfTransitionCount = 0; | |
} | |
SDL_RenderPresent(Renderer); | |
} | |
free(TemporaryStorage); | |
free(PermanentStorage); | |
SDL_DestroyRenderer(Renderer); | |
SDL_DestroyWindow(Window); | |
SDL_Quit(); | |
} | |
return(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment