fp: Merge TELY_Game and FP_Game

This commit is contained in:
doyle 2023-09-17 20:24:07 +10:00
parent 9abaf8fd5f
commit 6d33b18364
5 changed files with 328 additions and 384 deletions

View File

@ -47,12 +47,11 @@ void TELY_DLL_Init(void *user_data)
// NOTE: TELY Game =============================================================================
TELY_Assets *assets = &platform->assets;
Feely_Pona *pona = Dqn_Arena_New(&platform->arena, Feely_Pona, Dqn_ZeroMem_Yes);
TELY_Game *game = &pona->game;
FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes);
game->chunk_pool.arena = &platform->arena;
platform->user_data = game;
{
TELY_AssetSpriteSheet *sheet = &pona->hero_sprite_sheet;
TELY_AssetSpriteSheet *sheet = &game->hero_sprite_sheet;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/adventurer-v1.5-sheet.png", DQN_STRING_FMT(assets->textures_dir));
sheet->tex_handle = platform->func_load_texture(assets, DQN_STRING8("Hero"), sheet_path);
@ -83,11 +82,11 @@ void TELY_DLL_Init(void *user_data)
{DQN_STRING8("Leap slice C"), /*index*/ 103, /*count*/ 6},
};
pona->hero_sprite_anims = Dqn_Slice_Alloc<TELY_AssetSpriteAnimation>(&platform->arena, DQN_ARRAY_UCOUNT(hero_anims), Dqn_ZeroMem_No);
DQN_MEMCPY(pona->hero_sprite_anims.data, &hero_anims, sizeof(hero_anims[0]) * DQN_ARRAY_UCOUNT(hero_anims));
game->hero_sprite_anims = Dqn_Slice_Alloc<TELY_AssetSpriteAnimation>(&platform->arena, DQN_ARRAY_UCOUNT(hero_anims), Dqn_ZeroMem_No);
DQN_MEMCPY(game->hero_sprite_anims.data, &hero_anims, sizeof(hero_anims[0]) * DQN_ARRAY_UCOUNT(hero_anims));
}
game->entities = Dqn_VArray_Init<TELY_GameEntity>(&platform->arena, 1024 * 8);
game->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
game->root_entity = Dqn_VArray_Make(&game->entities, 1, Dqn_ZeroMem_No);
Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle);
@ -95,32 +94,32 @@ void TELY_DLL_Init(void *user_data)
{
// NOTE: Setup entity-tree =================================================================
TELY_GameEntity *f = TELY_Game_MakeEntityPointerF(game, "F");
TELY_Game_PushParentEntity(game, f->handle);
TELY_GameEntity *b = TELY_Game_MakeEntityPointerF(game, "B");
TELY_GameEntity *g = TELY_Game_MakeEntityPointerF(game, "G");
TELY_Game_PushParentEntity(game, b->handle);
TELY_GameEntity *a = TELY_Game_MakeEntityPointerF(game, "A");
TELY_GameEntity *d = TELY_Game_MakeEntityPointerF(game, "D");
TELY_Game_PushParentEntity(game, d->handle);
TELY_GameEntity *c = TELY_Game_MakeEntityPointerF(game, "C");
TELY_GameEntity *e = TELY_Game_MakeEntityPointerF(game, "E");
TELY_Game_PopParentEntity(game);
TELY_Game_PopParentEntity(game);
FP_GameEntity *f = FP_Game_MakeEntityPointerF(game, "F");
FP_Game_PushParentEntity(game, f->handle);
FP_GameEntity *b = FP_Game_MakeEntityPointerF(game, "B");
FP_GameEntity *g = FP_Game_MakeEntityPointerF(game, "G");
FP_Game_PushParentEntity(game, b->handle);
FP_GameEntity *a = FP_Game_MakeEntityPointerF(game, "A");
FP_GameEntity *d = FP_Game_MakeEntityPointerF(game, "D");
FP_Game_PushParentEntity(game, d->handle);
FP_GameEntity *c = FP_Game_MakeEntityPointerF(game, "C");
FP_GameEntity *e = FP_Game_MakeEntityPointerF(game, "E");
FP_Game_PopParentEntity(game);
FP_Game_PopParentEntity(game);
TELY_Game_PushParentEntity(game, g->handle);
TELY_GameEntity *i = TELY_Game_MakeEntityPointerF(game, "I");
TELY_Game_PushParentEntity(game, i->handle);
TELY_GameEntity *h = TELY_Game_MakeEntityPointerF(game, "H");
TELY_Game_PopParentEntity(game);
TELY_Game_PopParentEntity(game);
TELY_Game_PopParentEntity(game);
FP_Game_PushParentEntity(game, g->handle);
FP_GameEntity *i = FP_Game_MakeEntityPointerF(game, "I");
FP_Game_PushParentEntity(game, i->handle);
FP_GameEntity *h = FP_Game_MakeEntityPointerF(game, "H");
FP_Game_PopParentEntity(game);
FP_Game_PopParentEntity(game);
FP_Game_PopParentEntity(game);
// NOTE: Pre order test ====================================================================
TELY_GameEntity *pre_order_walk[9] = {};
FP_GameEntity *pre_order_walk[9] = {};
Dqn_usize pre_order_walk_count = 0;
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity);) {
for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity);) {
DQN_ASSERT(pre_order_walk_count < DQN_ARRAY_UCOUNT(pre_order_walk));
pre_order_walk[pre_order_walk_count++] = it.entity;
}
@ -138,9 +137,9 @@ void TELY_DLL_Init(void *user_data)
// NOTE: Post order test ===================================================================
TELY_GameEntity *post_order_walk[9] = {};
FP_GameEntity *post_order_walk[9] = {};
Dqn_usize post_order_walk_count = 0;
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity);) {
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity);) {
DQN_ASSERT(post_order_walk_count < DQN_ARRAY_UCOUNT(post_order_walk));
post_order_walk[post_order_walk_count++] = it.entity;
}
@ -157,7 +156,7 @@ void TELY_DLL_Init(void *user_data)
DQN_ASSERT(post_order_walk[8] == f);
// NOTE: Cleanup ===========================================================================
TELY_Game_DeleteEntity(game, game->root_entity->handle);
FP_Game_DeleteEntity(game, game->root_entity->handle);
DQN_ASSERT(game->root_entity->first_child == nullptr);
DQN_ASSERT(game->root_entity->last_child == nullptr);
DQN_ASSERT(game->root_entity->next == nullptr);
@ -167,65 +166,60 @@ void TELY_DLL_Init(void *user_data)
// NOTE: Hero
{
TELY_GameEntity *hero = TELY_Game_MakeEntityPointerF(game, "Hero");
FP_GameEntity *hero = FP_Game_MakeEntityPointerF(game, "Hero");
hero->local_pos = Dqn_V2_InitNx2(100.f, 100.f);
hero->size_scale = Dqn_V2_InitNx1(4);
hero->sprite_sheet = &pona->hero_sprite_sheet;
hero->sprite_anims = pona->hero_sprite_anims;
hero->local_hit_box_size = Dqn_V2_InitV2I(pona->hero_sprite_sheet.sprite_size);
hero->flags |= TELY_EntityFlag_Clickable;
hero->flags |= TELY_EntityFlag_MoveByKeyboard;
hero->flags |= TELY_EntityFlag_MoveByMouse;
hero->sprite_sheet = &game->hero_sprite_sheet;
hero->sprite_anims = game->hero_sprite_anims;
hero->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size);
hero->flags |= FP_EntityFlag_Clickable;
hero->flags |= FP_EntityFlag_MoveByKeyboard;
hero->flags |= FP_EntityFlag_MoveByMouse;
game->clicked_entity = hero->handle;
}
// NOTE: Enemy
{
TELY_GameEntity *enemy = TELY_Game_MakeEntityPointerF(game, "Enemy");
FP_GameEntity *enemy = FP_Game_MakeEntityPointerF(game, "Enemy");
enemy->local_pos = Dqn_V2_InitNx2(300.f, 300.f);
enemy->size_scale = Dqn_V2_InitNx1(4);
enemy->sprite_sheet = &pona->hero_sprite_sheet;
enemy->sprite_anims = pona->hero_sprite_anims;
enemy->local_hit_box_size = Dqn_V2_InitV2I(pona->hero_sprite_sheet.sprite_size);
enemy->flags |= TELY_EntityFlag_Clickable;
enemy->flags |= TELY_EntityFlag_MoveByKeyboard;
enemy->flags |= TELY_EntityFlag_MoveByMouse;
enemy->sprite_sheet = &game->hero_sprite_sheet;
enemy->sprite_anims = game->hero_sprite_anims;
enemy->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size);
enemy->flags |= FP_EntityFlag_Clickable;
enemy->flags |= FP_EntityFlag_MoveByKeyboard;
enemy->flags |= FP_EntityFlag_MoveByMouse;
}
// NOTE: Wall
{
TELY_GameEntity *entity = TELY_Game_MakeEntityPointerF(game, "V. Wall");
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "V. Wall");
entity->local_pos = Dqn_V2_InitNx2(100.f, 300.f);
entity->local_hit_box_size = Dqn_V2_InitNx2(100.f, 300.f);
entity->flags |= TELY_EntityFlag_Clickable;
entity->flags |= TELY_EntityFlag_MoveByKeyboard;
entity->flags |= TELY_EntityFlag_MoveByMouse;
entity->flags |= FP_EntityFlag_Clickable;
entity->flags |= FP_EntityFlag_MoveByKeyboard;
entity->flags |= FP_EntityFlag_MoveByMouse;
TELY_GameShape *wall = Dqn_FArray_Make(&entity->shapes, Dqn_ZeroMem_Yes);
wall->type = TELY_GameShapeType_Rect;
FP_GameShape *wall = Dqn_FArray_Make(&entity->shapes, Dqn_ZeroMem_Yes);
wall->type = FP_GameShapeType_Rect;
wall->p2 = entity->local_hit_box_size;
wall->colour = TELY_COLOUR_GREEN_DARK_KHAKI_V4;
}
uint16_t font_size = 18;
game->camera.scale = Dqn_V2_InitNx1(1);
pona->inter_regular_font = platform->func_load_font(assets, DQN_STRING8("Inter (Regular)"), DQN_STRING8("Data/Inter-Regular.otf"), font_size);
pona->inter_italic_font = platform->func_load_font(assets, DQN_STRING8("Inter (Italic)"), DQN_STRING8("Data/Inter-Italic.otf"), font_size);
pona->jetbrains_mono_font = platform->func_load_font(assets, DQN_STRING8("JetBrains Mono NL (Regular)"), DQN_STRING8("Data/JetBrainsMonoNL-Regular.ttf"), font_size);
pona->test_audio = platform->func_load_audio(assets, DQN_STRING8("Test Audio"), DQN_STRING8("Data/Audio/Purrple Cat - Moonwinds.qoa"));
game->inter_regular_font = platform->func_load_font(assets, DQN_STRING8("Inter (Regular)"), DQN_STRING8("Data/Inter-Regular.otf"), font_size);
game->inter_italic_font = platform->func_load_font(assets, DQN_STRING8("Inter (Italic)"), DQN_STRING8("Data/Inter-Italic.otf"), font_size);
game->jetbrains_mono_font = platform->func_load_font(assets, DQN_STRING8("JetBrains Mono NL (Regular)"), DQN_STRING8("Data/JetBrainsMonoNL-Regular.ttf"), font_size);
game->test_audio = platform->func_load_audio(assets, DQN_STRING8("Test Audio"), DQN_STRING8("Data/Audio/Purrple Cat - Moonwinds.qoa"));
// NOTE: TELY audio ============================================================================
TELY_Audio *audio = &platform->audio;
audio->chunk_pool = &game->chunk_pool;
// NOTE: TELY ui ===============================================================================
TELY_UI *ui = &game->ui;
ui->arena.allocs_are_allowed_to_leak = true;
}
void TELY_Game_EntityChangeState(TELY_GameEntity *entity, TELY_GameEntityState state)
void FP_Game_EntityChangeState(FP_GameEntity *entity, FP_GameEntityState state)
{
if (entity->state == state)
return;
@ -237,18 +231,18 @@ void TELY_Game_EntityChangeState(TELY_GameEntity *entity, TELY_GameEntityState s
// decouple the state from the animation, e.g. the wall doesn't have an animation
uint16_t desired_sprite_anim_index = 0;
switch (state) {
case TELY_GameEntityState_Nil: {
case FP_GameEntityState_Nil: {
} break;
case TELY_GameEntityState_Idle: {
case FP_GameEntityState_Idle: {
desired_sprite_anim_index = TELY_Asset_GetSpriteAnim(entity->sprite_anims, DQN_STRING8("Idle"));
} break;
case TELY_GameEntityState_Attack: {
case FP_GameEntityState_Attack: {
desired_sprite_anim_index = TELY_Asset_GetSpriteAnim(entity->sprite_anims, DQN_STRING8("Attack A"));
} break;
case TELY_GameEntityState_Run: {
case FP_GameEntityState_Run: {
desired_sprite_anim_index = TELY_Asset_GetSpriteAnim(entity->sprite_anims, DQN_STRING8("Running"));
} break;
}
@ -366,7 +360,7 @@ Dqn_Slice<Dqn_V2I> AStarPathFind(Dqn_Arena *arena, TELY_Platform *platform, Dqn_
return result;
}
void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
void FP_GameUpdate(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
{
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
game->clicked_entity = game->prev_active_entity;
@ -383,19 +377,19 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
if (game->clicked_entity.id) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete))
TELY_Game_DeleteEntity(game, game->clicked_entity);
FP_Game_DeleteEntity(game, game->clicked_entity);
TELY_GameEntity *player = TELY_Game_GetEntity(game, game->clicked_entity);
FP_GameEntity *player = FP_Game_GetEntity(game, game->clicked_entity);
if (player) {
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_J)) {
TELY_Game_EntityChangeState(player, TELY_GameEntityState_Attack);
FP_Game_EntityChangeState(player, FP_GameEntityState_Attack);
}
if (player->state != TELY_GameEntityState_Attack) {
if (player->state != FP_GameEntityState_Attack) {
if (dir_vector.x || dir_vector.y) {
TELY_Game_EntityChangeState(player, TELY_GameEntityState_Run);
FP_Game_EntityChangeState(player, FP_GameEntityState_Run);
} else {
TELY_Game_EntityChangeState(player, TELY_GameEntityState_Idle);
FP_Game_EntityChangeState(player, FP_GameEntityState_Idle);
}
}
}
@ -403,36 +397,36 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
game->camera.world_pos += dir_vector * 5.f;
}
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
TELY_GameEntity *entity = it.entity;
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
FP_GameEntity *entity = it.entity;
entity->alive_time_s += input->delta_s;
// NOTE: Move entity by keyboard ===========================================================
Dqn_V2 acceleration = {};
if (game->clicked_entity == entity->handle) {
if (entity->flags & TELY_EntityFlag_MoveByKeyboard) {
if (entity->flags & FP_EntityFlag_MoveByKeyboard) {
acceleration = dir_vector * 10000000.f;
if (dir_vector.x)
entity->facing_left = dir_vector.x < 0.f;
}
}
Dqn_V2 entity_world_pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
if (entity->name == DQN_STRING8("Enemy")) {
if (entity->handle != game->clicked_entity && entity->stalk_entity != game->clicked_entity) {
entity->stalk_entity = game->clicked_entity;
}
TELY_GameEntity *stalk_entity = TELY_Game_GetEntity(game, entity->stalk_entity);
FP_GameEntity *stalk_entity = FP_Game_GetEntity(game, entity->stalk_entity);
if (stalk_entity) {
Dqn_V2 stalk_world_pos = TELY_Game_CalcEntityWorldPos(game, stalk_entity->handle);
Dqn_V2 stalk_world_pos = FP_Game_CalcEntityWorldPos(game, stalk_entity->handle);
Dqn_V2I stalk_tile = Dqn_V2I_InitNx2(stalk_world_pos.x / FP_TILE_SIZE, stalk_world_pos.y / FP_TILE_SIZE);
if (entity->stalk_entity_last_known_tile != stalk_tile) {
entity->stalk_entity_last_known_tile = stalk_tile;
// NOTE: Dealloc all waypoints
for (TELY_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; ) {
TELY_GameWaypoint *next = waypoint->next;
for (FP_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; ) {
FP_GameWaypoint *next = waypoint->next;
TELY_ChunkPool_Dealloc(&game->chunk_pool, waypoint);
waypoint = next;
}
@ -443,7 +437,7 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
Dqn_Slice<Dqn_V2I> path_find = AStarPathFind(&platform->arena, platform, entity_tile, stalk_tile);
for (Dqn_usize index = path_find.size - 1; index < path_find.size; index--) {
TELY_GameWaypoint *waypoint = TELY_ChunkPool_New(&game->chunk_pool, TELY_GameWaypoint);
FP_GameWaypoint *waypoint = TELY_ChunkPool_New(&game->chunk_pool, FP_GameWaypoint);
waypoint->pos = path_find.data[index];
waypoint->next = entity->waypoints;
waypoint->prev = entity->waypoints->prev;
@ -454,13 +448,13 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
}
}
for (TELY_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; waypoint = waypoint->next) {
for (FP_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; waypoint = waypoint->next) {
Dqn_V2 circle_pos = Dqn_V2_InitNx2(waypoint->pos.x * FP_TILE_SIZE + FP_TILE_SIZE * .5f, waypoint->pos.y * FP_TILE_SIZE + FP_TILE_SIZE * .5f);
TELY_Render_CircleColourV4(renderer, circle_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4);
}
if (entity->waypoints->next != entity->waypoints) {
TELY_GameWaypoint *waypoint = entity->waypoints->next;
FP_GameWaypoint *waypoint = entity->waypoints->next;
Dqn_V2I target_tile = entity->waypoints->next->pos;
Dqn_V2 target_pos = Dqn_V2_InitNx2(target_tile.x * FP_TILE_SIZE + FP_TILE_SIZE *.5f, target_tile.y * FP_TILE_SIZE + FP_TILE_SIZE * .5f);
Dqn_V2 entity_to_target_pos = target_pos - entity_world_pos;
@ -487,42 +481,42 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
Dqn_V2 delta_p = (acceleration * 0.5f * t_squared) + (entity->velocity * t);
entity->local_pos += delta_p;
Dqn_Rect world_hit_box = TELY_Game_CalcEntityWorldHitBox(game, entity->handle);
Dqn_Rect world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
Dqn_Rect new_world_hit_box = world_hit_box;
new_world_hit_box.pos += delta_p;
for (TELY_GameEntityIterator collider_it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) {
TELY_GameEntity *collider = collider_it.entity;
for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) {
FP_GameEntity *collider = collider_it.entity;
if (collider->handle == entity->handle)
continue;
// TODO(doyle): Minkowski sweep?
Dqn_Rect collider_world_hit_box = TELY_Game_CalcEntityWorldHitBox(game, collider->handle);
Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle);
if (Dqn_Rect_Intersects(new_world_hit_box, collider_world_hit_box)) {
}
}
}
// NOTE: Move entity by mouse ==============================================================
if (game->active_entity == entity->handle && entity->flags & TELY_EntityFlag_MoveByMouse) {
if (entity->flags & TELY_EntityFlag_MoveByMouse) {
if (game->active_entity == entity->handle && entity->flags & FP_EntityFlag_MoveByMouse) {
if (entity->flags & FP_EntityFlag_MoveByMouse) {
entity->velocity = {};
entity->local_pos += input->mouse_p_delta;
}
}
if (entity->flags & TELY_EntityFlag_DeriveHitBoxFromChildrenBoundingBox) {
if (entity->flags & FP_EntityFlag_DeriveHitBoxFromChildrenBoundingBox) {
Dqn_Rect children_bbox = {};
// TODO(doyle): Is the hit box supposed to include the containing
// entity itself? Not sure
children_bbox.pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
children_bbox.pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
for (TELY_GameEntityIterator child_it = {}; TELY_Game_DFSPreOrderWalkEntityTree(game, &child_it, entity);) {
TELY_GameEntity *child = child_it.entity;
for (FP_GameEntityIterator child_it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &child_it, entity);) {
FP_GameEntity *child = child_it.entity;
DQN_ASSERT(child != entity);
Dqn_Rect bbox = TELY_Game_CalcEntityWorldBoundingBox(game, child->handle);
Dqn_Rect bbox = FP_Game_CalcEntityWorldBoundingBox(game, child->handle);
children_bbox = Dqn_Rect_Union(children_bbox, bbox);
}
@ -550,22 +544,22 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
if (action_is_done) {
switch (entity->state) {
case TELY_GameEntityState_Nil: break;
case TELY_GameEntityState_Idle: break;
case TELY_GameEntityState_Attack: {
TELY_Game_EntityChangeState(entity, TELY_GameEntityState_Idle);
case FP_GameEntityState_Nil: break;
case FP_GameEntityState_Idle: break;
case FP_GameEntityState_Attack: {
FP_Game_EntityChangeState(entity, FP_GameEntityState_Idle);
} break;
case TELY_GameEntityState_Run: {
case FP_GameEntityState_Run: {
if (dir_vector.x == 0 && dir_vector.y == 0)
TELY_Game_EntityChangeState(entity, TELY_GameEntityState_Idle);
FP_Game_EntityChangeState(entity, FP_GameEntityState_Idle);
} break;
}
}
}
// NOTE: Calculate entity attack box =======================================================
if (entity->state == TELY_GameEntityState_Attack) {
if (entity->state == FP_GameEntityState_Attack) {
entity->attack_box_size = entity->local_hit_box_size;
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
@ -587,18 +581,18 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
}
// NOTE: Do attacks ============================================================================
for (TELY_GameEntityIterator attacker_it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &attacker_it, game->root_entity); ) {
TELY_GameEntity *attacker = attacker_it.entity;
for (FP_GameEntityIterator attacker_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &attacker_it, game->root_entity); ) {
FP_GameEntity *attacker = attacker_it.entity;
// NOTE: Resolve attack boxes
if (Dqn_V2_Area(attacker->attack_box_size)) {
Dqn_Rect attacker_box = TELY_Game_CalcEntityAttackWorldHitBox(game, attacker->handle);
for (TELY_GameEntityIterator defender_it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->root_entity); ) {
TELY_GameEntity *defender = defender_it.entity;
Dqn_Rect attacker_box = FP_Game_CalcEntityAttackWorldHitBox(game, attacker->handle);
for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->root_entity); ) {
FP_GameEntity *defender = defender_it.entity;
if (defender->handle == attacker->handle)
continue;
Dqn_Rect defender_box = TELY_Game_CalcEntityWorldHitBox(game, defender->handle);
Dqn_Rect defender_box = FP_Game_CalcEntityWorldHitBox(game, defender->handle);
Dqn_Rect hit_rect = Dqn_Rect_Intersection(attacker_box, defender_box);
if (!Dqn_Rect_Area(hit_rect))
@ -624,10 +618,10 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
}
}
void FP_GameRender(TELY_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
void FP_GameRender(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
{
TELY_PlatformInput *input = &platform->input;
Dqn_M2x3 model_view = TELY_Game_CameraModelViewM2x3(game->camera, platform);
Dqn_M2x3 model_view = FP_Game_CameraModelViewM2x3(game->camera, platform);
Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(model_view, input->mouse_p);
// NOTE: Draw tiles ============================================================================
@ -647,31 +641,31 @@ void FP_GameRender(TELY_Game *game, TELY_Platform *platform, TELY_Renderer *rend
}
// NOTE: Draw entities =========================================================================
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
TELY_GameEntity *entity = it.entity;
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
FP_GameEntity *entity = it.entity;
entity->alive_time_s += input->delta_s;
// NOTE: Render shapes in entity ===========================================================
Dqn_V2 world_pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
for (TELY_GameShape const &shape_ : entity->shapes) {
TELY_GameShape const *shape = &shape_;
Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
for (FP_GameShape const &shape_ : entity->shapes) {
FP_GameShape const *shape = &shape_;
Dqn_V2 local_to_world_p1 = world_pos + shape->p1;
Dqn_V2 local_to_world_p2 = world_pos + shape->p2;
switch (shape->type) {
case TELY_GameShapeType_None: {
case FP_GameShapeType_None: {
} break;
case TELY_GameShapeType_Circle: {
case FP_GameShapeType_Circle: {
TELY_Render_CircleColourV4(renderer, local_to_world_p1, shape->circle_radius, shape->render_mode, shape->colour);
} break;
case TELY_GameShapeType_Rect: {
case FP_GameShapeType_Rect: {
Dqn_Rect rect = Dqn_Rect_InitV2x2(local_to_world_p1, local_to_world_p2 - local_to_world_p1);
rect.pos -= rect.size * .5f;
TELY_Render_RectColourV4(renderer, rect, shape->render_mode, shape->colour);
} break;
case TELY_GameShapeType_Line: {
case FP_GameShapeType_Line: {
TELY_Render_LineColourV4(renderer, local_to_world_p1, local_to_world_p2, shape->colour, shape->line_thickness);
} break;
}
@ -704,7 +698,7 @@ void FP_GameRender(TELY_Game *game, TELY_Platform *platform, TELY_Renderer *rend
// NOTE: Render attack box =================================================================
{
Dqn_Rect attack_box = TELY_Game_CalcEntityAttackWorldHitBox(game, entity->handle);
Dqn_Rect attack_box = FP_Game_CalcEntityAttackWorldHitBox(game, entity->handle);
TELY_Render_RectColourV4(renderer, attack_box, TELY_RenderShapeMode_Line, TELY_COLOUR_RED_TOMATO_V4);
}
@ -712,10 +706,10 @@ void FP_GameRender(TELY_Game *game, TELY_Platform *platform, TELY_Renderer *rend
TELY_Render_CircleColourV4(renderer, world_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_RED_TOMATO_V4);
// NOTE: Render hot/active entity ==========================================================
Dqn_Rect world_hit_box = TELY_Game_CalcEntityWorldHitBox(game, entity->handle);
Dqn_Rect world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
if (game->clicked_entity == entity->handle) {
TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, TELY_COLOUR_WHITE_PALE_GOLDENROD_V4);
} else if (game->hot_entity == entity->handle || (entity->flags & TELY_EntityFlag_DrawHitBox)) {
} else if (game->hot_entity == entity->handle || (entity->flags & FP_EntityFlag_DrawHitBox)) {
Dqn_V4 hot_colour = game->hot_entity == entity->handle ? TELY_COLOUR_RED_TOMATO_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_YELLOW_SANDY_V4, .5f);
TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, hot_colour);
@ -724,7 +718,7 @@ void FP_GameRender(TELY_Game *game, TELY_Platform *platform, TELY_Renderer *rend
if (game->clicked_entity == entity->handle || game->hot_entity == entity->handle) {
if (entity->name.size) {
Dqn_V2I player_tile = Dqn_V2I_InitNx2(world_pos.x / FP_TILE_SIZE, world_pos.y / FP_TILE_SIZE);
Dqn_V2 entity_world_pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 label = Dqn_String8_InitF(scratch.allocator,
"%.*s (%.1f, %.1f) (%I32d, %I32d)",
@ -746,15 +740,10 @@ void TELY_DLL_FrameUpdate(void *user_data)
TELY_PlatformInput *input = &platform->input;
TELY_Assets *assets = &platform->assets;
TELY_Renderer *renderer = &platform->renderer;
Feely_Pona *pona = DQN_CAST(Feely_Pona *) platform->user_data;
TELY_Game *game = &pona->game;
TELY_UI *ui = &game->ui;
TELY_UI_FrameSetup(ui, assets, &platform->frame_arena);
TELY_UI_PushFont(ui, pona->jetbrains_mono_font);
FP_Game *game = DQN_CAST(FP_Game *) platform->user_data;
TELY_Render_ClearColourV3(renderer, TELY_COLOUR_BLACK_MIDNIGHT_V4.rgb);
TELY_Render_PushFont(renderer, pona->jetbrains_mono_font);
TELY_Render_PushFont(renderer, game->jetbrains_mono_font);
{
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8Builder builder = {};
@ -797,14 +786,14 @@ void TELY_DLL_FrameUpdate(void *user_data)
Dqn_FArray_Clear(&game->parent_entity_stack);
Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle);
Dqn_M2x3 model_view = TELY_Game_CameraModelViewM2x3(game->camera, platform);
Dqn_M2x3 model_view = FP_Game_CameraModelViewM2x3(game->camera, platform);
Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(model_view, input->mouse_p);
// =============================================================================================
TELY_Audio *audio = &platform->audio;
if (audio->playback_size == 0) {
TELY_Audio_Play(audio, pona->test_audio, 1.f /*volume*/);
TELY_Audio_Play(audio, game->test_audio, 1.f /*volume*/);
}
// =============================================================================================
@ -813,15 +802,15 @@ void TELY_DLL_FrameUpdate(void *user_data)
if (game->prev_active_entity.id)
game->active_entity = game->prev_active_entity;
} else {
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity); ) {
TELY_GameEntity *entity = it.entity;
for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity); ) {
FP_GameEntity *entity = it.entity;
if (entity->local_hit_box_size.x <= 0 || entity->local_hit_box_size.y <= 0)
continue;
if ((entity->flags & TELY_EntityFlag_Clickable) == 0)
if ((entity->flags & FP_EntityFlag_Clickable) == 0)
continue;
Dqn_Rect world_hit_box = TELY_Game_CalcEntityWorldHitBox(game, entity->handle);
Dqn_Rect world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
if (!Dqn_Rect_ContainsPoint(world_hit_box, world_mouse_p))
continue;

View File

@ -3,13 +3,140 @@
#include "feely_pona_unity.h"
#endif
struct Feely_Pona
enum FP_EntityFlag
{
TELY_Game game;
TELY_AssetFontHandle inter_regular_font;
TELY_AssetFontHandle inter_italic_font;
TELY_AssetFontHandle jetbrains_mono_font;
Dqn_Slice<TELY_AssetSpriteAnimation> hero_sprite_anims;
TELY_AssetSpriteSheet hero_sprite_sheet;
TELY_AssetAudioHandle test_audio;
FP_EntityFlag_Clickable = 1 << 0,
FP_EntityFlag_MoveByKeyboard = 1 << 1,
FP_EntityFlag_MoveByMouse = 1 << 2,
FP_EntityFlag_DrawHitBox = 1 << 3,
FP_EntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 4,
};
enum FP_GameShapeType
{
FP_GameShapeType_None,
FP_GameShapeType_Circle,
FP_GameShapeType_Rect,
FP_GameShapeType_Line,
};
struct FP_GameShape
{
FP_GameShapeType type;
Dqn_V2 p1;
Dqn_V2 p2;
Dqn_V4 colour;
Dqn_f32 line_thickness;
Dqn_f32 circle_radius;
TELY_RenderShapeMode render_mode;
};
const uint64_t FP_GAME_ENTITY_HANDLE_GENERATION_MASK = 0xFFFF'0000'0000'0000;
const uint64_t FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT = 48;
const uint64_t FP_GAME_ENTITY_HANDLE_GENERATION_MAX = FP_GAME_ENTITY_HANDLE_GENERATION_MASK >> FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT;
const uint64_t FP_GAME_ENTITY_HANDLE_INDEX_MASK = 0x0000'FFFF'FFFF'FFFF;
const uint64_t FP_GAME_ENTITY_HANDLE_INDEX_MAX = FP_GAME_ENTITY_HANDLE_INDEX_MASK - 1;
struct FP_GameEntityHandle
{
uint64_t id;
};
enum FP_GameEntityState
{
FP_GameEntityState_Nil,
FP_GameEntityState_Idle,
FP_GameEntityState_Attack,
FP_GameEntityState_Run,
};
struct FP_GameEntityAnimation
{
uint16_t index;
uint16_t frame;
uint16_t ticks;
uint16_t ticks_per_frame;
};
struct FP_GameWaypoint
{
Dqn_V2I pos;
FP_GameWaypoint *next;
FP_GameWaypoint *prev;
};
struct FP_GameEntity
{
Dqn_String8 name;
FP_GameEntityHandle handle;
TELY_AssetSpriteSheet *sprite_sheet;
Dqn_Slice<TELY_AssetSpriteAnimation> sprite_anims;
Dqn_V2 size_scale;
FP_GameEntityAnimation anim;
FP_GameEntityState state;
Dqn_V2 velocity;
FP_GameEntityHandle stalk_entity;
Dqn_V2I stalk_entity_last_known_tile;
FP_GameWaypoint *waypoints;
// NOTE: The entity hit box is positioned at the center of the entity.
Dqn_V2 local_hit_box_size;
Dqn_V2 local_hit_box_offset;
Dqn_V2 attack_box_size;
Dqn_V2 attack_box_offset;
uint64_t flags;
bool facing_left;
Dqn_V2 local_pos;
Dqn_f64 alive_time_s;
Dqn_FArray<FP_GameShape, 4> shapes;
FP_GameEntity *next;
FP_GameEntity *prev;
FP_GameEntity *first_child;
FP_GameEntity *last_child;
FP_GameEntity *parent;
};
struct FP_GameEntityIterator
{
bool init;
Dqn_usize iteration_count;
FP_GameEntity *entity;
FP_GameEntity *last_visited;
FP_GameEntity *entity_parent;
FP_GameEntity *entity_next;
FP_GameEntity *entity_first_child;
};
struct FP_GameCamera
{
Dqn_V2 world_pos;
Dqn_f32 rotate_rads;
Dqn_V2 scale;
};
struct FP_Game
{
TELY_ChunkPool chunk_pool;
TELY_AssetFontHandle inter_regular_font;
TELY_AssetFontHandle inter_italic_font;
TELY_AssetFontHandle jetbrains_mono_font;
TELY_AssetAudioHandle test_audio;
Dqn_Slice<TELY_AssetSpriteAnimation> hero_sprite_anims;
TELY_AssetSpriteSheet hero_sprite_sheet;
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
Dqn_VArray<FP_GameEntity> entities;
FP_GameEntity *root_entity;
FP_GameEntity *entity_free_list;
FP_GameEntityHandle clicked_entity;
FP_GameEntityHandle hot_entity;
FP_GameEntityHandle active_entity;
FP_GameEntityHandle prev_clicked_entity;
FP_GameEntityHandle prev_hot_entity;
FP_GameEntityHandle prev_active_entity;
FP_GameCamera camera;
};

View File

@ -3,19 +3,19 @@
#include "playground_unity.h"
#endif
static bool operator==(TELY_GameEntityHandle const &lhs, TELY_GameEntityHandle const &rhs)
static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs)
{
bool result = lhs.id == rhs.id;
return result;
}
static bool operator!=(TELY_GameEntityHandle const &lhs, TELY_GameEntityHandle const &rhs)
static bool operator!=(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs)
{
bool result = !(lhs == rhs);
return result;
}
static Dqn_M2x3 TELY_Game_CameraModelViewM2x3(TELY_GameCamera camera, TELY_Platform *platform)
static Dqn_M2x3 FP_Game_CameraModelViewM2x3(FP_GameCamera camera, TELY_Platform *platform)
{
Dqn_M2x3 result = Dqn_M2x3_Identity();
if (platform) {
@ -28,24 +28,24 @@ static Dqn_M2x3 TELY_Game_CameraModelViewM2x3(TELY_GameCamera camera, TELY_Platf
return result;
}
static TELY_GameEntity *TELY_Game_GetEntity(TELY_Game *game, TELY_GameEntityHandle handle)
static FP_GameEntity *FP_Game_GetEntity(FP_Game *game, FP_GameEntityHandle handle)
{
TELY_GameEntity *result = nullptr;
FP_GameEntity *result = nullptr;
if (!game)
return result;
result = game->entities.data;
uint64_t index_from_handle = handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK;
uint64_t index_from_handle = handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK;
if (index_from_handle >= game->entities.size)
return result;
TELY_GameEntity *candidate = game->entities.data + index_from_handle;
FP_GameEntity *candidate = game->entities.data + index_from_handle;
if (candidate->handle == handle)
result = candidate;
return result;
}
static bool TELY_Game_DFSPreOrderWalkEntityTree(TELY_Game *game, TELY_GameEntityIterator *it, TELY_GameEntity *root)
static bool FP_Game_DFSPreOrderWalkEntityTree(FP_Game *game, FP_GameEntityIterator *it, FP_GameEntity *root)
{
if (!game || !it || !root)
return false;
@ -88,7 +88,7 @@ static bool TELY_Game_DFSPreOrderWalkEntityTree(TELY_Game *game, TELY_GameEntity
return it->entity->handle != root->handle;
}
static bool TELY_Game_DFSPostOrderWalkEntityTree(TELY_Game *game, TELY_GameEntityIterator *it, TELY_GameEntity *root)
static bool FP_Game_DFSPostOrderWalkEntityTree(FP_Game *game, FP_GameEntityIterator *it, FP_GameEntity *root)
{
if (!game || !it || !root)
return false;
@ -149,39 +149,39 @@ static bool TELY_Game_DFSPostOrderWalkEntityTree(TELY_Game *game, TELY_GameEntit
// NOTE: Parent entity
static void TELY_Game_PushParentEntity(TELY_Game *game, TELY_GameEntityHandle handle)
static void FP_Game_PushParentEntity(FP_Game *game, FP_GameEntityHandle handle)
{
DQN_ASSERTF(game->parent_entity_stack.size >= 1, "Sentinel/nil entity has not been assigned as the 0th slot yet");
if (game)
Dqn_FArray_Add(&game->parent_entity_stack, handle);
}
static void TELY_Game_PopParentEntity(TELY_Game *game)
static void FP_Game_PopParentEntity(FP_Game *game)
{
// NOTE: 0th slot is reserved for the nil entity
if (game && game->parent_entity_stack.size > 1)
Dqn_FArray_PopBack(&game->parent_entity_stack, 1);
}
static TELY_GameEntityHandle TELY_Game_ActiveParentEntity(TELY_Game const *game)
static FP_GameEntityHandle FP_Game_ActiveParentEntity(FP_Game const *game)
{
TELY_GameEntityHandle result = {};
FP_GameEntityHandle result = {};
if (!game || !game->parent_entity_stack.size)
return result;
result = game->parent_entity_stack.data[game->parent_entity_stack.size - 1];
return result;
}
static TELY_GameEntity *TELY_Game_ActiveParentEntityPointer(TELY_Game const *game)
static FP_GameEntity *FP_Game_ActiveParentEntityPointer(FP_Game const *game)
{
TELY_GameEntityHandle handle = TELY_Game_ActiveParentEntity(game);
TELY_GameEntity *result = TELY_Game_GetEntity(DQN_CAST(TELY_Game *)game, handle);
FP_GameEntityHandle handle = FP_Game_ActiveParentEntity(game);
FP_GameEntity *result = FP_Game_GetEntity(DQN_CAST(FP_Game *)game, handle);
return result;
}
static TELY_GameEntity *TELY_Game_MakeEntityPointerFV(TELY_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
TELY_GameEntity *result = nullptr;
FP_GameEntity *result = nullptr;
if (!game)
return result;
@ -194,24 +194,24 @@ static TELY_GameEntity *TELY_Game_MakeEntityPointerFV(TELY_Game *game, DQN_FMT_S
game->entity_free_list = game->entity_free_list->next;
result->next = nullptr;
} else {
if (game->entities.size >= (TELY_GAME_ENTITY_HANDLE_INDEX_MAX + 1))
if (game->entities.size >= (FP_GAME_ENTITY_HANDLE_INDEX_MAX + 1))
return result;
result = Dqn_VArray_Make(&game->entities, 1, Dqn_ZeroMem_Yes);
if (!result)
return result;
result->handle.id = (game->entities.size - 1) & TELY_GAME_ENTITY_HANDLE_INDEX_MASK;
result->handle.id = (game->entities.size - 1) & FP_GAME_ENTITY_HANDLE_INDEX_MASK;
}
result->size_scale = Dqn_V2_InitNx1(1);
result->parent = TELY_Game_ActiveParentEntityPointer(game);
result->parent = FP_Game_ActiveParentEntityPointer(game);
result->name = TELY_ChunkPool_AllocFmtFV(&game->chunk_pool, fmt, args);
result->waypoints = TELY_ChunkPool_New(&game->chunk_pool, TELY_GameWaypoint);
result->waypoints = TELY_ChunkPool_New(&game->chunk_pool, FP_GameWaypoint);
result->waypoints->next = result->waypoints;
result->waypoints->prev = result->waypoints;
// NOTE: Attach entity as a child to the parent
TELY_GameEntity *parent = result->parent;
FP_GameEntity *parent = result->parent;
if (parent->first_child)
parent->last_child->next = result;
else
@ -225,38 +225,38 @@ static TELY_GameEntity *TELY_Game_MakeEntityPointerFV(TELY_Game *game, DQN_FMT_S
return result;
}
static TELY_GameEntity *TELY_Game_MakeEntityPointerF(TELY_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
static FP_GameEntity *FP_Game_MakeEntityPointerF(FP_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
TELY_GameEntity *result = TELY_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntity *result = FP_Game_MakeEntityPointerFV(game, fmt, args);
va_end(args);
return result;
}
static TELY_GameEntityHandle TELY_Game_MakeEntityF(TELY_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
static FP_GameEntityHandle FP_Game_MakeEntityF(FP_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
TELY_GameEntity *entity = TELY_Game_MakeEntityPointerF(game, fmt, args);
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, fmt, args);
va_end(args);
TELY_GameEntityHandle result = {};
FP_GameEntityHandle result = {};
if (entity)
result = entity->handle;
return result;
}
static bool TELY_Game_IsNilEntity(TELY_GameEntity *entity)
static bool FP_Game_IsNilEntity(FP_GameEntity *entity)
{
bool result = entity ? ((entity->handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK) == 0) : true;
bool result = entity ? ((entity->handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK) == 0) : true;
return result;
}
static void TELY_Game_DetachEntityIntoFreeList(TELY_Game *game, TELY_GameEntityHandle handle)
static void FP_Game_DetachEntityIntoFreeList(FP_Game *game, FP_GameEntityHandle handle)
{
TELY_GameEntity *entity = TELY_Game_GetEntity(game, handle);
if (TELY_Game_IsNilEntity(entity))
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
if (FP_Game_IsNilEntity(entity))
return;
// NOTE: Entities in the entity tree always have a parent (except for the
@ -267,11 +267,11 @@ static void TELY_Game_DetachEntityIntoFreeList(TELY_Game *game, TELY_GameEntityH
if (!DQN_CHECK(entity->parent))
return;
uint64_t const entity_index_from_handle = entity->handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK;
uint64_t const entity_index_from_handle = entity->handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK;
DQN_ASSERT(entity_index_from_handle < game->entities.size);
uint64_t const entity_generation_raw = entity->handle.id & TELY_GAME_ENTITY_HANDLE_GENERATION_MASK;
uint64_t const entity_generation = entity_generation_raw >> TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT;
uint64_t const entity_generation_raw = entity->handle.id & FP_GAME_ENTITY_HANDLE_GENERATION_MASK;
uint64_t const entity_generation = entity_generation_raw >> FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT;
uint64_t const new_entity_generation = entity_generation + 1;
// NOTE: De-attach entity from adjacent children
@ -282,7 +282,7 @@ static void TELY_Game_DetachEntityIntoFreeList(TELY_Game *game, TELY_GameEntityH
entity->next->prev = entity->prev;
// NOTE: De-attach from parent
TELY_GameEntity *parent = entity->parent;
FP_GameEntity *parent = entity->parent;
if (parent->first_child == entity)
parent->first_child = entity->next;
@ -296,7 +296,7 @@ static void TELY_Game_DetachEntityIntoFreeList(TELY_Game *game, TELY_GameEntityH
// NOTE: Update the incremented handle disassociating all prior handles
// to this entity which would reference older generation values
*entity = {};
entity->handle.id = entity_index_from_handle | (new_entity_generation << TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT);
entity->handle.id = entity_index_from_handle | (new_entity_generation << FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT);
// NOTE: Attach entity to the free list
entity->next = game->entity_free_list;
@ -310,34 +310,34 @@ static void TELY_Game_DetachEntityIntoFreeList(TELY_Game *game, TELY_GameEntityH
}
}
static void TELY_Game_DeleteEntity(TELY_Game *game, TELY_GameEntityHandle handle)
static void FP_Game_DeleteEntity(FP_Game *game, FP_GameEntityHandle handle)
{
uint64_t index_from_handle = handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK;
uint64_t index_from_handle = handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK;
if (!game || !DQN_CHECK(index_from_handle < game->entities.size))
return;
TELY_GameEntity *root = game->entities.data + index_from_handle;
FP_GameEntity *root = game->entities.data + index_from_handle;
if (root->handle != handle)
return;
// NOTE: The iterator snaps a copy of all the internal n-ary tree pointers
// so as we delete we do not accidentally invalidate any of the pointers.
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &it, root); ) {
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, root); ) {
DQN_ASSERT(it.entity != root);
TELY_GameEntity *entity = it.entity;
TELY_Game_DetachEntityIntoFreeList(game, entity->handle);
FP_GameEntity *entity = it.entity;
FP_Game_DetachEntityIntoFreeList(game, entity->handle);
}
TELY_Game_DetachEntityIntoFreeList(game, root->handle);
FP_Game_DetachEntityIntoFreeList(game, root->handle);
}
static Dqn_V2 TELY_Game_CalcEntityWorldPos(TELY_Game const *game, TELY_GameEntityHandle handle)
static Dqn_V2 FP_Game_CalcEntityWorldPos(FP_Game const *game, FP_GameEntityHandle handle)
{
Dqn_V2 result = {};
if (!game)
return result;
for (TELY_GameEntity const *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle);
for (FP_GameEntity const *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
entity != game->root_entity;
entity = entity->parent) {
result += entity->local_pos;
@ -345,68 +345,68 @@ static Dqn_V2 TELY_Game_CalcEntityWorldPos(TELY_Game const *game, TELY_GameEntit
return result;
}
static Dqn_Rect TELY_Game_CalcEntityLocalHitBox(TELY_Game const *game, TELY_GameEntityHandle handle)
static Dqn_Rect FP_Game_CalcEntityLocalHitBox(FP_Game const *game, FP_GameEntityHandle handle)
{
TELY_GameEntity *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *)game, handle);
FP_GameEntity *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *)game, handle);
Dqn_V2 half_hit_box_size = entity->local_hit_box_size * .5f;
Dqn_Rect result = Dqn_Rect_InitV2x2(entity->local_hit_box_offset - half_hit_box_size, entity->local_hit_box_size);
return result;
}
static Dqn_Rect TELY_Game_CalcEntityWorldHitBox(TELY_Game const *game, TELY_GameEntityHandle handle)
static Dqn_Rect FP_Game_CalcEntityWorldHitBox(FP_Game const *game, FP_GameEntityHandle handle)
{
TELY_GameEntity *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle);
Dqn_V2 world_pos = TELY_Game_CalcEntityWorldPos(game, handle);
Dqn_Rect local_hit_box = TELY_Game_CalcEntityLocalHitBox(game, entity->handle);
FP_GameEntity *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, handle);
Dqn_Rect local_hit_box = FP_Game_CalcEntityLocalHitBox(game, entity->handle);
Dqn_Rect result = Dqn_Rect_InitV2x2(world_pos + local_hit_box.pos, local_hit_box.size);
return result;
}
static Dqn_Rect TELY_Game_CalcEntityAttackWorldHitBox(TELY_Game const *game, TELY_GameEntityHandle handle)
static Dqn_Rect FP_Game_CalcEntityAttackWorldHitBox(FP_Game const *game, FP_GameEntityHandle handle)
{
TELY_GameEntity *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle);
Dqn_V2 world_pos = TELY_Game_CalcEntityWorldPos(game, handle);
FP_GameEntity *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, handle);
Dqn_V2 half_hit_box_size = entity->attack_box_size * .5f;
Dqn_Rect result = Dqn_Rect_InitV2x2(world_pos + entity->attack_box_offset - half_hit_box_size, entity->attack_box_size);
return result;
}
static Dqn_Rect TELY_Game_CalcEntityArrayWorldBoundingBox(TELY_Game const *game, TELY_GameEntityHandle const *handles, Dqn_usize count)
static Dqn_Rect FP_Game_CalcEntityArrayWorldBoundingBox(FP_Game const *game, FP_GameEntityHandle const *handles, Dqn_usize count)
{
Dqn_Rect result = {};
if (!game || !handles)
return result;
DQN_FOR_UINDEX(index, count) {
TELY_GameEntityHandle handle = handles[index];
TELY_GameEntity const *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle);
Dqn_Rect bbox = TELY_Game_CalcEntityLocalHitBox(game, entity->handle);
for (TELY_GameShape const &shape_ : entity->shapes) {
TELY_GameShape const *shape = &shape_;
FP_GameEntityHandle handle = handles[index];
FP_GameEntity const *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
Dqn_Rect bbox = FP_Game_CalcEntityLocalHitBox(game, entity->handle);
for (FP_GameShape const &shape_ : entity->shapes) {
FP_GameShape const *shape = &shape_;
switch (shape->type) {
case TELY_GameShapeType_None: {
case FP_GameShapeType_None: {
} break;
case TELY_GameShapeType_Circle: {
case FP_GameShapeType_Circle: {
Dqn_Rect rect =
Dqn_Rect_InitV2x2(shape->p1 - shape->circle_radius, Dqn_V2_InitNx1(shape->circle_radius * 2.f));
bbox = Dqn_Rect_Union(bbox, rect);
} break;
case TELY_GameShapeType_Rect: /*FALLTHRU*/
case TELY_GameShapeType_Line: {
case FP_GameShapeType_Rect: /*FALLTHRU*/
case FP_GameShapeType_Line: {
Dqn_V2 min = Dqn_V2_Min(shape->p1, shape->p2);
Dqn_V2 max = Dqn_V2_Max(shape->p1, shape->p2);
Dqn_Rect rect = Dqn_Rect_InitV2x2(min, max - min);
if (shape->type == TELY_GameShapeType_Rect)
if (shape->type == FP_GameShapeType_Rect)
rect.pos -= rect.size * .5f;
bbox = Dqn_Rect_Union(bbox, rect);
} break;
}
}
bbox.pos += TELY_Game_CalcEntityWorldPos(game, entity->handle);
bbox.pos += FP_Game_CalcEntityWorldPos(game, entity->handle);
if (index)
result = Dqn_Rect_Union(result, bbox);
@ -416,8 +416,8 @@ static Dqn_Rect TELY_Game_CalcEntityArrayWorldBoundingBox(TELY_Game const *game,
return result;
}
static Dqn_Rect TELY_Game_CalcEntityWorldBoundingBox(TELY_Game *game, TELY_GameEntityHandle handle)
static Dqn_Rect FP_Game_CalcEntityWorldBoundingBox(FP_Game *game, FP_GameEntityHandle handle)
{
Dqn_Rect result = TELY_Game_CalcEntityArrayWorldBoundingBox(game, &handle, 1);
Dqn_Rect result = FP_Game_CalcEntityArrayWorldBoundingBox(game, &handle, 1);
return result;
}

View File

@ -1,171 +0,0 @@
#if defined(__clang__)
#pragma once
#include "feely_pona_unity.h"
#endif
enum TELY_EntityFlag
{
TELY_EntityFlag_Clickable = 1 << 0,
TELY_EntityFlag_MoveByKeyboard = 1 << 1,
TELY_EntityFlag_MoveByMouse = 1 << 2,
TELY_EntityFlag_DrawHitBox = 1 << 3,
TELY_EntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 4,
};
enum TELY_GameShapeType
{
TELY_GameShapeType_None,
TELY_GameShapeType_Circle,
TELY_GameShapeType_Rect,
TELY_GameShapeType_Line,
};
struct TELY_GameShape
{
TELY_GameShapeType type;
Dqn_V2 p1;
Dqn_V2 p2;
Dqn_V4 colour;
Dqn_f32 line_thickness;
Dqn_f32 circle_radius;
TELY_RenderShapeMode render_mode;
};
const uint64_t TELY_GAME_ENTITY_HANDLE_GENERATION_MASK = 0xFFFF'0000'0000'0000;
const uint64_t TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT = 48;
const uint64_t TELY_GAME_ENTITY_HANDLE_GENERATION_MAX = TELY_GAME_ENTITY_HANDLE_GENERATION_MASK >> TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT;
const uint64_t TELY_GAME_ENTITY_HANDLE_INDEX_MASK = 0x0000'FFFF'FFFF'FFFF;
const uint64_t TELY_GAME_ENTITY_HANDLE_INDEX_MAX = TELY_GAME_ENTITY_HANDLE_INDEX_MASK - 1;
struct TELY_GameEntityHandle
{
uint64_t id;
};
enum TELY_GameEntityState
{
TELY_GameEntityState_Nil,
TELY_GameEntityState_Idle,
TELY_GameEntityState_Attack,
TELY_GameEntityState_Run,
};
struct TELY_GameEntityAnimation
{
uint16_t index;
uint16_t frame;
uint16_t ticks;
uint16_t ticks_per_frame;
};
struct TELY_GameWaypoint
{
Dqn_V2I pos;
TELY_GameWaypoint *next;
TELY_GameWaypoint *prev;
};
struct TELY_GameEntity
{
Dqn_String8 name;
TELY_GameEntityHandle handle;
TELY_AssetSpriteSheet *sprite_sheet;
Dqn_Slice<TELY_AssetSpriteAnimation> sprite_anims;
Dqn_V2 size_scale;
TELY_GameEntityAnimation anim;
TELY_GameEntityState state;
Dqn_V2 velocity;
TELY_GameEntityHandle stalk_entity;
Dqn_V2I stalk_entity_last_known_tile;
TELY_GameWaypoint *waypoints;
// NOTE: The entity hit box is positioned at the center of the entity.
Dqn_V2 local_hit_box_size;
Dqn_V2 local_hit_box_offset;
Dqn_V2 attack_box_size;
Dqn_V2 attack_box_offset;
uint64_t flags;
bool facing_left;
Dqn_V2 local_pos;
Dqn_f64 alive_time_s;
Dqn_FArray<TELY_GameShape, 4> shapes;
TELY_GameEntity *next;
TELY_GameEntity *prev;
TELY_GameEntity *first_child;
TELY_GameEntity *last_child;
TELY_GameEntity *parent;
};
struct TELY_GameEntityIterator
{
bool init;
Dqn_usize iteration_count;
TELY_GameEntity *entity;
TELY_GameEntity *last_visited;
TELY_GameEntity *entity_parent;
TELY_GameEntity *entity_next;
TELY_GameEntity *entity_first_child;
};
struct TELY_FreyaGameMath
{
TELY_GameEntityHandle lec01_group_box;
TELY_GameEntityHandle lec01_axis;
TELY_GameEntityHandle lec01_point_a;
TELY_GameEntityHandle lec01_point_b;
TELY_GameEntityHandle lec01_task01_group_box;
TELY_GameEntityHandle lec01_task01_radial_trigger;
Dqn_f32 lec01_task01_radial_trigger_radius;
TELY_GameEntityHandle lec01_task01_player;
TELY_GameEntityHandle lec01_task02_group_box;
TELY_GameEntityHandle lec01_task02_ray_begin;
TELY_GameEntityHandle lec01_task02_ray_end;
TELY_GameEntityHandle lec01_task02_surface;
};
struct TELY_GameCamera
{
Dqn_V2 world_pos;
Dqn_f32 rotate_rads;
Dqn_V2 scale;
};
struct TELY_Game
{
TELY_Platform *platform;
TELY_RFui rfui;
TELY_UI ui;
TELY_ChunkPool chunk_pool;
TELY_AssetFontHandle inter_regular_font;
TELY_AssetFontHandle inter_italic_font;
TELY_AssetFontHandle jetbrains_mono_font;
TELY_AssetAudioHandle test_audio;
Dqn_Slice<TELY_AssetSpriteAnimation> hero_sprite_anims;
TELY_AssetSpriteSheet hero_sprite_sheet;
Dqn_FArray<TELY_GameEntityHandle, 8> parent_entity_stack;
Dqn_VArray<TELY_GameEntity> entities;
TELY_GameEntity *root_entity;
TELY_GameEntity *entity_free_list;
TELY_GameEntityHandle clicked_entity;
TELY_GameEntityHandle hot_entity;
TELY_GameEntityHandle active_entity;
TELY_GameEntityHandle prev_clicked_entity;
TELY_GameEntityHandle prev_hot_entity;
TELY_GameEntityHandle prev_active_entity;
TELY_FreyaGameMath freya_game_math;
TELY_GameCamera camera;
};

View File

@ -62,7 +62,6 @@ DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with inte
// NOTE: feely_pona ================================================================================
#include "feely_pona_game.h"
#include "feely_pona.h"
#include "feely_pona_game.cpp"