Compare commits
2 Commits
5e441e1a13
...
ffb2ec3ea3
Author | SHA1 | Date | |
---|---|---|---|
ffb2ec3ea3 | |||
a9fba6f01d |
BIN
Data/Textures/smoochie_resized_25%.png
(Stored with Git LFS)
BIN
Data/Textures/smoochie_resized_25%.png
(Stored with Git LFS)
Binary file not shown.
BIN
Data/Textures/smoochie_resized_25%.txt
(Stored with Git LFS)
BIN
Data/Textures/smoochie_resized_25%.txt
(Stored with Git LFS)
Binary file not shown.
BIN
Data/Textures/sprite_spec.txt
(Stored with Git LFS)
BIN
Data/Textures/sprite_spec.txt
(Stored with Git LFS)
Binary file not shown.
2
External/tely
vendored
2
External/tely
vendored
@ -1 +1 @@
|
|||||||
Subproject commit e86fb0cad3342a12713c400da017648741c29234
|
Subproject commit f28426b027a5d63974ff8901459b8fb8454f39fe
|
197
feely_pona.cpp
197
feely_pona.cpp
@ -19,8 +19,8 @@ struct FP_GlobalAnimations
|
|||||||
Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down");
|
Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down");
|
||||||
Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left");
|
Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left");
|
||||||
Dqn_String8 smoochie_walk_right = DQN_STRING8("smoochie_walk_right");
|
Dqn_String8 smoochie_walk_right = DQN_STRING8("smoochie_walk_right");
|
||||||
Dqn_String8 smoochie_attack_down = DQN_STRING8("smoochie_attack");
|
Dqn_String8 smoochie_attack_down = DQN_STRING8("smoochie_attack_down");
|
||||||
Dqn_String8 smoochie_attack_side = DQN_STRING8("smoochie_attack_side");
|
Dqn_String8 smoochie_hurt_side = DQN_STRING8("smoochie_hurt_side");
|
||||||
Dqn_String8 smoochie_attack_heart = DQN_STRING8("smoochie_attack_heart");
|
Dqn_String8 smoochie_attack_heart = DQN_STRING8("smoochie_attack_heart");
|
||||||
Dqn_String8 smoochie_death = DQN_STRING8("smoochie_death");
|
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);
|
FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes);
|
||||||
game->chunk_pool = &platform->chunk_pool;
|
game->chunk_pool = &platform->chunk_pool;
|
||||||
game->meters_to_pixels = 65.416f;
|
game->meters_to_pixels = 65.416f;
|
||||||
|
Dqn_PCG32_Seed(&game->rng, 0xABCDEF);
|
||||||
|
|
||||||
platform->user_data = game;
|
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 (we_are_clicked_entity) {
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
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;
|
action->next_state = FP_EntitySmoochieState_AttackDown;
|
||||||
} break;
|
|
||||||
}
|
|
||||||
} else if (dir_vector.x || dir_vector.y) {
|
} else if (dir_vector.x || dir_vector.y) {
|
||||||
action->next_state = FP_EntitySmoochieState_Run;
|
action->next_state = FP_EntitySmoochieState_Run;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entity_has_velocity) {
|
||||||
|
action->next_state = FP_EntitySmoochieState_Run;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntitySmoochieState_AttackDown: {
|
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);
|
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) {
|
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;
|
action->next_state = FP_EntitySmoochieState_Idle;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntitySmoochieState_AttackSide: {
|
case FP_EntitySmoochieState_HurtSide: {
|
||||||
if (entering_new_state) {
|
if (entering_new_state) {
|
||||||
TELY_AssetFlip flip = entity->direction == FP_GameDirection_Right ? TELY_AssetFlip_X : TELY_AssetFlip_No;
|
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;
|
uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame;
|
||||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
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 (we_are_clicked_entity) {
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
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;
|
action->next_state = FP_EntitySmoochieState_AttackDown;
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entity_has_velocity /*&& !has_collision*/) {
|
if (!entity_has_velocity) {
|
||||||
action->next_state = FP_EntitySmoochieState_Idle;
|
action->next_state = FP_EntitySmoochieState_Idle;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*state == FP_EntitySmoochieState_AttackSide || *state == FP_EntitySmoochieState_AttackDown) {
|
if (*state == FP_EntitySmoochieState_AttackDown) {
|
||||||
entity->attack_box_size = entity->local_hit_box_size;
|
entity->attack_box_size = entity->local_hit_box_size;
|
||||||
// NOTE: Position the attack box
|
// NOTE: Position the attack box
|
||||||
if (entity->direction == FP_GameDirection_Left) {
|
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;
|
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 =============================================================
|
// 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))) {
|
if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) {
|
||||||
FP_SentinelListLink<FP_GameEntityHandle> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
|
FP_SentinelListLink<FP_GameWaypoint> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
|
||||||
if (first_waypoint->data != closest_terry->handle) {
|
if (first_waypoint->data.entity != closest_terry->handle) {
|
||||||
FP_SentinelListLink<FP_GameEntityHandle> *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool);
|
FP_SentinelListLink<FP_GameWaypoint> *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool);
|
||||||
link->data = closest_terry->handle;
|
link->data.entity = closest_terry->handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (entity->waypoints.size) {
|
while (entity->waypoints.size) {
|
||||||
FP_SentinelListLink<FP_GameEntityHandle> *waypoint_link = entity->waypoints.sentinel->next;
|
FP_SentinelListLink<FP_GameWaypoint> *waypoint_link = entity->waypoints.sentinel->next;
|
||||||
FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data);
|
FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data.entity);
|
||||||
if (FP_Game_IsNilEntity(waypoint)) {
|
if (FP_Game_IsNilEntity(waypoint)) {
|
||||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
||||||
continue;
|
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
|
// NOTE: Check if we've arrived at the waypoint
|
||||||
Dqn_f32 dist_to_waypoint_sq = Dqn_V2_LengthSq(entity_to_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) {
|
Dqn_f32 arrival_threshold = {};
|
||||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
switch (waypoint_link->data.arrive) {
|
||||||
continue;
|
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
|
// 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);
|
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;
|
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 ==========================================================
|
// 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
|
// NOTE: Setup the mob with waypoints
|
||||||
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
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;
|
mob->flags |= FP_GameEntityFlag_AggrosWhenNearTerry;
|
||||||
|
|
||||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
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;
|
continue;
|
||||||
|
|
||||||
// NOTE: Add the waypoint
|
// NOTE: Add the waypoint
|
||||||
FP_SentinelListLink<FP_GameEntityHandle> *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool);
|
FP_SentinelListLink<FP_GameWaypoint> *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool);
|
||||||
waypoint->data = waypoint_entity->handle;
|
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;
|
} 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_f32 size_scale = entity->sprite_height.meters / sprite_in_meters;
|
||||||
|
|
||||||
Dqn_Rect dest_rect = {};
|
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);
|
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) {
|
if (entity->flags & FP_GameEntityFlag_MobSpawner) {
|
||||||
Dqn_V2 start = world_pos;
|
Dqn_V2 start = world_pos;
|
||||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
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
|
// NOTE: Draw the waypoints that the entity is moving along
|
||||||
if (entity->waypoints.size) {
|
if (entity->waypoints.size) {
|
||||||
Dqn_V2 start = world_pos;
|
Dqn_V2 start = world_pos;
|
||||||
for (FP_SentinelListLink<FP_GameEntityHandle> *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) {
|
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) {
|
||||||
FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data);
|
FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data.entity);
|
||||||
if (FP_Game_IsNilEntity(waypoint))
|
if (FP_Game_IsNilEntity(waypoint))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D
|
|||||||
entity->hp = 3;
|
entity->hp = 3;
|
||||||
entity->local_pos = pos;
|
entity->local_pos = pos;
|
||||||
entity->sprite_height.meters = 1.6f;
|
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);
|
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
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->type = FP_EntityType_Terry;
|
||||||
entity->local_pos = pos;
|
entity->local_pos = pos;
|
||||||
entity->sprite_height.meters = 1.8f;
|
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);
|
FP_Entity_AddDebugEditorFlags(game, result);
|
||||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||||
return result;
|
return result;
|
||||||
|
@ -27,7 +27,7 @@ enum FP_EntitySmoochieState
|
|||||||
FP_EntitySmoochieState_Nil,
|
FP_EntitySmoochieState_Nil,
|
||||||
FP_EntitySmoochieState_Idle,
|
FP_EntitySmoochieState_Idle,
|
||||||
FP_EntitySmoochieState_AttackDown,
|
FP_EntitySmoochieState_AttackDown,
|
||||||
FP_EntitySmoochieState_AttackSide,
|
FP_EntitySmoochieState_HurtSide,
|
||||||
FP_EntitySmoochieState_AttackHeart,
|
FP_EntitySmoochieState_AttackHeart,
|
||||||
FP_EntitySmoochieState_Run,
|
FP_EntitySmoochieState_Run,
|
||||||
};
|
};
|
||||||
|
@ -3,35 +3,13 @@
|
|||||||
#include "feely_pona_unity.h"
|
#include "feely_pona_unity.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static Dqn_f32 FP_Game_PixelsToMeters(FP_Game const *game)
|
#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)
|
||||||
Dqn_f32 result = game ? (1.f / game->meters_to_pixels) : 10.f;
|
#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game)->meters_to_pixels)
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Dqn_f32 FP_Game_MetersToPixels(FP_Game const *game)
|
#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))
|
||||||
Dqn_f32 result = game ? game->meters_to_pixels : 1 / 10.f;
|
#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game)->meters_to_pixels))
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs)
|
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)
|
if (!game)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
for (FP_GameEntity const *entity = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
|
FP_GameEntity const *first = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle);
|
||||||
entity != game->root_entity;
|
for (FP_GameEntity const *entity = first; entity != game->root_entity; entity = entity->parent)
|
||||||
entity = entity->parent) {
|
|
||||||
result += entity->local_pos;
|
result += entity->local_pos;
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +48,26 @@ struct FP_GameEntityHandle
|
|||||||
uint64_t id;
|
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
|
struct FP_GameWaypoint
|
||||||
{
|
{
|
||||||
Dqn_V2I pos;
|
FP_GameEntityHandle entity;
|
||||||
FP_GameWaypoint *next;
|
FP_GameWaypointArrive arrive;
|
||||||
FP_GameWaypoint *prev;
|
Dqn_f32 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameEntitySpawnList
|
struct FP_GameEntitySpawnList
|
||||||
@ -86,6 +101,15 @@ enum FP_GameDirection
|
|||||||
FP_GameDirection_Right,
|
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
|
struct FP_GameEntity
|
||||||
{
|
{
|
||||||
FP_GameEntity *next;
|
FP_GameEntity *next;
|
||||||
@ -104,7 +128,12 @@ struct FP_GameEntity
|
|||||||
FP_GameEntityAction action;
|
FP_GameEntityAction action;
|
||||||
Dqn_V2 velocity;
|
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.
|
// NOTE: The entity hit box is positioned at the center of the entity.
|
||||||
Dqn_V2 local_hit_box_size;
|
Dqn_V2 local_hit_box_size;
|
||||||
@ -181,6 +210,7 @@ struct FP_Game
|
|||||||
|
|
||||||
Dqn_f32 meters_to_pixels;
|
Dqn_f32 meters_to_pixels;
|
||||||
uint64_t clock_ms;
|
uint64_t clock_ms;
|
||||||
|
Dqn_PCG32 rng;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameAStarNode
|
struct FP_GameAStarNode
|
||||||
|
Loading…
Reference in New Issue
Block a user