974 lines
30 KiB
C
974 lines
30 KiB
C
#include "core.h"
|
|
#include "handmade.h"
|
|
#include "handmade_math.h"
|
|
#include "sdl_handmade.h"
|
|
|
|
#include "handmade_intrinsics.h"
|
|
#include "handmade_random.h"
|
|
#include "handmade_tile.h"
|
|
#include "handmade_tile.c"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h> /* memset */
|
|
|
|
static void GameOutputSound(struct game_state *GameState,
|
|
struct game_sound_output_buffer *SoundBuffer,
|
|
int ToneHz)
|
|
{
|
|
/*
|
|
short ToneVolume, *SampleOut;
|
|
int WavePeriod, SampleIndex;
|
|
|
|
ToneVolume = 0;
|
|
WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz;
|
|
|
|
SampleOut = SoundBuffer->Samples;
|
|
|
|
for(SampleIndex = 0;
|
|
SampleIndex < SoundBuffer->SampleCount;
|
|
SampleIndex++)
|
|
{
|
|
float SineValue;
|
|
short SampleValue;
|
|
|
|
SineValue = sinf(GameState->tSine);
|
|
SampleValue = (short)(SineValue * ToneVolume);
|
|
*SampleOut++ = SampleValue;
|
|
*SampleOut++ = SampleValue;
|
|
|
|
GameState->tSine += 2.0f * (float)PI * 1.0f / (float)WavePeriod;
|
|
if(GameState->tSine > 2.0f*(float)PI) {
|
|
GameState->tSine -= 2.0f*(float)PI;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
static void ClearBackground(struct game_offscreen_buffer *Buffer)
|
|
{
|
|
int Width = Buffer->Width;
|
|
int Height = Buffer->Height;
|
|
|
|
int X;
|
|
for(X = 0; X < Width * Height; X++) {
|
|
unsigned int *Pixel = &((unsigned int *)Buffer->Memory)[X];
|
|
*Pixel = 0xFFFFFF;
|
|
}
|
|
}
|
|
|
|
static void DrawRectangle(struct game_offscreen_buffer *Buffer,
|
|
struct v2 vMin, struct v2 vMax,
|
|
float R, float G, float B)
|
|
{
|
|
int Y, X;
|
|
|
|
int Width, Height;
|
|
|
|
int MinX, MinY, MaxX, MaxY;
|
|
|
|
unsigned int Color;
|
|
|
|
unsigned char *Row;
|
|
unsigned int *Pixel;
|
|
|
|
MinX = RoundFloatToInt(vMin.X);
|
|
MinY = RoundFloatToInt(vMin.Y);
|
|
MaxX = RoundFloatToInt(vMax.X);
|
|
MaxY = RoundFloatToInt(vMax.Y);
|
|
|
|
Width = Buffer->Width;
|
|
Height = Buffer->Height;
|
|
|
|
if(MinX < 0)
|
|
MinX = 0;
|
|
if(MinY < 0)
|
|
MinY = 0;
|
|
|
|
if(MaxX > Width)
|
|
MaxX = Width;
|
|
if(MaxY > Height)
|
|
MaxY = Height;
|
|
|
|
/*unsigned int Color = (unsigned int)((RoundFloatToInt(R * 255.0f) << 16) |
|
|
(RoundFloatToInt(G * 255.0f) << 8) |
|
|
(RoundFloatToInt(B * 255.0f) << 0));*/
|
|
Color = (unsigned int)((RoundFloatToInt(255.0f) << 24) |
|
|
(RoundFloatToInt(R * 255.0f) << 16) |
|
|
(RoundFloatToInt(G * 255.0f) << 8) |
|
|
(RoundFloatToInt(B * 255.0f) << 0));
|
|
|
|
Row = ((unsigned char *)Buffer->Memory + MinX*Buffer->BytesPerPixel +
|
|
MinY*Buffer->Pitch);
|
|
for(Y = MinY; Y < MaxY; Y++) {
|
|
Pixel = (unsigned int *)Row;
|
|
for(X = MinX; X < MaxX; X++) {
|
|
*(unsigned int *)Pixel = Color;
|
|
Pixel++;
|
|
}
|
|
Row += Buffer->Pitch;
|
|
}
|
|
}
|
|
|
|
static void DrawBitmap(struct game_offscreen_buffer *Buffer,
|
|
struct loaded_bitmap *Bitmap,
|
|
float RelX, float RelY, int AlignX, int AlignY)
|
|
{
|
|
int MinX, MinY, MaxX, MaxY;
|
|
|
|
int Width, Height;
|
|
|
|
int SourceOffsetX, SourceOffsetY;
|
|
|
|
unsigned int *SourceRow;
|
|
unsigned char *DestRow;
|
|
|
|
int Y, X;
|
|
|
|
RelX -= (float)AlignX;
|
|
RelY -= (float)AlignY;
|
|
|
|
MinX = RoundFloatToInt(RelX);
|
|
MinY = RoundFloatToInt(RelY);
|
|
MaxX = RoundFloatToInt(RelX + (float)Bitmap->Width);
|
|
MaxY = RoundFloatToInt(RelY + (float)Bitmap->Height);
|
|
|
|
Width = Buffer->Width;
|
|
Height = Buffer->Height;
|
|
|
|
SourceOffsetX = 0;
|
|
if(MinX < 0) {
|
|
SourceOffsetX = -MinX;
|
|
MinX = 0;
|
|
}
|
|
|
|
SourceOffsetY = 0;
|
|
if(MinY < 0) {
|
|
SourceOffsetY = -MinY;
|
|
MinY = 0;
|
|
}
|
|
|
|
if(MaxX > Width)
|
|
MaxX = Width;
|
|
if(MaxY > Height)
|
|
MaxY = Height;
|
|
|
|
SourceRow =
|
|
Bitmap->Pixels + Bitmap->Width*(Bitmap->Height - 1);
|
|
SourceRow += -Bitmap->Width*SourceOffsetY + SourceOffsetX;
|
|
DestRow = ((unsigned char *)Buffer->Memory +
|
|
MinX*Buffer->BytesPerPixel + MinY*Buffer->Pitch);
|
|
for(Y = MinY; Y < MaxY; Y++) {
|
|
unsigned int *Dest = (unsigned int *)DestRow;
|
|
unsigned int *Source = SourceRow;
|
|
for(X = MinX; X < MaxX; X++) {
|
|
float SA = (float)((*Source >> 24) & 0xFF);
|
|
float SR = (float)((*Source >> 16) & 0xFF);
|
|
float SG = (float)((*Source >> 8) & 0xFF);
|
|
float SB = (float)((*Source >> 0) & 0xFF);
|
|
|
|
float DR = (float)((*Dest >> 16) & 0xFF);
|
|
float DG = (float)((*Dest >> 8) & 0xFF);
|
|
float DB = (float)((*Dest >> 0) & 0xFF);
|
|
|
|
float A = SA / 255.0f;
|
|
|
|
/* TODO: This should be replaced by premultiplied alpha. */
|
|
float R = (1.0f-A)*DR + A*SR;
|
|
float G = (1.0f-A)*DG + A*SG;
|
|
float B = (1.0f-A)*DB + A*SB;
|
|
|
|
*Dest = (((unsigned int)RoundFloatToInt(SA) << 24) |
|
|
((unsigned int)RoundFloatToInt(R) << 16) |
|
|
((unsigned int)RoundFloatToInt(G) << 8) |
|
|
((unsigned int)RoundFloatToInt(B) << 0));
|
|
|
|
Dest++;
|
|
Source++;
|
|
}
|
|
DestRow += Buffer->Pitch;
|
|
SourceRow -= Bitmap->Width;
|
|
}
|
|
}
|
|
|
|
#pragma pack(push, 1)
|
|
struct bitmap_header {
|
|
unsigned short FileType;
|
|
unsigned int FileSize;
|
|
unsigned short Reserved1;
|
|
unsigned short Reserved2;
|
|
unsigned int BitmapOffset;
|
|
unsigned int Size;
|
|
int Width;
|
|
int Height;
|
|
unsigned short Planes;
|
|
unsigned short BitsPerPixel;
|
|
unsigned int Compression;
|
|
unsigned int SizeOfBitmap;
|
|
int HorzResolution;
|
|
int VertResolution;
|
|
unsigned int ColorsUsed;
|
|
unsigned int ColorsImportant;
|
|
|
|
unsigned int RedMask;
|
|
unsigned int GreenMask;
|
|
unsigned int BlueMask;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
static struct loaded_bitmap DEBUGLoadBMP(struct thread_context *Thread,
|
|
struct debug_read_file_result
|
|
(*DEBUGPlatformReadEntireFile)(struct thread_context *Thread,
|
|
const char *), char *FileName)
|
|
{
|
|
struct loaded_bitmap Result;
|
|
|
|
struct debug_read_file_result ReadResult;
|
|
|
|
memset(&Result, 0, sizeof(Result));
|
|
|
|
ReadResult = DEBUGPlatformReadEntireFile(Thread, FileName);
|
|
if(ReadResult.ContentsSize > 0) {
|
|
struct bitmap_header *Header;
|
|
|
|
unsigned int *Pixels;
|
|
|
|
unsigned int RedMask, GreenMask, BlueMask, AlphaMask;
|
|
|
|
struct bit_scan_result RedShift, GreenShift,
|
|
BlueShift, AlphaShift;
|
|
|
|
unsigned int *SourceDest;
|
|
|
|
int X, Y;
|
|
|
|
Header = (struct bitmap_header *)ReadResult.Contents;
|
|
|
|
Pixels = (unsigned int *)((unsigned char *)ReadResult.Contents +
|
|
Header->BitmapOffset);
|
|
Result.Pixels = Pixels;
|
|
Result.Width = Header->Width;
|
|
Result.Height = Header->Height;
|
|
|
|
ASSERT(Header->Compression == 3);
|
|
|
|
/* NOTE: The byte order in memory is determined by the Header. */
|
|
|
|
RedMask = Header->RedMask;
|
|
GreenMask = Header->GreenMask;
|
|
BlueMask = Header->BlueMask;
|
|
AlphaMask = ~(RedMask | GreenMask | BlueMask);
|
|
|
|
RedShift = FindLeastSignificantSetBit(RedMask);
|
|
GreenShift = FindLeastSignificantSetBit(GreenMask);
|
|
BlueShift = FindLeastSignificantSetBit(BlueMask);
|
|
AlphaShift = FindLeastSignificantSetBit(AlphaMask);
|
|
|
|
ASSERT(RedShift.Found);
|
|
ASSERT(GreenShift.Found);
|
|
ASSERT(BlueShift.Found);
|
|
ASSERT(AlphaShift.Found);
|
|
|
|
SourceDest = Pixels;
|
|
for(Y = 0; Y < Header->Height; Y++) {
|
|
for(X = 0; X < Header->Width; X++) {
|
|
unsigned int C = *SourceDest;
|
|
|
|
*SourceDest = ((((C >> AlphaShift.Index) & 0xFF) << 24) |
|
|
(((C >> RedShift.Index ) & 0xFF) << 16) |
|
|
(((C >> GreenShift.Index) & 0xFF) << 8) |
|
|
(((C >> BlueShift.Index ) & 0xFF) << 0));
|
|
SourceDest++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static struct entity *GetEntity(struct game_state *GameState,
|
|
unsigned int Index)
|
|
{
|
|
struct entity *Entity = NULL;
|
|
|
|
if((Index > 0) && (Index < ARRAY_SIZE(GameState->Entities))) {
|
|
Entity = &GameState->Entities[Index];
|
|
}
|
|
|
|
return Entity;
|
|
}
|
|
|
|
static void InitializePlayer(struct entity *Entity)
|
|
{
|
|
Entity->Exists = 1;
|
|
Entity->P.AbsTileX = 2;
|
|
Entity->P.AbsTileY = 3;
|
|
Entity->P.Offset.X = 5.0f;
|
|
Entity->P.Offset.Y = 5.0f;
|
|
Entity->Height = 1.4f;
|
|
Entity->Width = 0.75f * Entity->Height;
|
|
}
|
|
|
|
static unsigned int AddEntity(struct game_state *GameState)
|
|
{
|
|
unsigned int EntityIndex = ++GameState->EntityCount;
|
|
struct entity *Entity;
|
|
|
|
ASSERT(GameState->EntityCount < ARRAY_SIZE(GameState->Entities));
|
|
Entity = &GameState->Entities[EntityIndex];
|
|
memset(Entity, 0, sizeof(struct entity));
|
|
|
|
return EntityIndex;
|
|
}
|
|
|
|
static void MovePlayer(struct game_state *GameState,
|
|
struct entity *Entity, float dt, struct v2 ddPlayer)
|
|
{
|
|
struct tile_map *TileMap;
|
|
|
|
float PlayerSpeed;
|
|
|
|
struct tile_map_position OldPlayerP, NewPlayerP;
|
|
struct v2 PlayerDelta;
|
|
|
|
TileMap = GameState->World->TileMap;
|
|
|
|
if((ddPlayer.X != 0.0f) && (ddPlayer.Y != 0.0f))
|
|
ddPlayer = V2Multiply(0.7071067811865475244f, ddPlayer);
|
|
|
|
PlayerSpeed = 10.0f;
|
|
ddPlayer = V2Multiply(PlayerSpeed, ddPlayer);
|
|
|
|
ddPlayer = V2Add(ddPlayer, V2Unary(V2Multiply(1.5f, Entity->dP)));
|
|
|
|
OldPlayerP = Entity->P;
|
|
NewPlayerP = OldPlayerP;
|
|
PlayerDelta = V2Add(V2Multiply(0.5f, V2Multiply(Square(dt), ddPlayer)),
|
|
V2Multiply(dt, Entity->dP));
|
|
NewPlayerP.Offset = V2Add(PlayerDelta, NewPlayerP.Offset);
|
|
Entity->dP = V2Add(V2Multiply(dt, ddPlayer), Entity->dP);
|
|
NewPlayerP = RecannonicalizePosition(TileMap, NewPlayerP);
|
|
|
|
{
|
|
#if 1
|
|
struct tile_map_position PlayerLeft, PlayerRight;
|
|
|
|
int Collided;
|
|
|
|
struct tile_map_position ColP;
|
|
|
|
PlayerLeft = NewPlayerP;
|
|
PlayerLeft.Offset.X -= 0.5f*Entity->Width;
|
|
PlayerLeft = RecannonicalizePosition(TileMap, PlayerLeft);
|
|
|
|
PlayerRight = NewPlayerP;
|
|
PlayerRight.Offset.X += 0.5f*Entity->Width;
|
|
PlayerRight = RecannonicalizePosition(TileMap, PlayerRight);
|
|
|
|
Collided = 0;
|
|
memset(&ColP, 0, sizeof(ColP));
|
|
if(!IsTileMapPointEmpty(TileMap, NewPlayerP)) {
|
|
ColP = NewPlayerP;
|
|
Collided = 1;
|
|
}
|
|
if(!IsTileMapPointEmpty(TileMap, PlayerLeft)) {
|
|
ColP = PlayerLeft;
|
|
Collided = 1;
|
|
}
|
|
if(!IsTileMapPointEmpty(TileMap, PlayerRight)) {
|
|
ColP = PlayerRight;
|
|
Collided = 1;
|
|
}
|
|
|
|
if(Collided) {
|
|
struct v2 r = { 0, 0 };
|
|
if(ColP.AbsTileX < Entity->P.AbsTileX) {
|
|
r.X = 1;
|
|
r.Y = 0;
|
|
}
|
|
if(ColP.AbsTileX > Entity->P.AbsTileX) {
|
|
r.X = -1;
|
|
r.Y = 0;
|
|
}
|
|
if(ColP.AbsTileY < Entity->P.AbsTileY) {
|
|
r.X = 0;
|
|
r.Y = 1;
|
|
}
|
|
if(ColP.AbsTileY > Entity->P.AbsTileY) {
|
|
r.X = 0;
|
|
r.Y = -1;
|
|
}
|
|
|
|
Entity->dP = V2Subtract(Entity->dP,
|
|
V2Multiply(1*InnerProduct(Entity->dP, r), r));
|
|
} else {
|
|
Entity->P = NewPlayerP;
|
|
}
|
|
#else
|
|
unsigned int MinTileX = 0;
|
|
unsigned int MinTileY = 0;
|
|
unsigned int OnePastMaxTileX = 0;
|
|
unsigned int OnePastMaxTileY = 0;
|
|
unsigned int AbsTileZ = GameState->PlayerP.AbsTileZ;
|
|
tile_map_position BestPlayerP = GameState->PlayerP;
|
|
F32 BestDistance = LengthSq(PlayerDelta);
|
|
for(unsigned int AbsTileY = MinTileY;
|
|
AbsTileY != OnePastMaxTileY;
|
|
AbsTileY++)
|
|
{
|
|
for(unsigned int AbsTileX = MinTileX;
|
|
AbsTileX != OnePastMaxTileX;
|
|
AbsTileX++)
|
|
{
|
|
tile_map_position TestTileP =
|
|
CenteredTilePoint(AbsTileX, AbsTileY, AbsTileZ);
|
|
unsigned int TileValue = GetTileValueByPos(TileMap, TestTileP);
|
|
if(IsTileValueEmpty(TileValue)) {
|
|
v2 MinCorner = V2Multiply(-0.5f,
|
|
(v2){ TileMap->TileSideInMeters,
|
|
TileMap->TileSideInMeters });
|
|
v2 MaxCorner = V2Multiply(-0.5f,
|
|
(v2){ TileMap->TileSideInMeters,
|
|
TileMap->TileSideInMeters });
|
|
|
|
tile_map_difference RelNewPlayerP =
|
|
SubtractInF32(TileMap, &TestTileP, &NewPlayerP);
|
|
v2 TestP = ClosestPointInRectangle(MinCorner, MaxCorner,
|
|
RelNewPlayerP);
|
|
if(...) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(!AreOnSameTile(&OldPlayerP, &Entity->P)) {
|
|
unsigned int NewTileValue = GetTileValueByPos(TileMap, Entity->P);
|
|
|
|
if(NewTileValue == 3) {
|
|
Entity->P.AbsTileZ++;
|
|
} else if(NewTileValue == 4) {
|
|
Entity->P.AbsTileZ--;
|
|
}
|
|
}
|
|
|
|
if(AbsoluteValue(ddPlayer.X) > AbsoluteValue(ddPlayer.Y)) {
|
|
if(ddPlayer.X > 0) {
|
|
Entity->FacingDirection = 0;
|
|
} else {
|
|
Entity->FacingDirection = 2;
|
|
}
|
|
} else if(AbsoluteValue(ddPlayer.X) < AbsoluteValue(ddPlayer.Y)) {
|
|
if(ddPlayer.Y > 0) {
|
|
Entity->FacingDirection = 1;
|
|
} else {
|
|
Entity->FacingDirection = 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateAndRender(struct thread_context *Thread,
|
|
struct game_memory *Memory, struct game_input *Input,
|
|
struct game_offscreen_buffer *Buffer,
|
|
struct game_sound_output_buffer *SoundBuffer)
|
|
{
|
|
struct game_state *GameState;
|
|
|
|
struct world *World;
|
|
struct tile_map *TileMap;
|
|
|
|
int TileSideInPixels;
|
|
float MetersToPixels;
|
|
|
|
unsigned int ControllerIndex;
|
|
|
|
struct entity *CameraFollowingEntity;
|
|
|
|
float ScreenCenterX, ScreenCenterY;
|
|
|
|
int RelRow, RelColumn;
|
|
|
|
struct entity *Entity;
|
|
|
|
unsigned int EntityIndex;
|
|
|
|
ASSERT((&Input->Controllers[0].u.s.Terminator -
|
|
&Input->Controllers[0].u.Buttons[0]) ==
|
|
ARRAY_SIZE(Input->Controllers[0].u.Buttons));
|
|
ASSERT(sizeof(struct game_state) <= Memory->PermanentStorageSize);
|
|
|
|
GameState = (struct game_state *)Memory->PermanentStorage;
|
|
if(!Memory->IsInitialized) {
|
|
struct hero_bitmaps *Bitmap;
|
|
struct world *World;
|
|
struct tile_map *TileMap;
|
|
|
|
unsigned int RandomNumberIndex;
|
|
|
|
unsigned int TilesPerWidth, TilesPerHeight;
|
|
unsigned int ScreenX, ScreenY;
|
|
|
|
unsigned int AbsTileZ;
|
|
|
|
int DoorTop, DoorBottom, DoorLeft, DoorRight, DoorUp, DoorDown;
|
|
|
|
unsigned int ScreenIndex;
|
|
|
|
/* NOTE: Reserve entity slot 0 for null entity */
|
|
AddEntity(GameState);
|
|
|
|
GameState->rect_pos.X = 0.0f;
|
|
GameState->rect_pos.Y = 0.0f;
|
|
|
|
GameState->ProgramIcon.Width = 256;
|
|
GameState->ProgramIcon.Height = 256;
|
|
GameState->ProgramIcon.BytesPerPixel = 4;
|
|
GameState->ProgramIcon.Pitch = GameState->ProgramIcon.BytesPerPixel *
|
|
GameState->ProgramIcon.Width;
|
|
GameState->ProgramIcon.Memory =
|
|
malloc(GameState->ProgramIcon.BytesPerPixel *
|
|
GameState->ProgramIcon.Width *
|
|
GameState->ProgramIcon.Height);
|
|
memset(GameState->ProgramIcon.Memory, 0,
|
|
GameState->ProgramIcon.BytesPerPixel *
|
|
GameState->ProgramIcon.Width *
|
|
GameState->ProgramIcon.Height);
|
|
|
|
#if 0
|
|
GameState->ProgramIconBMP = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile, "data/program_icon.bmp");
|
|
DrawBitmap(&GameState->ProgramIcon, &GameState->ProgramIconBMP,
|
|
0.0f, 0.0f, 0, 0);
|
|
Memory->SetProgramIcon(&GameState->ProgramIcon);
|
|
#endif
|
|
|
|
GameState->Backdrop = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile, "data/test_background.bmp");
|
|
|
|
Bitmap = &GameState->HeroBitmaps[0];
|
|
Bitmap->AlignX = 72;
|
|
Bitmap->AlignY = 182;
|
|
Bitmap->Head = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_right_head.bmp");
|
|
Bitmap->Cape = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_right_cape.bmp");
|
|
Bitmap->Torso = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_right_torso.bmp");
|
|
Bitmap++;
|
|
|
|
Bitmap->AlignX = 72;
|
|
Bitmap->AlignY = 182;
|
|
Bitmap->Head = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_back_head.bmp");
|
|
Bitmap->Cape = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_back_cape.bmp");
|
|
Bitmap->Torso = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_back_torso.bmp");
|
|
Bitmap++;
|
|
|
|
Bitmap->AlignX = 72;
|
|
Bitmap->AlignY = 182;
|
|
Bitmap->Head = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_left_head.bmp");
|
|
Bitmap->Cape = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_left_cape.bmp");
|
|
Bitmap->Torso = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_left_torso.bmp");
|
|
Bitmap++;
|
|
|
|
Bitmap->AlignX = 72;
|
|
Bitmap->AlignY = 182;
|
|
Bitmap->Head = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_front_head.bmp");
|
|
Bitmap->Cape = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_front_cape.bmp");
|
|
Bitmap->Torso = DEBUGLoadBMP(Thread,
|
|
Memory->DEBUGPlatformReadEntireFile,
|
|
"data/test_hero_front_torso.bmp");
|
|
|
|
GameState->CameraP.AbsTileX = 17 / 2;
|
|
GameState->CameraP.AbsTileY = 9 / 2;
|
|
|
|
InitializeArena(&GameState->WorldArena,
|
|
Memory->PermanentStorageSize - sizeof(struct game_state),
|
|
(unsigned char *)Memory->PermanentStorage +
|
|
sizeof(struct game_state));
|
|
|
|
GameState->World = PUSH_STRUCT(&GameState->WorldArena, struct world);
|
|
World = GameState->World;
|
|
World->TileMap = PUSH_STRUCT(&GameState->WorldArena, struct 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,
|
|
struct tile_chunk);
|
|
|
|
TileMap->TileSideInMeters = 1.4f;
|
|
|
|
RandomNumberIndex = 0;
|
|
|
|
TilesPerWidth = 17;
|
|
TilesPerHeight = 9;
|
|
ScreenX = 0;
|
|
ScreenY = 0;
|
|
|
|
AbsTileZ = 0;
|
|
|
|
DoorTop = 0;
|
|
DoorBottom = 0;
|
|
DoorLeft = 0;
|
|
DoorRight = 0;
|
|
DoorUp = 0;
|
|
DoorDown = 0;
|
|
for(ScreenIndex = 0; ScreenIndex < 100; ScreenIndex++) {
|
|
|
|
/* TODO: Random number generator. */
|
|
unsigned int RandomChoice;
|
|
|
|
int CreatedZDoor;
|
|
|
|
unsigned int TileY, TileX;
|
|
|
|
ASSERT(RandomNumberIndex < ARRAY_SIZE(RandomNumberTable));
|
|
|
|
if(DoorUp || DoorDown) {
|
|
RandomChoice = RandomNumberTable[RandomNumberIndex++] % 2;
|
|
} else {
|
|
RandomChoice = RandomNumberTable[RandomNumberIndex++] % 3;
|
|
}
|
|
|
|
CreatedZDoor = 0;
|
|
|
|
if(RandomChoice == 2) {
|
|
CreatedZDoor = 1;
|
|
|
|
if(AbsTileZ == 0) {
|
|
DoorUp = 1;
|
|
} else {
|
|
DoorDown = 1;
|
|
}
|
|
} else if(RandomChoice == 1) {
|
|
DoorRight = 1;
|
|
} else {
|
|
DoorTop = 1;
|
|
}
|
|
|
|
for(TileY = 0; TileY < TilesPerHeight; TileY++) {
|
|
for(TileX = 0; TileX < TilesPerWidth; TileX++) {
|
|
unsigned int AbsTileX = ScreenX * TilesPerWidth + TileX;
|
|
unsigned int AbsTileY = ScreenY * TilesPerHeight + TileY;
|
|
|
|
unsigned int 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 = 0;
|
|
DoorDown = 0;
|
|
}
|
|
|
|
DoorTop = 0;
|
|
DoorRight = 0;
|
|
|
|
if(RandomChoice == 2) {
|
|
if(AbsTileZ == 0) {
|
|
AbsTileZ = 1;
|
|
} else {
|
|
AbsTileZ = 0;
|
|
}
|
|
} else if(RandomChoice == 1) {
|
|
ScreenX++;
|
|
} else {
|
|
ScreenY++;
|
|
}
|
|
}
|
|
|
|
Memory->IsInitialized = 1;
|
|
}
|
|
|
|
World = GameState->World;
|
|
TileMap = World->TileMap;
|
|
|
|
TileSideInPixels = 60;
|
|
MetersToPixels = (float)TileSideInPixels / TileMap->TileSideInMeters;
|
|
|
|
for(ControllerIndex = 0;
|
|
ControllerIndex < ARRAY_SIZE(Input->Controllers);
|
|
ControllerIndex++)
|
|
{
|
|
struct game_controller_input *Controller;
|
|
struct entity *ControllingEntity;
|
|
|
|
Controller = GetController(Input, ControllerIndex);
|
|
ControllingEntity = GetEntity(GameState,
|
|
GameState->PlayerIndexForController[ControllerIndex]);
|
|
if(ControllingEntity) {
|
|
struct v2 ddPlayer; /* accerlation */
|
|
memset(&ddPlayer, 0, sizeof(ddPlayer));
|
|
|
|
if(Controller->IsAnalog) {
|
|
ddPlayer.X = Controller->StickAverageX;
|
|
ddPlayer.Y = -Controller->StickAverageY;
|
|
} else {
|
|
if(Controller->u.s.MoveUp.EndedDown) {
|
|
GameState->rect_pos.Y -= 1.0f;
|
|
|
|
ddPlayer.Y = 1.0f;
|
|
}
|
|
if(Controller->u.s.MoveDown.EndedDown) {
|
|
GameState->rect_pos.Y += 1.0f;
|
|
|
|
ddPlayer.Y = -1.0f;
|
|
}
|
|
if(Controller->u.s.MoveLeft.EndedDown) {
|
|
GameState->rect_pos.X -= 1.0f;
|
|
|
|
ddPlayer.X = -1.0f;
|
|
}
|
|
if(Controller->u.s.MoveRight.EndedDown) {
|
|
GameState->rect_pos.X += 1.0f;
|
|
|
|
ddPlayer.X = 1.0f;
|
|
}
|
|
}
|
|
|
|
MovePlayer(GameState, ControllingEntity, Input->dtForFrame,
|
|
ddPlayer);
|
|
} else {
|
|
if(Controller->u.s.Start.EndedDown) {
|
|
unsigned int EntityIndex = AddEntity(GameState);
|
|
ControllingEntity = GetEntity(GameState, EntityIndex);
|
|
InitializePlayer(ControllingEntity);
|
|
GameState->PlayerIndexForController[ControllerIndex] =
|
|
EntityIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
CameraFollowingEntity =
|
|
GetEntity(GameState, GameState->CameraFollowingEntityIndex);
|
|
if(CameraFollowingEntity) {
|
|
struct tile_map_difference Diff;
|
|
|
|
GameState->CameraP.AbsTileZ = CameraFollowingEntity->P.AbsTileZ;
|
|
|
|
Diff = SubtractInFloat(TileMap, &CameraFollowingEntity->P,
|
|
&GameState->CameraP);
|
|
if(Diff.dXY.X > (9.0f*TileMap->TileSideInMeters)) {
|
|
GameState->CameraP.AbsTileX += 17;
|
|
}
|
|
if(Diff.dXY.X < -(9.0f*TileMap->TileSideInMeters)) {
|
|
GameState->CameraP.AbsTileX -= 17;
|
|
}
|
|
if(Diff.dXY.Y > (5.0f*TileMap->TileSideInMeters)) {
|
|
GameState->CameraP.AbsTileY += 9;
|
|
}
|
|
if(Diff.dXY.Y < -(5.0f*TileMap->TileSideInMeters)) {
|
|
GameState->CameraP.AbsTileY -= 9;
|
|
}
|
|
}
|
|
|
|
/*DrawRectangle(Buffer, 0.0f, 0.0f,
|
|
(F32)Buffer->Width, (F32)Buffer->Height,
|
|
0.0f, 1.0f, 0.0f);*/
|
|
|
|
DrawBitmap(Buffer, &GameState->Backdrop, 0.0f, 0.0f, 0, 0);
|
|
|
|
ScreenCenterX = 0.5f * (float)Buffer->Width;
|
|
ScreenCenterY = 0.5f * (float)Buffer->Height;
|
|
|
|
for(RelRow = -10; RelRow < 10; RelRow++) {
|
|
for(RelColumn = -20; RelColumn < 20; RelColumn++) {
|
|
unsigned int Column = GameState->CameraP.AbsTileX + RelColumn;
|
|
unsigned int Row = GameState->CameraP.AbsTileY + RelRow;
|
|
|
|
unsigned int TileID = GetTileValue(TileMap, Column, Row,
|
|
GameState->CameraP.AbsTileZ);
|
|
|
|
if(TileID > 1) {
|
|
struct v2 TileSide, Center;
|
|
|
|
struct v2 Min, Max;
|
|
|
|
float Gray = 0.5f;
|
|
|
|
if(TileID == 2) {
|
|
Gray = 1.0f;
|
|
}
|
|
|
|
if(TileID > 2) {
|
|
Gray = 0.25f;
|
|
}
|
|
|
|
if(Column == GameState->CameraP.AbsTileX &&
|
|
Row == GameState->CameraP.AbsTileY)
|
|
{
|
|
Gray = 0.0f;
|
|
}
|
|
|
|
TileSide.X = 0.5f*(float)TileSideInPixels;
|
|
TileSide.Y = 0.5f*(float)TileSideInPixels;
|
|
Center.X = ScreenCenterX -
|
|
MetersToPixels*GameState->CameraP.Offset.X +
|
|
(float)RelColumn*(float)TileSideInPixels;
|
|
Center.Y = ScreenCenterY + MetersToPixels *
|
|
GameState->CameraP.Offset.Y -
|
|
(float)RelRow*(float)TileSideInPixels;
|
|
Min = V2Subtract(Center, TileSide);
|
|
Max = V2Add(Center, TileSide);
|
|
DrawRectangle(Buffer, Min, Max, Gray, Gray, Gray);
|
|
}
|
|
}
|
|
}
|
|
|
|
Entity = GameState->Entities;
|
|
|
|
for(EntityIndex = 0;
|
|
EntityIndex < GameState->EntityCount;
|
|
EntityIndex++, Entity++)
|
|
{
|
|
if(Entity->Exists) {
|
|
struct tile_map_difference Diff;
|
|
|
|
float PlayerR, PlayerG, PlayerB;
|
|
float PlayerGroundPointX, PlayerGroundPointY;
|
|
struct v2 PlayerLeftTop, PlayerWidthHeight;
|
|
|
|
struct hero_bitmaps *Bitmap;
|
|
|
|
Diff = SubtractInFloat(TileMap, &Entity->P, &GameState->CameraP);
|
|
|
|
PlayerR = 1.0f;
|
|
PlayerG = 0.0f;
|
|
PlayerB = 0.0f;
|
|
PlayerGroundPointX = ScreenCenterX + MetersToPixels*Diff.dXY.X;
|
|
PlayerGroundPointY = ScreenCenterY - MetersToPixels*Diff.dXY.Y;
|
|
|
|
PlayerLeftTop.X =
|
|
PlayerGroundPointX - 0.5f * MetersToPixels*Entity->Width;
|
|
PlayerLeftTop.Y =
|
|
PlayerGroundPointY - MetersToPixels*Entity->Height;
|
|
|
|
PlayerWidthHeight.X = Entity->Width;
|
|
PlayerWidthHeight.X = Entity->Height;
|
|
|
|
DrawRectangle(Buffer, PlayerLeftTop,
|
|
V2Add(PlayerLeftTop, V2Multiply(MetersToPixels,
|
|
PlayerWidthHeight)),
|
|
PlayerR, PlayerG, PlayerB);
|
|
|
|
Bitmap = &GameState->HeroBitmaps[Entity->FacingDirection];
|
|
DrawBitmap(Buffer, &Bitmap->Torso,
|
|
PlayerGroundPointX, PlayerGroundPointY,
|
|
Bitmap->AlignX, Bitmap->AlignY);
|
|
DrawBitmap(Buffer, &Bitmap->Cape,
|
|
PlayerGroundPointX, PlayerGroundPointY,
|
|
Bitmap->AlignX, Bitmap->AlignY);
|
|
DrawBitmap(Buffer, &Bitmap->Head,
|
|
PlayerGroundPointX, PlayerGroundPointY,
|
|
Bitmap->AlignX, Bitmap->AlignY);
|
|
|
|
/* TODO: Remove this. */
|
|
/*DrawRectangle(&GameState->ProgramIcon, GameState->rect_pos,
|
|
(struct v2){ GameState->rect_pos.X + 20.0f,
|
|
GameState->rect_pos.Y + 20.0f },
|
|
0.8f, 0.8f, 0.8f);*/
|
|
/*Memory->SetProgramIcon(&GameState->ProgramIcon);*/
|
|
}
|
|
}
|
|
|
|
GameOutputSound(GameState, SoundBuffer, 400);
|
|
}
|
|
|
|
/*
|
|
static void RenderWeirdGradient(game_offscreen_buffer *Buffer,
|
|
int XOffset, int YOffset)
|
|
{
|
|
int Width, Height, Y;
|
|
U8 *Row;
|
|
|
|
Width = Buffer->Width;
|
|
Height = Buffer->Height;
|
|
|
|
Row = (U8 *)Buffer->Memory;
|
|
for(Y = 0; Y < Height; Y++) {
|
|
unsigned int *Pixel = (unsigned int *)Row;
|
|
int X;
|
|
|
|
Pixel = (unsigned int *)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;
|
|
}
|
|
}
|
|
*/
|