Compare commits
No commits in common. "5d84b911181f0c071b6cdebb63c206915538853a" and "3ea0ae2e6498ada9b5186933c8220366212c5a9e" have entirely different histories.
5d84b91118
...
3ea0ae2e64
2
External/tely
vendored
2
External/tely
vendored
@ -1 +1 @@
|
||||
Subproject commit 9b91e7e1b9bb3a6eb103aeb5de457c4a56fee331
|
||||
Subproject commit b59630fb8f3ecaa4be1382d9bdcc236afea3ce93
|
512
feely_pona.cpp
512
feely_pona.cpp
@ -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;
|
||||
TELY_Game *game = Dqn_Arena_New(&platform->arena, TELY_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,8 +82,8 @@ 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);
|
||||
@ -165,39 +164,25 @@ void TELY_DLL_Init(void *user_data)
|
||||
DQN_ASSERT(game->root_entity->parent == nullptr);
|
||||
}
|
||||
|
||||
// NOTE: Hero
|
||||
// NOTE: Test sprite animation entity
|
||||
{
|
||||
TELY_GameEntity *hero = TELY_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;
|
||||
game->clicked_entity = hero->handle;
|
||||
}
|
||||
|
||||
// NOTE: Enemy
|
||||
{
|
||||
TELY_GameEntity *enemy = TELY_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;
|
||||
TELY_GameEntity *first_entity = TELY_Game_MakeEntityPointerF(game, "Hero");
|
||||
first_entity->local_pos = Dqn_V2_InitNx2(100.f, 100.f);
|
||||
first_entity->size_scale = Dqn_V2_InitNx1(4);
|
||||
first_entity->sprite_sheet = &game->hero_sprite_sheet;
|
||||
first_entity->sprite_anims = game->hero_sprite_anims;
|
||||
first_entity->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size);
|
||||
first_entity->flags |= TELY_EntityFlag_Clickable;
|
||||
first_entity->flags |= TELY_EntityFlag_MoveByKeyboard;
|
||||
first_entity->flags |= TELY_EntityFlag_MoveByMouse;
|
||||
}
|
||||
|
||||
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 ============================================================================
|
||||
|
||||
@ -210,308 +195,6 @@ void TELY_DLL_Init(void *user_data)
|
||||
ui->arena.allocs_are_allowed_to_leak = true;
|
||||
}
|
||||
|
||||
void TELY_Game_EntityChangeState(TELY_GameEntity *entity, TELY_GameEntityState state)
|
||||
{
|
||||
if (entity->state == state)
|
||||
return;
|
||||
entity->state = state;
|
||||
entity->anim.frame = 0;
|
||||
entity->anim.ticks = 0;
|
||||
}
|
||||
|
||||
void FP_GameUpdate(TELY_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
|
||||
{
|
||||
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
|
||||
game->clicked_entity = game->prev_active_entity;
|
||||
|
||||
Dqn_V2 dir_vector = {};
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_W))
|
||||
dir_vector.y = -1.f;
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_A))
|
||||
dir_vector.x = -1.f;
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_S))
|
||||
dir_vector.y = +1.f;
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_D))
|
||||
dir_vector.x = +1.f;
|
||||
|
||||
if (game->clicked_entity.id) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete))
|
||||
TELY_Game_DeleteEntity(game, game->clicked_entity);
|
||||
|
||||
TELY_GameEntity *player = TELY_Game_GetEntity(game, game->clicked_entity);
|
||||
if (player) {
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_J)) {
|
||||
TELY_Game_EntityChangeState(player, TELY_GameEntityState_Attack);
|
||||
}
|
||||
|
||||
if (player->state != TELY_GameEntityState_Attack) {
|
||||
if (dir_vector.x || dir_vector.y) {
|
||||
TELY_Game_EntityChangeState(player, TELY_GameEntityState_Run);
|
||||
} else {
|
||||
TELY_Game_EntityChangeState(player, TELY_GameEntityState_Idle);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
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) {
|
||||
acceleration = dir_vector * 10000000.f;
|
||||
if (dir_vector.x)
|
||||
entity->facing_left = dir_vector.x < 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Core equations of motion ==========================================================
|
||||
{
|
||||
// f"(t) = a
|
||||
// f'(t) = at + v
|
||||
// f (t) = 0.5f*a(t^2) + vt + p
|
||||
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s);
|
||||
Dqn_f32 t_squared = DQN_SQUARED(t);
|
||||
entity->velocity = (acceleration * t) + entity->velocity * 0.82f;
|
||||
entity->local_pos += (acceleration * 0.5f * t_squared) + (entity->velocity * t);
|
||||
}
|
||||
|
||||
// NOTE: Move entity by mouse ==============================================================
|
||||
if (game->active_entity == entity->handle && entity->flags & TELY_EntityFlag_MoveByMouse) {
|
||||
if (entity->flags & TELY_EntityFlag_MoveByMouse) {
|
||||
entity->velocity = {};
|
||||
entity->local_pos += input->mouse_p_delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity->flags & TELY_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);
|
||||
|
||||
for (TELY_GameEntityIterator child_it = {}; TELY_Game_DFSPreOrderWalkEntityTree(game, &child_it, entity);) {
|
||||
TELY_GameEntity *child = child_it.entity;
|
||||
DQN_ASSERT(child != entity);
|
||||
|
||||
Dqn_Rect bbox = TELY_Game_CalcEntityWorldBoundingBox(game, child->handle);
|
||||
children_bbox = Dqn_Rect_Union(children_bbox, bbox);
|
||||
}
|
||||
|
||||
Dqn_Rect padded_bbox = Dqn_Rect_Expand(children_bbox, 16.f);
|
||||
entity->local_hit_box_offset = padded_bbox.pos - entity->local_pos + (padded_bbox.size * .5f);
|
||||
entity->local_hit_box_size = padded_bbox.size;
|
||||
}
|
||||
|
||||
// NOTE: Handle animation state ============================================================
|
||||
uint16_t desired_anim_index = 0;
|
||||
switch (entity->state) {
|
||||
case TELY_GameEntityState_Idle: {
|
||||
desired_anim_index = TELY_Asset_GetSpriteAnim(entity->sprite_anims, DQN_STRING8("Idle"));
|
||||
} break;
|
||||
|
||||
case TELY_GameEntityState_Attack: {
|
||||
desired_anim_index = TELY_Asset_GetSpriteAnim(entity->sprite_anims, DQN_STRING8("Attack A"));
|
||||
} break;
|
||||
|
||||
case TELY_GameEntityState_Run: {
|
||||
desired_anim_index = TELY_Asset_GetSpriteAnim(entity->sprite_anims, DQN_STRING8("Run"));
|
||||
} break;
|
||||
}
|
||||
|
||||
{
|
||||
TELY_AssetSpriteAnimation const *sprite_anim = entity->sprite_anims.data + entity->anim.index;
|
||||
if (desired_anim_index == entity->anim.index) { // NOTE: Current anim matches, check if its finished
|
||||
if (entity->anim.frame >= sprite_anim->count) { // NOTE: Animation is finished
|
||||
entity->anim.frame = 0;
|
||||
entity->anim.ticks = 0;
|
||||
switch (entity->state) {
|
||||
case TELY_GameEntityState_Idle: {
|
||||
} break;
|
||||
|
||||
case TELY_GameEntityState_Attack: {
|
||||
entity->state = TELY_GameEntityState_Idle;
|
||||
} break;
|
||||
|
||||
case TELY_GameEntityState_Run: {
|
||||
if (dir_vector.x == 0 && dir_vector.y == 0) {
|
||||
entity->state = TELY_GameEntityState_Idle;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
if (entity->anim.ticks++ > 4 /*ticks_per_anim_frame*/) {
|
||||
entity->anim.frame++;
|
||||
entity->anim.ticks = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NOTE: Current animation does not match the desired animation, change anim
|
||||
entity->anim.index = desired_anim_index;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Calculate entity attack box =======================================================
|
||||
if (entity->state == TELY_GameEntityState_Attack) {
|
||||
|
||||
entity->attack_box_size = entity->local_hit_box_size;
|
||||
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
|
||||
if (sprite_sheet) {
|
||||
entity->attack_box_size = Dqn_V2_InitV2I(sprite_sheet->sprite_size);
|
||||
}
|
||||
|
||||
// NOTE: Position the attack box
|
||||
if (entity->facing_left) {
|
||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
|
||||
entity->local_hit_box_offset.y);
|
||||
} else {
|
||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->local_hit_box_size.w,
|
||||
entity->local_hit_box_offset.y);
|
||||
}
|
||||
} else {
|
||||
entity->attack_box_size = {};
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Resolve collision ====================================================================
|
||||
for (TELY_GameEntityIterator attacker_it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &attacker_it, game->root_entity); ) {
|
||||
TELY_GameEntity *attacker = attacker_it.entity;
|
||||
|
||||
// NOTE: Resolve box collision
|
||||
Dqn_Rect attacker_hit_box = TELY_Game_CalcEntityWorldHitBox(game, attacker->handle);
|
||||
|
||||
// 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;
|
||||
if (defender->handle == attacker->handle)
|
||||
continue;
|
||||
|
||||
Dqn_Rect defender_box = TELY_Game_CalcEntityWorldHitBox(game, defender->handle);
|
||||
Dqn_Rect hit_rect = Dqn_Rect_Intersection(attacker_box, defender_box);
|
||||
|
||||
if (!Dqn_Rect_Area(hit_rect))
|
||||
continue;
|
||||
|
||||
TELY_Render_CircleColourV4(renderer, hit_rect.pos, 10.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4);
|
||||
Dqn_V2 attacker_center_pos = Dqn_Rect_Center(attacker_box);
|
||||
Dqn_V2 defender_center_pos = Dqn_Rect_Center(defender_box);
|
||||
Dqn_V2 attacker_to_defender = defender_center_pos - attacker_center_pos;
|
||||
Dqn_V2 attacker_to_defender_norm = Dqn_V2_Normalise(attacker_to_defender);
|
||||
|
||||
TELY_Render_LineColourV4(renderer, defender_center_pos, defender_center_pos + (attacker_to_defender_norm * 100.f), TELY_COLOUR_RED_V4, 3.f);
|
||||
|
||||
Dqn_V2 acceleration = attacker_to_defender_norm * 10000000.f;
|
||||
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s);
|
||||
Dqn_f32 t_squared = DQN_SQUARED(t);
|
||||
|
||||
Dqn_V2 delta_p = (acceleration * 0.5f * t_squared) + (defender->velocity * t);
|
||||
defender->velocity = (acceleration * t) + defender->velocity * 2.0f;
|
||||
defender->local_pos += delta_p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FP_GameRender(TELY_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
{
|
||||
TELY_PlatformInput *input = &platform->input;
|
||||
Dqn_M2x3 model_view = TELY_Game_CameraModelViewM2x3(game->camera, platform);
|
||||
Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(model_view, input->mouse_p);
|
||||
|
||||
for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
|
||||
TELY_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 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: {
|
||||
} break;
|
||||
|
||||
case TELY_GameShapeType_Circle: {
|
||||
TELY_Render_CircleColourV4(renderer, local_to_world_p1, shape->circle_radius, shape->render_mode, shape->colour);
|
||||
} break;
|
||||
|
||||
case TELY_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: {
|
||||
TELY_Render_LineColourV4(renderer, local_to_world_p1, local_to_world_p2, shape->colour, shape->line_thickness);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Render entity sprites =============================================================
|
||||
if (entity->sprite_sheet && entity->sprite_anims.size) {
|
||||
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
|
||||
TELY_AssetSpriteAnimation const *sprite_anim = entity->sprite_anims.data + entity->anim.index;
|
||||
|
||||
Dqn_usize sprite_index = (sprite_anim->index + (entity->anim.frame % sprite_anim->count)) % sprite_sheet->sprite_count;
|
||||
Dqn_usize sprite_sheet_row = sprite_index / sprite_sheet->sprites_per_row;
|
||||
Dqn_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row;
|
||||
|
||||
Dqn_Rect src_rect = {};
|
||||
src_rect.pos.x = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite_sheet->sprite_size.w);
|
||||
src_rect.pos.y = DQN_CAST(Dqn_f32)(sprite_sheet_row * sprite_sheet->sprite_size.y);
|
||||
src_rect.size.w = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.w;
|
||||
src_rect.size.h = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.h;
|
||||
|
||||
Dqn_Rect dest_rect = {};
|
||||
dest_rect.size = src_rect.size * entity->size_scale;
|
||||
dest_rect.pos = world_pos - (dest_rect.size * .5f);
|
||||
|
||||
if (entity->facing_left)
|
||||
dest_rect.size.w *= -1.f; // NOTE: Flip the texture horizontally
|
||||
|
||||
TELY_Render_TextureColourV4(renderer, sprite_sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4);
|
||||
}
|
||||
|
||||
// NOTE: Render attack box =================================================================
|
||||
{
|
||||
Dqn_Rect attack_box = TELY_Game_CalcEntityAttackWorldHitBox(game, entity->handle);
|
||||
TELY_Render_RectColourV4(renderer, attack_box, TELY_RenderShapeMode_Line, TELY_COLOUR_RED_TOMATO_V4);
|
||||
}
|
||||
|
||||
// NOTE: Render world position =============================================================
|
||||
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);
|
||||
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)) {
|
||||
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);
|
||||
|
||||
if (game->hot_entity == entity->handle && (entity->name.size)) {
|
||||
Dqn_V2 entity_world_pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 label = Dqn_String8_InitF(scratch.allocator,
|
||||
"%.*s (%.1f, %.1f)",
|
||||
DQN_STRING_FMT(entity->name),
|
||||
entity_world_pos.x,
|
||||
entity_world_pos.y);
|
||||
TELY_Render_Text(renderer, world_mouse_p, Dqn_V2_InitNx2(0.f, 1), label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport)
|
||||
void TELY_DLL_FrameUpdate(void *user_data)
|
||||
{
|
||||
@ -519,15 +202,14 @@ 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_Game *game = DQN_CAST(TELY_Game *) platform->user_data;
|
||||
TELY_UI *ui = &game->ui;
|
||||
|
||||
TELY_UI_FrameSetup(ui, assets, &platform->frame_arena);
|
||||
TELY_UI_PushFont(ui, pona->jetbrains_mono_font);
|
||||
TELY_UI_PushFont(ui, game->jetbrains_mono_font);
|
||||
|
||||
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 = {};
|
||||
@ -570,14 +252,24 @@ 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 = {};
|
||||
{
|
||||
Dqn_V2 rotate_origin = game->camera.world_pos - (Dqn_V2_InitV2I(platform->core.window_size) * .5f);
|
||||
model_view = Dqn_M2x3_Identity();
|
||||
model_view = Dqn_M2x3_Mul(model_view, Dqn_M2x3_Translate(rotate_origin));
|
||||
model_view = Dqn_M2x3_Mul(model_view, Dqn_M2x3_Rotate(game->camera.rotate_rads));
|
||||
model_view = Dqn_M2x3_Mul(model_view, Dqn_M2x3_Scale(game->camera.scale));
|
||||
model_view = Dqn_M2x3_Mul(model_view, Dqn_M2x3_Translate((rotate_origin * -1) + game->camera.world_pos));
|
||||
TELY_Render_PushTransform(renderer, model_view);
|
||||
}
|
||||
|
||||
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*/);
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
@ -606,7 +298,141 @@ void TELY_DLL_FrameUpdate(void *user_data)
|
||||
}
|
||||
}
|
||||
|
||||
FP_GameUpdate(game, renderer, input);
|
||||
FP_GameRender(game, platform, renderer);
|
||||
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
|
||||
game->clicked_entity = game->prev_active_entity;
|
||||
|
||||
Dqn_V2 dir_vector = {};
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_W))
|
||||
dir_vector.y = -1.f;
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_A))
|
||||
dir_vector.x = -1.f;
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_S))
|
||||
dir_vector.y = +1.f;
|
||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_D))
|
||||
dir_vector.x = +1.f;
|
||||
|
||||
if (game->clicked_entity.id) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete))
|
||||
TELY_Game_DeleteEntity(game, game->clicked_entity);
|
||||
} else {
|
||||
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;
|
||||
entity->alive_time_s += input->delta_s;
|
||||
|
||||
// NOTE: Move entity by keyboard ===========================================================
|
||||
if (game->clicked_entity == entity->handle) {
|
||||
if (entity->flags & TELY_EntityFlag_MoveByKeyboard) {
|
||||
entity->local_pos += dir_vector * DQN_CAST(Dqn_f32)(300.f * input->delta_s);
|
||||
if (dir_vector.x)
|
||||
entity->facing_left = dir_vector.x < 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Move entity by mouse ==============================================================
|
||||
if (game->active_entity == entity->handle && entity->flags & TELY_EntityFlag_MoveByMouse) {
|
||||
if (entity->flags & TELY_EntityFlag_MoveByMouse)
|
||||
entity->local_pos += input->mouse_p_delta;
|
||||
}
|
||||
|
||||
if (entity->flags & TELY_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);
|
||||
|
||||
for (TELY_GameEntityIterator child_it = {}; TELY_Game_DFSPreOrderWalkEntityTree(game, &child_it, entity);) {
|
||||
TELY_GameEntity *child = child_it.entity;
|
||||
DQN_ASSERT(child != entity);
|
||||
|
||||
Dqn_Rect bbox = TELY_Game_CalcEntityWorldBoundingBox(game, child->handle);
|
||||
children_bbox = Dqn_Rect_Union(children_bbox, bbox);
|
||||
}
|
||||
|
||||
Dqn_Rect padded_bbox = Dqn_Rect_Expand(children_bbox, 16.f);
|
||||
entity->local_hit_box_offset = padded_bbox.pos - entity->local_pos + (padded_bbox.size * .5f);
|
||||
entity->local_hit_box_size = padded_bbox.size;
|
||||
}
|
||||
|
||||
// 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 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: {
|
||||
} break;
|
||||
|
||||
case TELY_GameShapeType_Circle: {
|
||||
TELY_Render_CircleColourV4(renderer, local_to_world_p1, shape->circle_radius, shape->render_mode, shape->colour);
|
||||
} break;
|
||||
|
||||
case TELY_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: {
|
||||
TELY_Render_LineColourV4(renderer, local_to_world_p1, local_to_world_p2, shape->colour, shape->line_thickness);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Render entity sprites =============================================================
|
||||
if (entity->sprite_sheet && entity->sprite_anims.size) {
|
||||
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
|
||||
TELY_AssetSpriteAnimation const *sprite_anim = entity->sprite_anims.data + entity->sprite_anim_index;
|
||||
uint32_t ticks_per_anim_frame = 10;
|
||||
uint64_t anim_frame_counter = input->frame_counter / ticks_per_anim_frame;
|
||||
|
||||
Dqn_usize sprite_index = (sprite_anim->index + (anim_frame_counter % sprite_anim->count)) % sprite_sheet->sprite_count;
|
||||
Dqn_usize sprite_sheet_row = sprite_index / sprite_sheet->sprites_per_row;
|
||||
Dqn_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row;
|
||||
|
||||
Dqn_Rect src_rect = {};
|
||||
src_rect.pos.x = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite_sheet->sprite_size.w);
|
||||
src_rect.pos.y = DQN_CAST(Dqn_f32)(sprite_sheet_row * sprite_sheet->sprite_size.y);
|
||||
src_rect.size.w = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.w;
|
||||
src_rect.size.h = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.h;
|
||||
|
||||
Dqn_Rect dest_rect = {};
|
||||
dest_rect.pos = world_pos;
|
||||
dest_rect.size = src_rect.size * entity->size_scale;
|
||||
|
||||
if (entity->facing_left)
|
||||
dest_rect.size.w *= -1.f; // NOTE: Flip the texture horizontally
|
||||
|
||||
TELY_Render_TextureColourV4(renderer, sprite_sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4);
|
||||
}
|
||||
|
||||
// NOTE: Render world position =============================================================
|
||||
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);
|
||||
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)) {
|
||||
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);
|
||||
|
||||
if (game->hot_entity == entity->handle && (entity->name.size)) {
|
||||
Dqn_V2 entity_world_pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 label = Dqn_String8_InitF(scratch.allocator,
|
||||
"%.*s (%.1f, %.1f)",
|
||||
DQN_STRING_FMT(entity->name),
|
||||
entity_world_pos.x,
|
||||
entity_world_pos.y);
|
||||
TELY_Render_Text(renderer, world_mouse_p, Dqn_V2_InitNx2(0.f, 1), label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TELY_Audio_MixPlaybackSamples(audio, assets);
|
||||
}
|
||||
|
15
feely_pona.h
15
feely_pona.h
@ -1,15 +0,0 @@
|
||||
#if defined(__clang__)
|
||||
#pragma once
|
||||
#include "feely_pona_unity.h"
|
||||
#endif
|
||||
|
||||
struct Feely_Pona
|
||||
{
|
||||
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;
|
||||
};
|
@ -51,7 +51,6 @@ DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with inte
|
||||
#include "tely_ui.h"
|
||||
#include "tely_rfui.h"
|
||||
#include "tely_game.h"
|
||||
#include "feely_pona.h"
|
||||
|
||||
#include "tely_tools.cpp"
|
||||
#include "tely_game.cpp"
|
||||
|
Loading…
Reference in New Issue
Block a user