#include "core.h" #include "handmade.h" #include "sdl_handmade.h" #include "handmade_intrinsics.h" #include "handmade_random.h" #include "handmade_tile.h" #include "handmade_tile.c" #include /* 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 = Buffer->Width; S32 Height = Buffer->Height; for(S32 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; if(MinX < 0) MinX = 0; if(MinY < 0) MinY = 0; if(MaxX > Width) MaxX = Width; if(MaxY > Height) MaxY = Height; U32 Color = (U32)((RoundF32toS32(R * 255.0f) << 16) | (RoundF32toS32(G * 255.0f) << 8) | (RoundF32toS32(B * 255.0f) << 0)); U8 *Row = ((U8 *)Buffer->Memory + MinX*Buffer->BytesPerPixel + MinY*Buffer->Pitch); for(S32 Y = MinY; Y < MaxY; Y++) { U32 *Pixel = (U32 *)Row; for(S32 X = MinX; X < MaxX; X++) { *(U32 *)Pixel = Color; Pixel++; } Row += Buffer->Pitch; } } #pragma pack(push, 1) typedef struct bitmap_header { U16 FileType; U32 FileSize; U16 Reserved1; U16 Reserved2; U32 BitmapOffset; U32 Size; S32 Width; S32 Height; U16 Planes; U16 BitsPerPixel; } bitmap_header; #pragma pack(pop) static U32 *LoadBMP(thread_context *Thread, debug_read_file_result (*DEBUGPlatformReadEntireFile)(thread_context *Thread, const char *), char *FileName) { U32 *Result = 0; debug_read_file_result ReadResult = DEBUGPlatformReadEntireFile(Thread, FileName); if(ReadResult.ContentsSize > 0) { bitmap_header *Header = (bitmap_header *)ReadResult.Contents; U32 *Pixels = (U32 *)((U8 *)ReadResult.Contents + Header->BitmapOffset); Result = Pixels; } return Result; } void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer, game_sound_output_buffer *SoundBuffer) { ASSERT((&Input->Controllers[0].Terminator - &Input->Controllers[0].Buttons[0]) == ARRAY_SIZE(Input->Controllers[0].Buttons)); ASSERT(sizeof(game_state) <= Memory->PermanentStorageSize); #define TILEMAP_COUNT_X 256 #define TILEMAP_COUNT_Y 256 game_state *GameState; F32 PlayerHeight = 1.4f; F32 PlayerWidth = 0.75f * PlayerHeight; GameState = (game_state *)Memory->PermanentStorage; if(!Memory->IsInitialized) { GameState->PixelPointer = LoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_background.bmp"); GameState->PlayerP.AbsTileX = 2; GameState->PlayerP.AbsTileY = 3; GameState->PlayerP.OffsetX = 5.0f; GameState->PlayerP.OffsetY = 5.0f; InitializeArena(&GameState->WorldArena, Memory->PermanentStorageSize - sizeof(game_state), (U8 *)Memory->PermanentStorage + sizeof(game_state)); GameState->World = PUSH_STRUCT(&GameState->WorldArena, world); world *World = GameState->World; World->TileMap = PUSH_STRUCT(&GameState->WorldArena, tile_map); tile_map *TileMap = World->TileMap; TileMap->ChunkShift = 4; TileMap->ChunkMask = (1 << TileMap->ChunkShift) - 1; TileMap->ChunkDim = (1 << TileMap->ChunkShift); TileMap->TileChunkCountX = 128; TileMap->TileChunkCountY = 128; TileMap->TileChunkCountZ = 2; TileMap->TileChunks = PUSH_ARRAY(&GameState->WorldArena, TileMap->TileChunkCountX * TileMap->TileChunkCountY * TileMap->TileChunkCountZ, tile_chunk); TileMap->TileSideInMeters = 1.4f; U32 RandomNumberIndex = 0; U32 TilesPerWidth = 17; U32 TilesPerHeight = 9; U32 ScreenX = 0; U32 ScreenY = 0; U32 AbsTileZ = 0; B32 DoorTop = FALSE; B32 DoorBottom = FALSE; B32 DoorLeft = FALSE; B32 DoorRight = FALSE; B32 DoorUp = FALSE; B32 DoorDown = FALSE; for(U32 ScreenIndex = 0; ScreenIndex < 100; ScreenIndex++) { ASSERT(RandomNumberIndex < ARRAY_SIZE(RandomNumberTable)); /* TODO: Random number generator. */ U32 RandomChoice; if(DoorUp || DoorDown) { RandomChoice = RandomNumberTable[RandomNumberIndex++] % 2; } else { RandomChoice = RandomNumberTable[RandomNumberIndex++] % 3; } B32 CreatedZDoor = FALSE; if(RandomChoice == 2) { CreatedZDoor = TRUE; if(AbsTileZ == 0) { DoorUp = TRUE; } else { DoorDown = TRUE; } } else if(RandomChoice == 1) { DoorRight = TRUE; } else { DoorTop = TRUE; } for(U32 TileY = 0; TileY < TilesPerHeight; TileY++) { for(U32 TileX = 0; TileX < TilesPerWidth; TileX++) { U32 AbsTileX = ScreenX * TilesPerWidth + TileX; U32 AbsTileY = ScreenY * TilesPerHeight + TileY; U32 TileValue = 1; if((TileX == 0) && (!DoorLeft || (TileY != (TilesPerHeight/2)))) { TileValue = 2; } if(((TileX == (TilesPerWidth - 1)) && (!DoorRight || (TileY != (TilesPerHeight/2))))) { TileValue = 2; } if((TileY == 0) && (!DoorBottom || (TileX != (TilesPerWidth/2)))) { TileValue = 2; } if((TileY == (TilesPerHeight - 1)) && (!DoorTop || (TileX != (TilesPerWidth/2)))) { TileValue = 2; } if((TileX == 10) && (TileY == 6)) { if(DoorUp) { TileValue = 3; } if(DoorDown) { TileValue = 4; } } SetTileValue(&GameState->WorldArena, World->TileMap, AbsTileX, AbsTileY, AbsTileZ, TileValue); } } DoorLeft = DoorRight; DoorBottom = DoorTop; if(CreatedZDoor) { DoorDown = !DoorDown; DoorUp = !DoorUp; } else { DoorUp = FALSE; DoorDown = FALSE; } DoorTop = FALSE; DoorRight = FALSE; if(RandomChoice == 2) { if(AbsTileZ == 0) { AbsTileZ = 1; } else { AbsTileZ = 0; } } else if(RandomChoice == 1) { ScreenX++; } else { ScreenY++; } } Memory->IsInitialized = TRUE; } world *World = GameState->World; tile_map *TileMap = World->TileMap; S32 TileSideInPixels = 60; F32 MetersToPixels = (F32)TileSideInPixels / TileMap->TileSideInMeters; for(U32 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; if(Controller->MoveUp.EndedDown) { dPlayerY = 1.0f; } if(Controller->MoveDown.EndedDown) { dPlayerY = -1.0f; } if(Controller->MoveLeft.EndedDown) { dPlayerX = -1.0f; } if(Controller->MoveRight.EndedDown) { dPlayerX = 1.0f; } F32 PlayerSpeed = 2.0f; if(Controller->ActionUp.EndedDown) PlayerSpeed = 10.0f; dPlayerX *= PlayerSpeed; dPlayerY *= PlayerSpeed; tile_map_position NewPlayerP; NewPlayerP = GameState->PlayerP; NewPlayerP.OffsetX += Input->dtForFrame*dPlayerX; NewPlayerP.OffsetY += Input->dtForFrame*dPlayerY; NewPlayerP = RecannonicalizePosition(TileMap, NewPlayerP); tile_map_position PlayerLeft; PlayerLeft = NewPlayerP; PlayerLeft.OffsetX -= 0.5f*PlayerWidth; PlayerLeft = RecannonicalizePosition(TileMap, PlayerLeft); tile_map_position PlayerRight; PlayerRight = NewPlayerP; PlayerRight.OffsetX += 0.5f*PlayerWidth; PlayerRight = RecannonicalizePosition(TileMap, PlayerRight); if(IsTileMapPointEmpty(TileMap, NewPlayerP) && IsTileMapPointEmpty(TileMap, PlayerLeft) && IsTileMapPointEmpty(TileMap, PlayerRight)) { if(!AreOnSameTile(&GameState->PlayerP, &NewPlayerP)) { U32 NewTileValue = GetTileValueByPos(TileMap, NewPlayerP); if(NewTileValue == 3) { NewPlayerP.AbsTileZ++; } else if(NewTileValue == 4) { NewPlayerP.AbsTileZ--; } } GameState->PlayerP = NewPlayerP; } } } F32 ScreenCenterX = 0.5f * (F32)Buffer->Width; F32 ScreenCenterY = 0.5f * (F32)Buffer->Height; DrawRectangle(Buffer, 0.0f, 0.0f, (F32)Buffer->Width, (F32)Buffer->Height, 0.0f, 1.0f, 0.0f); for(S32 RelRow = -10; RelRow < 10; RelRow++) { for(S32 RelColumn = -20; RelColumn < 20; RelColumn++) { U32 Column = GameState->PlayerP.AbsTileX + RelColumn; U32 Row = GameState->PlayerP.AbsTileY + RelRow; U32 TileID = GetTileValue(TileMap, Column, Row, GameState->PlayerP.AbsTileZ); if(TileID > 0) { F32 Gray = 0.5f; if(TileID == 2) { Gray = 1.0f; } if(TileID > 2) { Gray = 0.25f; } if((Column == GameState->PlayerP.AbsTileX) && (Row == GameState->PlayerP.AbsTileY)) { Gray = 0.0f; } F32 CenterX = ScreenCenterX - MetersToPixels*GameState->PlayerP.OffsetX + (F32)RelColumn*(F32)TileSideInPixels; F32 CenterY = ScreenCenterY + MetersToPixels*GameState->PlayerP.OffsetY - (F32)RelRow*(F32)TileSideInPixels; F32 MinX = CenterX - 0.5f*(F32)TileSideInPixels; F32 MinY = CenterY - 0.5f*(F32)TileSideInPixels; F32 MaxX = CenterX + 0.5f*(F32)TileSideInPixels; F32 MaxY = CenterY + 0.5f*(F32)TileSideInPixels; DrawRectangle(Buffer, MinX, MinY, MaxX, MaxY, Gray, Gray, Gray); } } } F32 PlayerR = 1.0f; F32 PlayerG = 0.0f; F32 PlayerB = 0.0f; F32 PlayerLeft = ScreenCenterX - 0.5f * MetersToPixels*PlayerWidth; F32 PlayerTop = ScreenCenterY - MetersToPixels*PlayerHeight; DrawRectangle(Buffer, PlayerLeft, PlayerTop, PlayerLeft + MetersToPixels*PlayerWidth, PlayerTop + MetersToPixels*PlayerHeight, PlayerR, PlayerG, PlayerB); #if 0 U32 *Source = GameState->PixelPointer; U32 *Dest = (U32 *)Buffer->Memory; for(S32 Y = 0; Y < Buffer->Height; Y++) { for(S32 X = 0; X < Buffer->Width; X++) { *Dest++ = *Source++; } } #endif DrawRectangle(Buffer, (F32)Input->MouseX-14.0f, (F32)Input->MouseY-14.0f, (F32)(Input->MouseX)+2.0f, (F32)(Input->MouseY)+2.0f, 1.0f, 0.0f, 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; } } */