428 lines
14 KiB
C
428 lines
14 KiB
C
#include "core.h"
|
|
#include "handmade.h"
|
|
#include "sdl_handmade.h"
|
|
|
|
#include "handmade_intrinsics.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 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 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(world *World, tile_map *TileMap, S32 TileX, S32 TileY)
|
|
{
|
|
U32 TileMapValue;
|
|
|
|
ASSERT(TileMap);
|
|
ASSERT((TileX >= 0) && (TileX < World->CountX) &&
|
|
(TileY >= 0) && (TileY < World->CountY));
|
|
|
|
TileMapValue = TileMap->Tiles[TileY*World->CountX + TileX];
|
|
return TileMapValue;
|
|
}
|
|
|
|
static B32 IsTileMapPointEmpty(world *World, tile_map *TileMap,
|
|
S32 TestTileX, S32 TestTileY)
|
|
{
|
|
B32 Empty = FALSE;
|
|
|
|
if(TileMap) {
|
|
if((TestTileX >= 0) && (TestTileX < World->CountX) &&
|
|
(TestTileY >= 0) && (TestTileY < World->CountY))
|
|
{
|
|
U32 TileMapValue =
|
|
GetTileValueUnchecked(World, TileMap, TestTileX, TestTileY);
|
|
Empty = (TileMapValue == 0);
|
|
}
|
|
}
|
|
|
|
return Empty;
|
|
}
|
|
|
|
static cannonical_position GetCannonicalPosition(world *World,
|
|
raw_position Pos)
|
|
{
|
|
cannonical_position Result;
|
|
F32 X, Y;
|
|
|
|
Result.TileMapX = Pos.TileMapX;
|
|
Result.TileMapY = Pos.TileMapY;
|
|
|
|
X = Pos.X - World->UpperLeftX;
|
|
Y = Pos.Y - World->UpperLeftY;
|
|
Result.TileX = FloorF32toS32(X / (F32)World->TileSideInPixels);
|
|
Result.TileY = FloorF32toS32(Y / (F32)World->TileSideInPixels);
|
|
|
|
Result.X = X - (F32)Result.TileX*(F32)World->TileSideInPixels;
|
|
Result.Y = Y - (F32)Result.TileY*(F32)World->TileSideInPixels;
|
|
|
|
ASSERT(Result.X >= 0);
|
|
ASSERT(Result.Y >= 0);
|
|
ASSERT(Result.X < (F32)World->TileSideInPixels);
|
|
ASSERT(Result.Y < (F32)World->TileSideInPixels);
|
|
|
|
if(Result.TileX < 0) {
|
|
Result.TileX = World->CountX + Result.TileX;
|
|
Result.TileMapX--;
|
|
}
|
|
if(Result.TileY < 0) {
|
|
Result.TileY = World->CountY + Result.TileY;
|
|
Result.TileMapY--;
|
|
}
|
|
if(Result.TileX >= World->CountX) {
|
|
Result.TileX = Result.TileX - World->CountX;
|
|
Result.TileMapX++;
|
|
}
|
|
if(Result.TileY >= World->CountY) {
|
|
Result.TileY = Result.TileY - World->CountY;
|
|
Result.TileMapY++;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static B32 IsWorldPointEmpty(world *World, raw_position TestPos)
|
|
{
|
|
B32 Empty = FALSE;
|
|
|
|
tile_map *TileMap;
|
|
|
|
cannonical_position CanPos = GetCannonicalPosition(World, TestPos);
|
|
TileMap = GetTileMap(World, CanPos.TileMapX, CanPos.TileMapY);
|
|
Empty = IsTileMapPointEmpty(World, TileMap, CanPos.TileX, CanPos.TileY);
|
|
|
|
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, 1, 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);
|
|
|
|
TileMaps[0][0].Tiles = (U32 *)Tiles00;
|
|
TileMaps[0][1].Tiles = (U32 *)Tiles10;
|
|
TileMaps[1][0].Tiles = (U32 *)Tiles01;
|
|
TileMaps[1][1].Tiles = (U32 *)Tiles11;
|
|
|
|
memset(&World, 0, sizeof(World));
|
|
World.TileMapCountX = 2;
|
|
World.TileMapCountY = 2;
|
|
World.CountX = TILEMAP_COUNT_X;
|
|
World.CountY = TILEMAP_COUNT_Y;
|
|
World.TileSideInMeters = 1.4f;
|
|
World.TileSideInPixels = 60;
|
|
World.UpperLeftX = -((F32)World.TileSideInPixels / 2.0f);
|
|
World.UpperLeftY = 0.0f;
|
|
World.TileMaps = (tile_map *)TileMaps;
|
|
|
|
PlayerWidth = 0.75f * (F32)World.TileSideInPixels;
|
|
PlayerHeight = (F32)World.TileSideInPixels;
|
|
|
|
GameState = (game_state *)Memory->PermanentStorage;
|
|
if(!Memory->IsInitialized) {
|
|
GameState->PlayerX = 200.0f;
|
|
GameState->PlayerY = 200.0f;
|
|
|
|
Memory->IsInitialized = TRUE;
|
|
}
|
|
|
|
TileMap = GetTileMap(&World, GameState->PlayerTileMapX,
|
|
GameState->PlayerTileMapY);
|
|
ASSERT(TileMap);
|
|
|
|
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;
|
|
|
|
raw_position PlayerPos, PlayerLeft, PlayerRight;
|
|
|
|
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 *= 200.0f;
|
|
dPlayerY *= 200.0f;
|
|
|
|
NewPlayerX = GameState->PlayerX + Input->dtForFrame*dPlayerX;
|
|
NewPlayerY = GameState->PlayerY + Input->dtForFrame*dPlayerY;
|
|
|
|
PlayerPos.TileMapX = GameState->PlayerTileMapX;
|
|
PlayerPos.TileMapY = GameState->PlayerTileMapY;
|
|
PlayerPos.X = NewPlayerX;
|
|
PlayerPos.Y = NewPlayerY;
|
|
|
|
PlayerLeft = PlayerPos;
|
|
PlayerLeft.X -= 0.5f*PlayerWidth;
|
|
|
|
PlayerRight = PlayerPos;
|
|
PlayerRight.X += 0.5f*PlayerWidth;
|
|
|
|
if(IsWorldPointEmpty(&World, PlayerPos) &&
|
|
IsWorldPointEmpty(&World, PlayerLeft) &&
|
|
IsWorldPointEmpty(&World, PlayerRight))
|
|
{
|
|
cannonical_position CanPos =
|
|
GetCannonicalPosition(&World, PlayerPos);
|
|
|
|
GameState->PlayerTileMapX = CanPos.TileMapX;
|
|
GameState->PlayerTileMapY = CanPos.TileMapY;
|
|
GameState->PlayerX = World.UpperLeftX +
|
|
(F32)World.TileSideInPixels * (F32)CanPos.TileX +
|
|
CanPos.X;
|
|
GameState->PlayerY = World.UpperLeftY +
|
|
(F32)World.TileSideInPixels * (F32)CanPos.TileY +
|
|
CanPos.Y;
|
|
}
|
|
}
|
|
}
|
|
|
|
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(&World, TileMap, Column, Row);
|
|
F32 Gray = 0.5f;
|
|
if(TileID) {
|
|
Gray = 1.0f;
|
|
}
|
|
MinX = World.UpperLeftX +
|
|
(F32)(Column) * (F32)World.TileSideInPixels;
|
|
MinY =
|
|
World.UpperLeftY + (F32)(Row) * (F32)World.TileSideInPixels;
|
|
MaxX = MinX + (F32)World.TileSideInPixels;
|
|
MaxY = MinY + (F32)World.TileSideInPixels;
|
|
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) - 15.0f, (F32)(Input->MouseY) - 15.0f,
|
|
(F32)(Input->MouseX) + 5.0f, (F32)(Input->MouseY) + 5.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;
|
|
}
|
|
}
|
|
*/
|