Files
handmadehero/handmade.c
T
2026-04-03 18:05:35 -07:00

411 lines
12 KiB
C

#include "core.h"
#include "handmade.h"
#include "sdl_handmade.h"
#include <math.h>
#include <string.h> /* memset */
static void GameOutputSound(game_state *GameState,
game_sound_output_buffer *SoundBuffer,
S32 ToneHz)
{
/*
S16 ToneVolume, *SampleOut;
S32 WavePeriod, SampleIndex;
ToneVolume = 0;
WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz;
SampleOut = SoundBuffer->Samples;
for(SampleIndex = 0;
SampleIndex < SoundBuffer->SampleCount;
SampleIndex++)
{
F32 SineValue;
S16 SampleValue;
SineValue = sinf(GameState->tSine);
SampleValue = (S16)(SineValue * ToneVolume);
*SampleOut++ = SampleValue;
*SampleOut++ = SampleValue;
GameState->tSine += 2.0f * (F32)PI * 1.0f / (F32)WavePeriod;
if(GameState->tSine > 2.0f*(F32)PI) {
GameState->tSine -= 2.0f*(F32)PI;
}
}
*/
}
static void ClearBackground(game_offscreen_buffer *Buffer)
{
S32 Width, Height, X;
Width = Buffer->Width;
Height = Buffer->Height;
for(X = 0; X < Width * Height; X++) {
U32 *Pixel = &((U32 *)Buffer->Memory)[X];
*Pixel = 0xFFFFFF;
}
}
static S32 RoundF32toS32(F32 Real32)
{
S32 Result = (S32)(Real32 + 0.5f);
return Result;
}
static void DrawRectangle(game_offscreen_buffer *Buffer,
F32 RealMinX, F32 RealMinY,
F32 RealMaxX, F32 RealMaxY,
F32 R, F32 G, F32 B)
{
S32 MinX = RoundF32toS32(RealMinX);
S32 MinY = RoundF32toS32(RealMinY);
S32 MaxX = RoundF32toS32(RealMaxX);
S32 MaxY = RoundF32toS32(RealMaxY);
S32 Width = Buffer->Width;
S32 Height = Buffer->Height;
U8 *Row;
S32 Y, X;
U32 Color;
if(MinX < 0)
MinX = 0;
if(MinY < 0)
MinY = 0;
if(MaxX > Width)
MaxX = Width;
if(MaxY > Height)
MaxY = Height;
Color = (U32)((RoundF32toS32(R * 255.0f) << 16) |
(RoundF32toS32(G * 255.0f) << 8) |
(RoundF32toS32(B * 255.0f) << 0));
Row = ((U8 *)Buffer->Memory + MinX*Buffer->BytesPerPixel +
MinY*Buffer->Pitch);
for(Y = MinY; Y < MaxY; Y++) {
U32 *Pixel = (U32 *)Row;
for(X = MinX; X < MaxX; X++) {
*(U32 *)Pixel = Color;
Pixel++;
}
Row += Buffer->Pitch;
}
}
static void RenderRectangle(game_offscreen_buffer *Buffer, S32 MouseX,
S32 MouseY)
{
U8 *Row;
U32 *Pixel;
S32 X, Y;
for(Y = MouseY; Y < MouseY+10; Y++) {
if(Y >= Buffer->Height)
break;
Row = (U8 *)Buffer->Memory + (Buffer->Pitch * Y);
for(X = MouseX; X < MouseX+10; X++) {
if(X >= Buffer->Width)
break;
Pixel = (U32 *)Row + X;
*Pixel = 0xFFFFFF;
}
}
}
static tile_map *GetTileMap(world *World, S32 TileMapX, S32 TileMapY)
{
tile_map *TileMap = NULL;
if((TileMapX >= 0) && (TileMapX < World->TileMapCountX) &&
(TileMapY >= 0) && (TileMapY < World->TileMapCountY))
{
TileMap =
&World->TileMaps[TileMapY*World->TileMapCountX + TileMapX];
}
return TileMap;
}
static U32 GetTileValueUnchecked(tile_map *TileMap, S32 TileX, S32 TileY)
{
U32 TileMapValue = TileMap->Tiles[TileY*TileMap->CountX + TileX];
return TileMapValue;
}
static B32 IsTileMapPointEmpty(tile_map *TileMap, F32 TestX, F32 TestY)
{
B32 Empty = FALSE;
S32 PlayerTileX =
(S32)((TestX - TileMap->UpperLeftX) / TileMap->TileWidth);
S32 PlayerTileY =
(S32)((TestY - TileMap->UpperLeftY) / TileMap->TileHeight);
if((PlayerTileX >= 0) && (PlayerTileX < TileMap->CountX) &&
(PlayerTileY >= 0) && (PlayerTileY < TileMap->CountY))
{
U32 TileMapValue =
GetTileValueUnchecked(TileMap, PlayerTileX, PlayerTileY);
Empty = (TileMapValue == 0);
}
return Empty;
}
static B32 IsWorldPointEmpty(world *World,
S32 TileMapX, S32 TileMapY,
F32 TestX, F32 TestY)
{
B32 Empty = FALSE;
tile_map *TileMap = GetTileMap(World, TileMapX, TileMapY);
S32 PlayerTileX, PlayerTileY;
TileMap = GetTileMap(World, TileMapX, TileMapY);
if(TileMap) {
S32 PlayerTileX =
(S32)((TestX - TileMap->UpperLeftX) / TileMap->TileWidth);
S32 PlayerTileY =
(S32)((TestY - TileMap->UpperLeftY) / TileMap->TileHeight);
if((PlayerTileX >= 0) && (PlayerTileX < TileMap->CountX) &&
(PlayerTileY >= 0) && (PlayerTileY < TileMap->CountY))
{
U32 TileMapValue =
GetTileValueUnchecked(TileMap, PlayerTileX, PlayerTileY);
Empty = (TileMapValue == 0);
}
}
return Empty;
}
void UpdateAndRender(thread_context *Thread, game_memory *Memory,
game_input *Input, game_offscreen_buffer *Buffer,
game_sound_output_buffer *SoundBuffer)
{
#define TILEMAP_COUNT_X 17
#define TILEMAP_COUNT_Y 9
game_state *GameState;
U32 ControllerIndex;
S32 Row, Column;
U32 Tiles00[TILEMAP_COUNT_Y][TILEMAP_COUNT_X] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
{1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
};
U32 Tiles01[TILEMAP_COUNT_Y][TILEMAP_COUNT_X] = {
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};
U32 Tiles10[TILEMAP_COUNT_Y][TILEMAP_COUNT_X] = {
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
};
U32 Tiles11[TILEMAP_COUNT_Y][TILEMAP_COUNT_X] = {
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};
F32 PlayerR, PlayerG, PlayerB, PlayerWidth, PlayerHeight,
PlayerLeft, PlayerTop;
tile_map TileMaps[2][2];
tile_map *TileMap;
world World;
ASSERT((&Input->Controllers[0].u.buttons.Terminator -
&Input->Controllers[0].u.Buttons[0]) ==
ARRAY_SIZE(Input->Controllers[0].u.Buttons));
ASSERT(sizeof(game_state) <= Memory->PermanentStorageSize);
memset(&World, 0, sizeof(World));
TileMaps[0][0].CountX = TILEMAP_COUNT_X;
TileMaps[0][0].CountY = TILEMAP_COUNT_Y;
TileMaps[0][0].UpperLeftX = 0.0f;
TileMaps[0][0].UpperLeftY = 0.0f;
TileMaps[0][0].TileWidth = 57.0f;
TileMaps[0][0].TileHeight = 57.0f;
TileMaps[0][0].Tiles = (U32 *)Tiles00;
TileMaps[0][1] = TileMaps[0][0];
TileMaps[0][1].Tiles = (U32 *)Tiles01;
TileMaps[1][0] = TileMaps[0][0];
TileMaps[1][0].Tiles = (U32 *)Tiles10;
TileMaps[1][1] = TileMaps[0][0];
TileMaps[1][1].Tiles = (U32 *)Tiles11;
TileMap = &TileMaps[0][0];
World.TileMaps = (tile_map *)TileMaps;
PlayerWidth = 0.75f * TileMap->TileWidth;
PlayerHeight = TileMap->TileHeight;
GameState = (game_state *)Memory->PermanentStorage;
if(!Memory->IsInitialized) {
GameState->PlayerX = 200.0f;
GameState->PlayerY = 200.0f;
Memory->IsInitialized = TRUE;
}
for(ControllerIndex = 0;
ControllerIndex < ARRAY_SIZE(Input->Controllers);
ControllerIndex++)
{
game_controller_input *Controller =
GetController(Input, ControllerIndex);
if(Controller->IsAnalog) {
} else {
F32 dPlayerX = 0.0f;
F32 dPlayerY = 0.0f;
F32 NewPlayerX, NewPlayerY;
if(Controller->u.buttons.MoveUp.EndedDown) {
dPlayerY = -1.0f;
}
if(Controller->u.buttons.MoveDown.EndedDown) {
dPlayerY = 1.0f;
}
if(Controller->u.buttons.MoveLeft.EndedDown) {
dPlayerX = -1.0f;
}
if(Controller->u.buttons.MoveRight.EndedDown) {
dPlayerX = 1.0f;
}
dPlayerX *= 100.0f;
dPlayerY *= 100.0f;
NewPlayerX = RoundF32toS32(GameState->PlayerX +
Input->dtForFrame*dPlayerX);
NewPlayerY = RoundF32toS32(GameState->PlayerY +
Input->dtForFrame*dPlayerY);
if(IsTileMapPointEmpty(TileMap, NewPlayerX - 0.5f*PlayerWidth,
NewPlayerY) &&
IsTileMapPointEmpty(TileMap, NewPlayerX + 0.5f*PlayerWidth,
NewPlayerY) &&
IsTileMapPointEmpty(TileMap, NewPlayerX, NewPlayerY))
{
GameState->PlayerX = NewPlayerX;
GameState->PlayerY = NewPlayerY;
}
}
}
DrawRectangle(Buffer, 0.0f, 0.0f,
(F32)(Buffer->Width), (F32)(Buffer->Height),
0.0f, 1.0f, 0.0f);
for(Row = 0; Row < TILEMAP_COUNT_Y; Row++) {
for(Column = 0; Column < TILEMAP_COUNT_X; Column++) {
F32 MinX, MinY, MaxX, MaxY;
U32 TileID = GetTileValueUnchecked(TileMap, Column, Row);
F32 Gray = 0.5f;
if(TileID) {
Gray = 1.0f;
}
MinX = TileMap->UpperLeftX + (F32)(Column) * TileMap->TileWidth;
MinY = TileMap->UpperLeftY + (F32)(Row) * TileMap->TileHeight;
MaxX = MinX + TileMap->TileWidth;
MaxY = MinY + TileMap->TileHeight;
DrawRectangle(Buffer, MinX, MinY, MaxX, MaxY, Gray, Gray, Gray);
}
}
PlayerR = 1.0f;
PlayerG = 0.0f;
PlayerB = 0.0f;
PlayerLeft = GameState->PlayerX - 0.5f*PlayerWidth;
PlayerTop = GameState->PlayerY - PlayerHeight;
DrawRectangle(Buffer,
PlayerLeft, PlayerTop,
PlayerLeft + PlayerWidth, PlayerTop + PlayerHeight,
PlayerR, PlayerG, PlayerB);
DrawRectangle(Buffer,
(F32)(Input->MouseX), (F32)(Input->MouseY),
(F32)(Input->MouseX) + 10.0f, (F32)(Input->MouseY) + 10.0f,
1.0f, 0.5f, 0.0f);
GameOutputSound(GameState, SoundBuffer, 400);
}
/*
static void RenderWeirdGradient(game_offscreen_buffer *Buffer, S32 XOffset,
S32 YOffset)
{
S32 Width, Height, Y;
U8 *Row;
Width = Buffer->Width;
Height = Buffer->Height;
Row = (U8 *)Buffer->Memory;
for(Y = 0; Y < Height; Y++) {
U32 *Pixel = (U32 *)Row;
S32 X;
Pixel = (U32 *)Row;
for(X = 0; X < Width; X++) {
U8 Blue, Green;
Blue = (U8)(X + XOffset);
Green = (U8)(Y + YOffset);
*Pixel = (Green << 8) | Blue;
Pixel++;
}
Row += Buffer->Pitch;
}
}
*/