fp: Integrate clinger as a spawnable mob
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,3 +13,4 @@ if not exist "%sprite_packer%" (
|
||||
%sprite_packer% 4096x4096 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\terry_resized_25%% || exit /b 1
|
||||
%sprite_packer% 4096x2048 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\terry_merchant_resized_25%% || exit /b 1
|
||||
%sprite_packer% 4096x2048 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\smoochie_resized_25%% || exit /b 1
|
||||
%sprite_packer% 4096x2048 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\clinger_resized_25%% || exit /b 1
|
||||
|
||||
+131
-1
@@ -15,6 +15,7 @@ struct FP_GlobalAnimations
|
||||
Dqn_String8 terry_attack_side = DQN_STRING8("terry_attack_side");
|
||||
Dqn_String8 terry_attack_down = DQN_STRING8("terry_attack_down");
|
||||
Dqn_String8 terry_merchant = DQN_STRING8("terry_merchant");
|
||||
|
||||
Dqn_String8 smoochie_walk_up = DQN_STRING8("smoochie_walk_up");
|
||||
Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down");
|
||||
Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left");
|
||||
@@ -23,6 +24,13 @@ struct FP_GlobalAnimations
|
||||
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");
|
||||
|
||||
Dqn_String8 clinger_attack_down = DQN_STRING8("clinger_attack_down");
|
||||
Dqn_String8 clinger_attack_side = DQN_STRING8("clinger_attack_side");
|
||||
Dqn_String8 clinger_attack_up = DQN_STRING8("clinger_attack_up");
|
||||
Dqn_String8 clinger_death = DQN_STRING8("clinger_death");
|
||||
Dqn_String8 clinger_walk_up = DQN_STRING8("clinger_walk_up");
|
||||
Dqn_String8 clinger_walk_down = DQN_STRING8("clinger_walk_down");
|
||||
}
|
||||
g_anim_names;
|
||||
|
||||
@@ -176,6 +184,7 @@ void TELY_DLL_Init(void *user_data)
|
||||
game->terry_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_resized_25%"));
|
||||
game->terry_merchant_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_merchant_resized_25%"));
|
||||
game->smoochie_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("smoochie_resized_25%"));
|
||||
game->clinger_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("clinger_resized_25%"));
|
||||
}
|
||||
|
||||
game->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
|
||||
@@ -589,6 +598,116 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Clinger: {
|
||||
FP_EntityClingerState *state = DQN_CAST(FP_EntityClingerState *)&action->state;
|
||||
TELY_AssetSpriteSheet *sheet = &game->clinger_sprite_sheet;
|
||||
|
||||
switch (*state) {
|
||||
case FP_EntityClingerState_Nil: {
|
||||
action->next_state = FP_EntityClingerState_Idle;
|
||||
} break;
|
||||
|
||||
case FP_EntityClingerState_Idle: {
|
||||
if (entering_new_state) {
|
||||
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.clinger_walk_down, TELY_AssetFlip_No);
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
||||
}
|
||||
|
||||
if (we_are_clicked_entity) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
action->next_state = FP_EntityClingerState_Attack;
|
||||
} else if (dir_vector.x || dir_vector.y) {
|
||||
action->next_state = FP_EntityClingerState_Run;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity_has_velocity) {
|
||||
action->next_state = FP_EntityClingerState_Run;
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityClingerState_Attack: {
|
||||
uint32_t asset_flip = {};
|
||||
Dqn_String8 desired_action_name = {};
|
||||
switch (entity->direction) {
|
||||
case FP_GameDirection_Up: desired_action_name = g_anim_names.clinger_attack_up; break;
|
||||
case FP_GameDirection_Down: desired_action_name = g_anim_names.clinger_attack_down; break;
|
||||
case FP_GameDirection_Left: desired_action_name = g_anim_names.clinger_attack_side; asset_flip |= TELY_AssetFlip_X; break;
|
||||
case FP_GameDirection_Right: desired_action_name = g_anim_names.clinger_attack_side; break;
|
||||
}
|
||||
|
||||
if (entering_new_state) {
|
||||
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, desired_action_name, DQN_CAST(TELY_AssetFlip)asset_flip);
|
||||
uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
||||
}
|
||||
|
||||
if (action_has_finished)
|
||||
action->next_state = FP_EntityClingerState_Idle;
|
||||
} break;
|
||||
|
||||
case FP_EntityClingerState_Death: {
|
||||
// TODO(doyle): Implement
|
||||
} break;
|
||||
|
||||
case FP_EntityClingerState_Run: {
|
||||
Dqn_String8 desired_action_name = {};
|
||||
switch (entity->direction) {
|
||||
case FP_GameDirection_Up: desired_action_name = g_anim_names.clinger_walk_up; break;
|
||||
case FP_GameDirection_Down: desired_action_name = g_anim_names.clinger_walk_down; break;
|
||||
case FP_GameDirection_Left: desired_action_name = g_anim_names.clinger_walk_down; break;
|
||||
case FP_GameDirection_Right: desired_action_name = g_anim_names.clinger_walk_down; break;
|
||||
}
|
||||
|
||||
if (entering_new_state || action->sprite.anim->label != desired_action_name) {
|
||||
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, desired_action_name, TELY_AssetFlip_No);
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
||||
}
|
||||
|
||||
if (we_are_clicked_entity) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
action->next_state = FP_EntityClingerState_Attack;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entity_has_velocity) {
|
||||
action->next_state = FP_EntityClingerState_Idle;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if (*state == FP_EntityClingerState_Attack) { // NOTE: Position the attack box
|
||||
entity->attack_box_size = entity->local_hit_box_size;
|
||||
switch (entity->direction) {
|
||||
case FP_GameDirection_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);
|
||||
} break;
|
||||
|
||||
case FP_GameDirection_Right: {
|
||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->attack_box_size.w,
|
||||
entity->local_hit_box_offset.y);
|
||||
} break;
|
||||
|
||||
case FP_GameDirection_Up: {
|
||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
||||
entity->local_hit_box_offset.y - entity->attack_box_size.h);
|
||||
} break;
|
||||
|
||||
case FP_GameDirection_Down: {
|
||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
||||
entity->local_hit_box_offset.y + entity->attack_box_size.h);
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
entity->attack_box_size = {};
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Merchant: {
|
||||
FP_EntityTerryMerchantState *state = DQN_CAST(FP_EntityTerryMerchantState *)&action->state;
|
||||
TELY_AssetSpriteSheet *sheet = &game->terry_merchant_sprite_sheet;
|
||||
@@ -776,7 +895,14 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
// from the entity's current state
|
||||
entity->action.next_state = FP_EntitySmoochieState_AttackDown;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Clinger: {
|
||||
entity->action.next_state = FP_EntityClingerState_Attack;
|
||||
} break;
|
||||
}
|
||||
|
||||
// NOTE: Aggro makes the entity attack Terry, we will
|
||||
// exit here preserving the waypoint in the entity.
|
||||
break;
|
||||
} else {
|
||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
||||
@@ -952,7 +1078,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
|
||||
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
FP_SentinelListLink<FP_GameEntityHandle> *link = FP_SentinelList_Make(&entity->spawn_list, game->chunk_pool);
|
||||
link->data = FP_Entity_CreateSmoochie(game, entity_world_pos, "Smoochie");
|
||||
|
||||
if (Dqn_PCG32_NextF32(&game->rng) >= 0.5f)
|
||||
link->data = FP_Entity_CreateClinger(game, entity_world_pos, "Clinger");
|
||||
else
|
||||
link->data = FP_Entity_CreateSmoochie(game, entity_world_pos, "Smoochie");
|
||||
|
||||
// NOTE: Setup the mob with waypoints
|
||||
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
||||
|
||||
@@ -35,6 +35,25 @@ static FP_GameEntityHandle FP_Entity_CreateWaypointF(FP_Game *game, Dqn_V2 pos,
|
||||
return result;
|
||||
}
|
||||
|
||||
static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
|
||||
FP_GameEntityHandle result = entity->handle;
|
||||
va_end(args);
|
||||
|
||||
entity->type = FP_EntityType_Clinger;
|
||||
entity->hp = 3;
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height.meters = 1.6f;
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.7f);
|
||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
return result;
|
||||
}
|
||||
|
||||
static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
@@ -9,6 +9,7 @@ enum FP_EntityType
|
||||
FP_EntityType_Terry,
|
||||
FP_EntityType_Smoochie,
|
||||
FP_EntityType_Merchant,
|
||||
FP_EntityType_Clinger,
|
||||
};
|
||||
|
||||
enum FP_EntityTerryState
|
||||
@@ -32,6 +33,15 @@ enum FP_EntitySmoochieState
|
||||
FP_EntitySmoochieState_Run,
|
||||
};
|
||||
|
||||
enum FP_EntityClingerState
|
||||
{
|
||||
FP_EntityClingerState_Nil,
|
||||
FP_EntityClingerState_Idle,
|
||||
FP_EntityClingerState_Attack,
|
||||
FP_EntityClingerState_Death,
|
||||
FP_EntityClingerState_Run,
|
||||
};
|
||||
|
||||
enum FP_EntityTerryMerchantState
|
||||
{
|
||||
FP_EntityTerryMerchantState_Nil,
|
||||
|
||||
@@ -192,6 +192,7 @@ struct FP_Game
|
||||
TELY_AssetSpriteSheet terry_sprite_sheet;
|
||||
TELY_AssetSpriteSheet smoochie_sprite_sheet;
|
||||
TELY_AssetSpriteSheet terry_merchant_sprite_sheet;
|
||||
TELY_AssetSpriteSheet clinger_sprite_sheet;
|
||||
|
||||
FP_GameEntity *root_entity;
|
||||
FP_GameEntity *entity_free_list;
|
||||
|
||||
Reference in New Issue
Block a user