Compare commits
2
Commits
5e441e1a13
...
ffb2ec3ea3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffb2ec3ea3 | ||
|
|
a9fba6f01d |
No files matched your search
Binary file not shown.
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.
Vendored
+1
-1
Submodule External/tely updated: e86fb0cad3...f28426b027.
+151
-46
@@ -19,8 +19,8 @@ struct FP_GlobalAnimations
|
||||
Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down");
|
||||
Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left");
|
||||
Dqn_String8 smoochie_walk_right = DQN_STRING8("smoochie_walk_right");
|
||||
Dqn_String8 smoochie_attack_down = DQN_STRING8("smoochie_attack");
|
||||
Dqn_String8 smoochie_attack_side = DQN_STRING8("smoochie_attack_side");
|
||||
Dqn_String8 smoochie_attack_down = DQN_STRING8("smoochie_attack_down");
|
||||
Dqn_String8 smoochie_hurt_side = DQN_STRING8("smoochie_hurt_side");
|
||||
Dqn_String8 smoochie_attack_heart = DQN_STRING8("smoochie_attack_heart");
|
||||
Dqn_String8 smoochie_death = DQN_STRING8("smoochie_death");
|
||||
}
|
||||
@@ -132,6 +132,7 @@ void TELY_DLL_Init(void *user_data)
|
||||
FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes);
|
||||
game->chunk_pool = &platform->chunk_pool;
|
||||
game->meters_to_pixels = 65.416f;
|
||||
Dqn_PCG32_Seed(&game->rng, 0xABCDEF);
|
||||
|
||||
platform->user_data = game;
|
||||
{
|
||||
@@ -464,22 +465,15 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
|
||||
if (we_are_clicked_entity) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
|
||||
switch (entity->direction) {
|
||||
case FP_GameDirection_Up: /*FALLTHRU*/
|
||||
case FP_GameDirection_Right: /*FALLTHRU*/
|
||||
case FP_GameDirection_Left: {
|
||||
action->next_state = FP_EntitySmoochieState_AttackSide;
|
||||
} break;
|
||||
|
||||
case FP_GameDirection_Down: {
|
||||
action->next_state = FP_EntitySmoochieState_AttackDown;
|
||||
} break;
|
||||
}
|
||||
} else if (dir_vector.x || dir_vector.y) {
|
||||
action->next_state = FP_EntitySmoochieState_Run;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity_has_velocity) {
|
||||
action->next_state = FP_EntitySmoochieState_Run;
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntitySmoochieState_AttackDown: {
|
||||
@@ -489,15 +483,52 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
||||
}
|
||||
|
||||
// NOTE: Check if the heart animation is playing
|
||||
bool has_heart_flourish = false;
|
||||
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
|
||||
FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index;
|
||||
if (sprite->asset.anim->label == g_anim_names.smoochie_attack_heart) {
|
||||
has_heart_flourish = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: If we don't have the anim playing make one
|
||||
if (!has_heart_flourish) {
|
||||
FP_GameRenderSprite *cosmetic_sprite = Dqn_FArray_Make(&entity->extra_cosmetic_anims, Dqn_ZeroMem_Yes);
|
||||
if (cosmetic_sprite) {
|
||||
cosmetic_sprite->asset = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_heart, TELY_AssetFlip_No);
|
||||
cosmetic_sprite->started_at_clock_ms = game->clock_ms;
|
||||
cosmetic_sprite->height.meters = entity->sprite_height.meters * .35f;
|
||||
cosmetic_sprite->loop = true;
|
||||
|
||||
uint32_t max_rng_dist_x = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * 1.f);
|
||||
uint32_t rng_x = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_x);
|
||||
cosmetic_sprite->offset.x = rng_x - (max_rng_dist_x * .5f);
|
||||
|
||||
uint32_t max_rng_dist_y = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * .25f);
|
||||
uint32_t rng_y = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_y);
|
||||
cosmetic_sprite->offset.y = -DQN_CAST(Dqn_f32)rng_y;
|
||||
}
|
||||
}
|
||||
|
||||
if (action_has_finished) {
|
||||
// NOTE: Ensure the heart animation terminates by removing the loop
|
||||
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
|
||||
FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index;
|
||||
if (sprite->asset.anim->label == g_anim_names.smoochie_attack_heart)
|
||||
sprite->loop = false;
|
||||
}
|
||||
|
||||
// NOTE: Transition out of the action
|
||||
action->next_state = FP_EntitySmoochieState_Idle;
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntitySmoochieState_AttackSide: {
|
||||
case FP_EntitySmoochieState_HurtSide: {
|
||||
if (entering_new_state) {
|
||||
TELY_AssetFlip flip = entity->direction == FP_GameDirection_Right ? TELY_AssetFlip_X : TELY_AssetFlip_No;
|
||||
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_side, flip);
|
||||
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_hurt_side, flip);
|
||||
uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
||||
}
|
||||
@@ -527,28 +558,17 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
|
||||
if (we_are_clicked_entity) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
|
||||
switch (entity->direction) {
|
||||
case FP_GameDirection_Up: /*FALLTHRU*/
|
||||
case FP_GameDirection_Right: /*FALLTHRU*/
|
||||
case FP_GameDirection_Left: {
|
||||
action->next_state = FP_EntitySmoochieState_AttackSide;
|
||||
} break;
|
||||
|
||||
case FP_GameDirection_Down: {
|
||||
action->next_state = FP_EntitySmoochieState_AttackDown;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!entity_has_velocity /*&& !has_collision*/) {
|
||||
if (!entity_has_velocity) {
|
||||
action->next_state = FP_EntitySmoochieState_Idle;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if (*state == FP_EntitySmoochieState_AttackSide || *state == FP_EntitySmoochieState_AttackDown) {
|
||||
if (*state == FP_EntitySmoochieState_AttackDown) {
|
||||
entity->attack_box_size = entity->local_hit_box_size;
|
||||
// NOTE: Position the attack box
|
||||
if (entity->direction == FP_GameDirection_Left) {
|
||||
@@ -665,6 +685,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
entity->direction = dir_vector.y > 0.f ? FP_GameDirection_Down : FP_GameDirection_Up;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (entity->velocity.x)
|
||||
entity->direction = entity->velocity.x > 0.f ? FP_GameDirection_Right : FP_GameDirection_Left;
|
||||
else if (entity->velocity.y)
|
||||
entity->direction = entity->velocity.y > 0.f ? FP_GameDirection_Down : FP_GameDirection_Up;
|
||||
}
|
||||
|
||||
// NOTE: Determine AI movement =============================================================
|
||||
@@ -689,18 +714,18 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 5.f))) {
|
||||
FP_SentinelListLink<FP_GameEntityHandle> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
|
||||
if (first_waypoint->data != closest_terry->handle) {
|
||||
FP_SentinelListLink<FP_GameEntityHandle> *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool);
|
||||
link->data = closest_terry->handle;
|
||||
if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) {
|
||||
FP_SentinelListLink<FP_GameWaypoint> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
|
||||
if (first_waypoint->data.entity != closest_terry->handle) {
|
||||
FP_SentinelListLink<FP_GameWaypoint> *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool);
|
||||
link->data.entity = closest_terry->handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (entity->waypoints.size) {
|
||||
FP_SentinelListLink<FP_GameEntityHandle> *waypoint_link = entity->waypoints.sentinel->next;
|
||||
FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data);
|
||||
FP_SentinelListLink<FP_GameWaypoint> *waypoint_link = entity->waypoints.sentinel->next;
|
||||
FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data.entity);
|
||||
if (FP_Game_IsNilEntity(waypoint)) {
|
||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
||||
continue;
|
||||
@@ -712,17 +737,51 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
|
||||
// NOTE: Check if we've arrived at the waypoint
|
||||
Dqn_f32 dist_to_waypoint_sq = Dqn_V2_LengthSq(entity_to_waypoint);
|
||||
Dqn_f32 arrival_threshold = DQN_MIN(DQN_SQUARED(entity->local_hit_box_size.x * .5f), 10.f);
|
||||
if (dist_to_waypoint_sq < arrival_threshold) {
|
||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
||||
continue;
|
||||
|
||||
Dqn_f32 arrival_threshold = {};
|
||||
switch (waypoint_link->data.arrive) {
|
||||
case FP_GameWaypointArrive_Default: {
|
||||
arrival_threshold = FP_Game_MetersToPixelsNx1(game, 1.f);
|
||||
} break;
|
||||
|
||||
case FP_GameWaypointArrive_WhenWithinEntitySize: {
|
||||
arrival_threshold = DQN_MAX(waypoint->local_hit_box_size.w, waypoint->local_hit_box_size.h) * waypoint_link->data.value;
|
||||
} break;
|
||||
}
|
||||
|
||||
// NOTE: We haven't arrived yet, calculate an acceleration vector to the waypoint
|
||||
if (dist_to_waypoint_sq > DQN_SQUARED(arrival_threshold)) {
|
||||
Dqn_V2 entity_to_waypoint_norm = Dqn_V2_Normalise(entity_to_waypoint);
|
||||
acceleration_meters_per_s = entity_to_waypoint_norm * 4.f;
|
||||
acceleration_meters_per_s = entity_to_waypoint_norm * 2.f;
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: We have arrived at the waypoint
|
||||
if ((entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) && waypoint->type == FP_EntityType_Terry) {
|
||||
// NOTE: We had a waypoint to move to Terry because he has
|
||||
// drawn our aggro and we've arrived at Terry
|
||||
|
||||
switch (entity->type) {
|
||||
case FP_EntityType_Nil: /*FALLTHRU*/
|
||||
case FP_EntityType_Merchant: break;
|
||||
|
||||
case FP_EntityType_Terry: {
|
||||
// TODO(doyle): We should check if it's valid to enter this new state
|
||||
// from the entity's current state
|
||||
entity->action.next_state = FP_EntityTerryState_AttackSide;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Smoochie: {
|
||||
// TODO(doyle): We should check if it's valid to enter this new state
|
||||
// from the entity's current state
|
||||
entity->action.next_state = FP_EntitySmoochieState_AttackDown;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Core equations of motion ==========================================================
|
||||
@@ -897,7 +956,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
|
||||
// NOTE: Setup the mob with waypoints
|
||||
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
||||
mob->waypoints = FP_SentinelList_Init<FP_GameEntityHandle>(game->chunk_pool);
|
||||
mob->waypoints = FP_SentinelList_Init<FP_GameWaypoint>(game->chunk_pool);
|
||||
mob->flags |= FP_GameEntityFlag_AggrosWhenNearTerry;
|
||||
|
||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
||||
@@ -905,8 +964,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
continue;
|
||||
|
||||
// NOTE: Add the waypoint
|
||||
FP_SentinelListLink<FP_GameEntityHandle> *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool);
|
||||
waypoint->data = waypoint_entity->handle;
|
||||
FP_SentinelListLink<FP_GameWaypoint> *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool);
|
||||
waypoint->data.entity = waypoint_entity->handle;
|
||||
waypoint->data.arrive = FP_GameWaypointArrive_WhenWithinEntitySize;
|
||||
waypoint->data.value = 1.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1032,7 +1093,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
} break;
|
||||
}
|
||||
|
||||
Dqn_f32 sprite_in_meters = src_rect.size.y * FP_Game_PixelsToMeters(game);
|
||||
Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, src_rect.size.y);
|
||||
Dqn_f32 size_scale = entity->sprite_height.meters / sprite_in_meters;
|
||||
|
||||
Dqn_Rect dest_rect = {};
|
||||
@@ -1048,6 +1109,50 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
TELY_Render_TextureColourV4(renderer, sprite.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4);
|
||||
}
|
||||
|
||||
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
|
||||
FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index;
|
||||
uint64_t elapsed_ms = game->clock_ms - sprite->started_at_clock_ms;
|
||||
uint16_t raw_anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite->asset.anim->ms_per_frame);
|
||||
if (raw_anim_frame > sprite->asset.anim->count && !sprite->loop) {
|
||||
anim_index = Dqn_FArray_EraseRange(&entity->extra_cosmetic_anims, anim_index, 1, Dqn_ArrayErase_Unstable).it_index;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t anim_frame = raw_anim_frame % sprite->asset.anim->count;
|
||||
Dqn_usize sprite_index = sprite->asset.anim->index + anim_frame;
|
||||
Dqn_Rect src_rect = {};
|
||||
switch (sprite->asset.sheet->type) {
|
||||
case TELY_AssetSpriteSheetType_Uniform: {
|
||||
Dqn_usize sprite_sheet_row = sprite_index / sprite->asset.sheet->sprites_per_row;
|
||||
Dqn_usize sprite_sheet_column = sprite_index % sprite->asset.sheet->sprites_per_row;
|
||||
src_rect.pos.x = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite->asset.sheet->sprite_size.w);
|
||||
src_rect.pos.y = DQN_CAST(Dqn_f32)(sprite_sheet_row * sprite->asset.sheet->sprite_size.y);
|
||||
src_rect.size.w = DQN_CAST(Dqn_f32)sprite->asset.sheet->sprite_size.w;
|
||||
src_rect.size.h = DQN_CAST(Dqn_f32)sprite->asset.sheet->sprite_size.h;
|
||||
} break;
|
||||
|
||||
case TELY_AssetSpriteSheetType_Rects: {
|
||||
DQN_ASSERT(sprite_index < sprite->asset.sheet->rects.size);
|
||||
src_rect = sprite->asset.sheet->rects.data[sprite_index];
|
||||
} break;
|
||||
}
|
||||
|
||||
Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, src_rect.size.y);
|
||||
Dqn_f32 size_scale = sprite->height.meters / sprite_in_meters;
|
||||
|
||||
Dqn_Rect dest_rect = {};
|
||||
dest_rect.size = src_rect.size * size_scale;
|
||||
dest_rect.pos = world_pos - (dest_rect.size * .5f) + sprite->offset;
|
||||
|
||||
if (sprite->asset.flip & TELY_AssetFlip_X)
|
||||
dest_rect.size.w *= -1.f; // NOTE: Flip the texture horizontally
|
||||
|
||||
if (sprite->asset.flip & TELY_AssetFlip_Y)
|
||||
dest_rect.size.h *= -1.f; // NOTE: Flip the texture vertically
|
||||
|
||||
TELY_Render_TextureColourV4(renderer, sprite->asset.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4);
|
||||
}
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_MobSpawner) {
|
||||
Dqn_V2 start = world_pos;
|
||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
||||
@@ -1077,8 +1182,8 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
// NOTE: Draw the waypoints that the entity is moving along
|
||||
if (entity->waypoints.size) {
|
||||
Dqn_V2 start = world_pos;
|
||||
for (FP_SentinelListLink<FP_GameEntityHandle> *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) {
|
||||
FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data);
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) {
|
||||
FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data.entity);
|
||||
if (FP_Game_IsNilEntity(waypoint))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D
|
||||
entity->hp = 3;
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height.meters = 1.6f;
|
||||
entity->local_hit_box_size = Dqn_V2_InitNx2(0.4f, 1.6f) * FP_Game_MetersToPixels(game);
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.6f);
|
||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
@@ -110,7 +110,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_
|
||||
entity->type = FP_EntityType_Terry;
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height.meters = 1.8f;
|
||||
entity->local_hit_box_size = Dqn_V2_InitNx2(0.5f, entity->sprite_height.meters) * FP_Game_MetersToPixels(game);
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters);
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
return result;
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ enum FP_EntitySmoochieState
|
||||
FP_EntitySmoochieState_Nil,
|
||||
FP_EntitySmoochieState_Idle,
|
||||
FP_EntitySmoochieState_AttackDown,
|
||||
FP_EntitySmoochieState_AttackSide,
|
||||
FP_EntitySmoochieState_HurtSide,
|
||||
FP_EntitySmoochieState_AttackHeart,
|
||||
FP_EntitySmoochieState_Run,
|
||||
};
|
||||
|
||||
+8
-32
@@ -3,35 +3,13 @@
|
||||
#include "feely_pona_unity.h"
|
||||
#endif
|
||||
|
||||
static Dqn_f32 FP_Game_PixelsToMeters(FP_Game const *game)
|
||||
{
|
||||
Dqn_f32 result = game ? (1.f / game->meters_to_pixels) : 10.f;
|
||||
return result;
|
||||
}
|
||||
#define FP_Game_MetersToPixelsNx1(game, val) ((val) * (game)->meters_to_pixels)
|
||||
#define FP_Game_MetersToPixelsNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (game)->meters_to_pixels)
|
||||
#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game)->meters_to_pixels)
|
||||
|
||||
static Dqn_f32 FP_Game_MetersToPixels(FP_Game const *game)
|
||||
{
|
||||
Dqn_f32 result = game ? game->meters_to_pixels : 1 / 10.f;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Dqn_V2 FP_Game_MetersToPixelsV2(FP_Game const *game, Dqn_V2 pos)
|
||||
{
|
||||
Dqn_V2 result = pos * FP_Game_MetersToPixels(game);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Dqn_f32 FP_Game_MetersToPixelsNx1(FP_Game const *game, Dqn_f32 val)
|
||||
{
|
||||
Dqn_f32 result = val * FP_Game_MetersToPixels(game);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Dqn_V2 FP_Game_MetersToPixelsNx2(FP_Game const *game, Dqn_f32 x, Dqn_f32 y)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(x, y) * FP_Game_MetersToPixels(game);
|
||||
return result;
|
||||
}
|
||||
#define FP_Game_PixelsToMetersNx1(game, val) ((val) * (1.f/(game)->meters_to_pixels))
|
||||
#define FP_Game_PixelsToMetersNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (1.f/(game)->meters_to_pixels))
|
||||
#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game)->meters_to_pixels))
|
||||
|
||||
static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs)
|
||||
{
|
||||
@@ -367,11 +345,9 @@ static Dqn_V2 FP_Game_CalcEntityWorldPos(FP_Game const *game, FP_GameEntityHandl
|
||||
if (!game)
|
||||
return result;
|
||||
|
||||
for (FP_GameEntity const *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
|
||||
entity != game->root_entity;
|
||||
entity = entity->parent) {
|
||||
FP_GameEntity const *first = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
|
||||
for (FP_GameEntity const *entity = first; entity != game->root_entity; entity = entity->parent)
|
||||
result += entity->local_pos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+34
-4
@@ -48,11 +48,26 @@ struct FP_GameEntityHandle
|
||||
uint64_t id;
|
||||
};
|
||||
|
||||
enum FP_GameWaypointArrive
|
||||
{
|
||||
// Considered arrived when within 1 meter of the target (`value` is ignored).
|
||||
FP_GameWaypointArrive_Default,
|
||||
|
||||
// If set, we consider the entity as arriving at the waypoint when it's
|
||||
// distance to the target is:
|
||||
//
|
||||
// `arrived = dist <= (entity_hit_box_size * value)`
|
||||
//
|
||||
// A value of 0 for example means we are considered arrived when the entity
|
||||
// is positioned exactly on top of the target's position.
|
||||
FP_GameWaypointArrive_WhenWithinEntitySize,
|
||||
};
|
||||
|
||||
struct FP_GameWaypoint
|
||||
{
|
||||
Dqn_V2I pos;
|
||||
FP_GameWaypoint *next;
|
||||
FP_GameWaypoint *prev;
|
||||
FP_GameEntityHandle entity;
|
||||
FP_GameWaypointArrive arrive;
|
||||
Dqn_f32 value;
|
||||
};
|
||||
|
||||
struct FP_GameEntitySpawnList
|
||||
@@ -86,6 +101,15 @@ enum FP_GameDirection
|
||||
FP_GameDirection_Right,
|
||||
};
|
||||
|
||||
struct FP_GameRenderSprite
|
||||
{
|
||||
Dqn_V2 offset;
|
||||
TELY_AssetAnimatedSprite asset;
|
||||
bool loop;
|
||||
FP_Meters height;
|
||||
uint64_t started_at_clock_ms;
|
||||
};
|
||||
|
||||
struct FP_GameEntity
|
||||
{
|
||||
FP_GameEntity *next;
|
||||
@@ -104,7 +128,12 @@ struct FP_GameEntity
|
||||
FP_GameEntityAction action;
|
||||
Dqn_V2 velocity;
|
||||
|
||||
FP_SentinelList<FP_GameEntityHandle> waypoints;
|
||||
// Extra animations that are to be executed, but, don't affect the state
|
||||
// of the entity. For example, when Smoochie attacks, we have a heart
|
||||
// animation that is a seperate sprite that will play out.
|
||||
Dqn_FArray<FP_GameRenderSprite, 2> extra_cosmetic_anims;
|
||||
|
||||
FP_SentinelList<FP_GameWaypoint> waypoints;
|
||||
|
||||
// NOTE: The entity hit box is positioned at the center of the entity.
|
||||
Dqn_V2 local_hit_box_size;
|
||||
@@ -181,6 +210,7 @@ struct FP_Game
|
||||
|
||||
Dqn_f32 meters_to_pixels;
|
||||
uint64_t clock_ms;
|
||||
Dqn_PCG32 rng;
|
||||
};
|
||||
|
||||
struct FP_GameAStarNode
|
||||
|
||||
Reference in new issue
Block a user