fp: Integrate perry assets

This commit is contained in:
doyle 2023-10-21 16:30:15 +11:00
parent 253d2826c9
commit 5d37c49f51
68 changed files with 359 additions and 80 deletions

BIN
Data/Textures/atlas.png (Stored with Git LFS)

Binary file not shown.

BIN
Data/Textures/atlas.txt (Stored with Git LFS)

Binary file not shown.

BIN
Data/Textures/atlas/end_screen.png (Stored with Git LFS)

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_down_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_down_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_down_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_phone_down_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_phone_down_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_phone_side_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_phone_side_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_phone_up_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_phone_up_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_side_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_side_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_side_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_up_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_up_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_attack_up_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_death_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_death_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_death_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_death_4.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_death_5.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_ghost_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_ghost_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_ghost_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_pat_dog_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_pat_dog_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_4.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_5.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_6.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_7.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_8.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_down_9.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_4.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_5.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_6.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_7.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_idle_8.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_4.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_5.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_6.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_right_7.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_up_1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_up_2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_up_3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_up_4.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_up_5.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/atlas/perry_walk_up_6.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Data/Textures/sprite_spec.txt (Stored with Git LFS)

Binary file not shown.

View File

@ -10,4 +10,4 @@ if not exist "%sprite_packer%" (
exit /B 1
)
%sprite_packer% 5100x5100 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\atlas || exit /b 1
%sprite_packer% 5200x5200 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\atlas || exit /b 1

View File

@ -91,10 +91,7 @@ static TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform,
static void FP_SetDefaultGamepadBindings(FP_GameControls *controls)
{
controls->up.gamepad_key = TELY_PlatformInputGamepadKey_DUp;
controls->down.gamepad_key = TELY_PlatformInputGamepadKey_DDown;
controls->left.gamepad_key = TELY_PlatformInputGamepadKey_DLeft;
controls->right.gamepad_key = TELY_PlatformInputGamepadKey_DRight;
// NOTE: Note up/down/left/right uses analog sticks, non-negotiable.
controls->attack.gamepad_key = TELY_PlatformInputGamepadKey_X;
controls->range_attack.gamepad_key = TELY_PlatformInputGamepadKey_Y;
controls->build_mode.gamepad_key = TELY_PlatformInputGamepadKey_L3;
@ -102,6 +99,8 @@ static void FP_SetDefaultGamepadBindings(FP_GameControls *controls)
controls->dash.gamepad_key = TELY_PlatformInputGamepadKey_A;
controls->buy_building.gamepad_key = TELY_PlatformInputGamepadKey_LeftBumper;
controls->buy_upgrade.gamepad_key = TELY_PlatformInputGamepadKey_RightBumper;
controls->move_building_ui_cursor_left.gamepad_key = TELY_PlatformInputGamepadKey_DLeft;
controls->move_building_ui_cursor_right.gamepad_key = TELY_PlatformInputGamepadKey_DRight;
}
static bool FP_ListenForNewPlayer(TELY_PlatformInput *input, FP_Game *game)
@ -123,7 +122,14 @@ static bool FP_ListenForNewPlayer(TELY_PlatformInput *input, FP_Game *game)
bool gamepad_pressed = TELY_Platform_InputGamepadKeyIsPressed(input, gamepad_index, TELY_PlatformInputGamepadKey_Start);
if (keyboard_pressed || gamepad_pressed) {
FP_GameEntityHandle terry_handle = FP_Entity_CreateTerry(game, Dqn_V2_InitNx2(1380, 11), "Terry");
FP_GameEntityHandle terry_handle = {};
if (play->players.size) {
FP_GameEntityHandle player = play->players.data[play->players.size - 1];
Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, player);
terry_handle = FP_Entity_CreatePerry(game, player_pos, "Perry");
} else {
terry_handle = FP_Entity_CreateTerry(game, Dqn_V2_InitNx2(1380, 11), "Perry");
}
Dqn_FArray_Add(&play->players, terry_handle);
FP_GameEntity *terry = FP_Game_GetEntity(game, terry_handle);
@ -142,6 +148,8 @@ static bool FP_ListenForNewPlayer(TELY_PlatformInput *input, FP_Game *game)
controls->dash.scan_code = TELY_PlatformInputScanCode_V;
controls->buy_building.scan_code = TELY_PlatformInputScanCode_T;
controls->buy_upgrade.scan_code = TELY_PlatformInputScanCode_Y;
controls->move_building_ui_cursor_left.scan_code = TELY_PlatformInputScanCode_Q;
controls->move_building_ui_cursor_right.scan_code = TELY_PlatformInputScanCode_E;
} else {
controls->up.scan_code = TELY_PlatformInputScanCode_O;
controls->down.scan_code = TELY_PlatformInputScanCode_L;
@ -154,6 +162,8 @@ static bool FP_ListenForNewPlayer(TELY_PlatformInput *input, FP_Game *game)
controls->dash.scan_code = TELY_PlatformInputScanCode_Down;
controls->buy_building.scan_code = TELY_PlatformInputScanCode_Backslash;
controls->buy_upgrade.scan_code = TELY_PlatformInputScanCode_Apostrophe;
controls->move_building_ui_cursor_left.scan_code = TELY_PlatformInputScanCode_I;
controls->move_building_ui_cursor_right.scan_code = TELY_PlatformInputScanCode_P;
}
} else {
controls->mode = FP_GameControlMode_Gamepad;
@ -506,9 +516,9 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, entity->action.state, entity->direction);
switch (entity->type) {
case FP_EntityType_Perry: /*FALLTHRU*/
case FP_EntityType_Terry: {
FP_EntityTerryState *state = DQN_CAST(FP_EntityTerryState *) & action->state;
{
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
if (!FP_Game_IsNilEntity(portal_monkey)) {
@ -537,7 +547,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
// NOTE: Check if we are nearby a monkey and picking it up
FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle);
if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 2.f))) {
if (TELY_Platform_InputScanCodeIsPressed(input, controls->attack.scan_code)) {
if (FP_Game_KeyBindIsPressed(input, controls, controls->attack)) {
entity->carried_monkey = closest_monkey.entity;
picked_up_monkey_this_frame = true;
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Monkey], 1.f);
@ -552,7 +562,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_RangeAttack);
}
} else {
if (!picked_up_monkey_this_frame && TELY_Platform_InputScanCodeIsPressed(input, controls->attack.scan_code)) {
if (!picked_up_monkey_this_frame && FP_Game_KeyBindIsPressed(input, controls, controls->attack)) {
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
portal_monkey->local_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
entity->carried_monkey = {};
@ -600,7 +610,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
// NOTE: Check if we are nearby a monkey and picking it up
FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle);
if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 2.f))) {
if (TELY_Platform_InputScanCodeIsPressed(input, controls->attack.scan_code)) {
if (FP_Game_KeyBindIsPressed(input, controls, controls->attack)) {
entity->carried_monkey = closest_monkey.entity;
picked_up_monkey_this_frame = true;
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Monkey], 1.f);
@ -1207,6 +1217,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
case FP_EntityType_Count: break;
case FP_EntityType_Terry: /*FALLTHRU*/
case FP_EntityType_Perry: /*FALLTHRU*/
case FP_EntityType_Catfish: /*FALLTHRU*/
case FP_EntityType_Clinger: /*FALLTHRU*/
case FP_EntityType_Smoochie: {
@ -1220,7 +1231,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} else if (entity->type == FP_EntityType_Smoochie) {
is_attacking = entity->action.state == FP_EntitySmoochieState_Attack;
} else {
DQN_ASSERT(entity->type == FP_EntityType_Terry);
DQN_ASSERT(entity->type == FP_EntityType_Terry || entity->type == FP_EntityType_Perry);
is_range_attack = entity->action.state == FP_EntityTerryState_RangeAttack;
is_attacking = is_range_attack || entity->action.state == FP_EntityTerryState_Attack;
}
@ -1286,16 +1297,20 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
game->play.clock_ms = DQN_CAST(uint64_t)(platform->input.timer_s * 1000.f);
Dqn_ProfilerZone update_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: Entity loop"), FP_ProfileZone_FPUpdate_EntityLoop);
if (game->play.state == FP_GameState_Play) {
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4127) // Conditional expression is constant 'FP_DEVELOPER_MODE'
if (FP_DEVELOPER_MODE && TELY_Platform_InputKeyIsReleased(input->mouse_left))
game->play.clicked_entity = game->play.prev_active_entity;
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Escape))
game->play.state = FP_GameState_Pause;
if (game->play.clicked_entity.id) {
if (FP_DEVELOPER_MODE && game->play.clicked_entity.id) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete))
FP_Game_DeleteEntity(game, game->play.clicked_entity);
}
DQN_MSVC_WARNING_POP
// NOTE: Handle input ==========================================================================
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) {
@ -1319,12 +1334,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
dir_vector.x = +1.f;
// NOTE: Gamepad movement input
// NOTE: button_codes 0 should be the first gamepad connected, we can
// get this working with other gamepads later
uint32_t gamepad = 0;
if (input->button_codes[gamepad]) {
dir_vector.x += input->left_stick[gamepad].x;
dir_vector.y += input->left_stick[gamepad].y;
if (controls->mode == FP_GameControlMode_Gamepad) {
dir_vector.x += input->left_stick[controls->gamepad_index].x;
dir_vector.y += input->left_stick[controls->gamepad_index].y;
}
// TODO(doyle): Some bug, diagonal is still faster
@ -1339,6 +1351,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (entity->flags & (FP_GameEntityFlag_MoveByKeyboard | FP_GameEntityFlag_MoveByGamepad)) {
bool move_entity = true;
switch (entity->type) {
case FP_EntityType_Perry: /*FALLTHRU*/
case FP_EntityType_Terry: {
auto *state = DQN_CAST(FP_EntityTerryState *)&entity->action.state;
move_entity = *state == FP_EntityTerryState_Run || *state == FP_EntityTerryState_Idle;
@ -1377,21 +1390,19 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
case FP_EntityType_KennelTerry: break;
case FP_EntityType_PhoneMessageProjectile: break;
case FP_EntityType_AirportTerryPlane: break;
case FP_EntityType_MobSpawner:
case FP_EntityType_MobSpawner: break;
case FP_EntityType_PortalMonkey: break;
case FP_EntityType_Billboard: break;
}
if (move_entity)
acceleration_meters_per_s = dir_vector * entity->base_acceleration_per_s.meters;
acceleration_meters_per_s += dir_vector * entity->base_acceleration_per_s.meters;
}
if (dir_vector == Dqn_V2_Zero) {
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 =============================================================
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
@ -1425,7 +1436,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (dist_to_defender > (aggro_dist_threshold * 1.5f)) {
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
FP_GameEntity *maybe_terry = FP_Game_GetEntity(game, link->data.entity);
if (maybe_terry->type == FP_EntityType_Terry) {
if (maybe_terry->type == FP_EntityType_Terry || maybe_terry->type == FP_EntityType_Perry) {
link = FP_SentinelList_Erase(&entity->waypoints, link, game->play.chunk_pool);
}
}
@ -1621,7 +1632,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
acceleration_meters_per_s = entity_to_waypoint_norm * (entity->base_acceleration_per_s.meters * 0.25f);
if (entity->type == FP_EntityType_Smoochie) {
if (waypoint_entity->type == FP_EntityType_Terry) {
if (waypoint_entity->type == FP_EntityType_Terry || waypoint_entity->type == FP_EntityType_Perry) {
if (dist_to_waypoint_sq < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 2.f)) &&
!entity->smoochie_has_teleported) {
@ -1731,13 +1742,14 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (can_attack) {
switch (entity->type) {
case FP_EntityType_Perry: /*FALLTHRU*/
case FP_EntityType_Terry: /*FALLTHRU*/
case FP_EntityType_Smoochie: /*FALLTHRU*/
case FP_EntityType_Catfish: /*FALLTHRU*/
case FP_EntityType_Clinger: {
// TODO(doyle): We should check if it's valid to enter this new state
// from the entity's current state
if (entity->type == FP_EntityType_Terry) {
if (entity->type == FP_EntityType_Terry || entity->type == FP_EntityType_Perry) {
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack);
} else if (entity->type == FP_EntityType_Smoochie) {
FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Attack);
@ -1803,7 +1815,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
// NOTE: Building selector =========================================================
Dqn_usize const last_building_index = DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS) - 1;
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Q)) {
if (FP_Game_KeyBindIsPressed(input, controls, controls->move_building_ui_cursor_left)) {
if (entity->build_mode_building_index <= 0) {
entity->build_mode_building_index = last_building_index;
} else {
@ -1811,7 +1823,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
}
}
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_E)) {
if (FP_Game_KeyBindIsPressed(input, controls, controls->move_building_ui_cursor_left)) {
if (entity->build_mode_building_index >= last_building_index) {
entity->build_mode_building_index = 0;
} else {
@ -3084,7 +3096,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
DQN_DEFER { TELY_Render_PopTransform(renderer); };
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, FP_EntityType_Terry, FP_EntityTerryState_Idle, FP_GameDirection_Down);
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, player->type, 0 /*state, usually 0 is idle*/, FP_GameDirection_Down);
player_avatar_rect.size = render_data.render_size;
TELY_Render_TextureColourV4(renderer,
@ -3623,7 +3635,11 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
}
// NOTE: Debug UI ==============================================================================
if (game->play.debug_ui) {
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4127) // Conditional expression is constant 'FP_DEVELOPER_MODE'
if (FP_DEVELOPER_MODE && game->play.debug_ui) {
DQN_MSVC_WARNING_POP
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
DQN_DEFER { TELY_Render_PopTransform(renderer); };
@ -3755,7 +3771,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
TELY_RFui_Flush(rfui, renderer, input, assets);
}
if (game->play.state == FP_GameState_Play) {
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4127) // Conditional expression is constant 'FP_DEVELOPER_MODE'
if (FP_DEVELOPER_MODE && game->play.state == FP_GameState_Play) {
DQN_MSVC_WARNING_POP
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
DQN_DEFER { TELY_Render_PopTransform(renderer); };

View File

@ -127,6 +127,26 @@ struct FP_GlobalAnimations
Dqn_String8 terry_walk_down = DQN_STRING8("terry_walk_down");
Dqn_String8 terry_walk_left = DQN_STRING8("terry_walk_left");
Dqn_String8 terry_walk_right = DQN_STRING8("terry_walk_right");
Dqn_String8 perry_attack_up = DQN_STRING8("perry_attack_up");
Dqn_String8 perry_attack_side = DQN_STRING8("perry_attack_side");
Dqn_String8 perry_attack_down = DQN_STRING8("perry_attack_down");
Dqn_String8 perry_attack_phone_up = DQN_STRING8("perry_attack_phone_up");
Dqn_String8 perry_attack_phone_side = DQN_STRING8("perry_attack_phone_side");
Dqn_String8 perry_attack_phone_down = DQN_STRING8("perry_attack_phone_down");
Dqn_String8 perry_death = DQN_STRING8("perry_death");
Dqn_String8 perry_pat_dog = DQN_STRING8("perry_pat_dog");
Dqn_String8 perry_ghost = DQN_STRING8("perry_ghost");
Dqn_String8 perry_walk_idle = DQN_STRING8("perry_walk_idle");
Dqn_String8 perry_walk_up = DQN_STRING8("perry_walk_up");
Dqn_String8 perry_walk_down = DQN_STRING8("perry_walk_down");
Dqn_String8 perry_walk_right = DQN_STRING8("perry_walk_right");
}
g_anim_names;
#if defined(DQN_PLATFORM_EMSCRIPTEN)
#define FP_DEVELOPER_MODE 0
#else
#define FP_DEVELOPER_MODE 0
#endif

View File

@ -83,6 +83,60 @@ static FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType
}
} break;
case FP_EntityType_Perry: {
result.height.meters = 1.72f;
FP_EntityPerryState state = DQN_CAST(FP_EntityPerryState)raw_state;
switch (state) {
case FP_EntityTerryState_Idle: {
result.anim_name = g_anim_names.perry_walk_idle;
} break;
case FP_EntityTerryState_Attack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.perry_attack_up; result.height.meters *= 1.5f; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.perry_attack_down; result.height.meters *= 1.6f; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.perry_attack_side; result.height.meters *= 1.5f; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.perry_attack_side; result.height.meters *= 1.5f; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_RangeAttack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.perry_attack_phone_up; result.height.meters *= 1.35f; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.perry_attack_phone_down; result.height.meters *= 1.35f; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.perry_attack_phone_side; result.height.meters *= 1.35f; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.perry_attack_phone_side; result.height.meters *= 1.35f; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_Dash: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.perry_ghost; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.perry_ghost; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.perry_ghost; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.perry_ghost; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_Run: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.perry_walk_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.perry_walk_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.perry_walk_right; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.perry_walk_right; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_DeadGhost: {
result.anim_name = g_anim_names.perry_death; break;
} break;
}
} break;
case FP_EntityType_Smoochie: {
result.height.meters = 1.6f;
FP_EntitySmoochieState state = DQN_CAST(FP_EntitySmoochieState)raw_state;

View File

@ -23,6 +23,7 @@ enum FP_EntityType
FP_EntityType_PortalMonkey,
FP_EntityType_Smoochie,
FP_EntityType_Terry,
FP_EntityType_Perry,
FP_EntityType_PhoneMessageProjectile,
FP_EntityType_Billboard,
FP_EntityType_Count,
@ -37,6 +38,7 @@ enum FP_EntityTerryState
FP_EntityTerryState_Dash,
FP_EntityTerryState_DeadGhost,
};
typedef FP_EntityTerryState FP_EntityPerryState;
enum FP_EntityMobSpawnerState
{

View File

@ -167,15 +167,12 @@ static FP_GameEntityHandle FP_Entity_CreateMobSpawner(FP_Game *game, Dqn_V2 pos,
return result;
}
static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
static FP_GameEntityHandle FP_Entity_CreateTerryInternal(FP_Game *game, Dqn_V2 pos, bool is_perry, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
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_Terry;
entity->type = is_perry ? FP_EntityType_Perry : FP_EntityType_Terry;
entity->local_pos = pos;
entity->base_acceleration_per_s.meters = 16.f;
@ -199,6 +196,24 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_
return result;
}
static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntityHandle result = FP_Entity_CreateTerryInternal(game, pos, false /*is_perry*/, fmt, args);
va_end(args);
return result;
}
static FP_GameEntityHandle FP_Entity_CreatePerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntityHandle result = FP_Entity_CreateTerryInternal(game, pos, true /*is_perry*/, fmt, args);
va_end(args);
return result;
}
static FP_GameEntityHandle FP_Entity_CreateMerchantTerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;

View File

@ -825,6 +825,7 @@ static bool FP_Game_CanEntityAttack(FP_GameEntity *entity, uint64_t current_time
static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity, uint32_t desired_state)
{
switch (entity->type) {
case FP_EntityType_Perry: /*FALLTHRU*/
case FP_EntityType_Terry: {
if (desired_state == FP_EntityTerryState_Attack ||
desired_state == FP_EntityTerryState_RangeAttack) {
@ -979,6 +980,7 @@ static FP_GameCanMoveToPositionResult FP_Game_CanEntityMoveToPosition(FP_Game *g
}
} break;
case FP_EntityType_Perry: /*FALLTHRU*/
case FP_EntityType_Terry: {
// NOTE: Don't collide with mobs when dashing (e.g. phase through)
FP_EntityTerryState state = *DQN_CAST(FP_EntityTerryState *)&entity->action.state;

View File

@ -188,6 +188,8 @@ struct FP_GameControls
FP_GameKeyBind attack;
FP_GameKeyBind buy_building;
FP_GameKeyBind buy_upgrade;
FP_GameKeyBind move_building_ui_cursor_left;
FP_GameKeyBind move_building_ui_cursor_right;
FP_GameKeyBind range_attack;
FP_GameKeyBind build_mode;
FP_GameKeyBind strafe;

Binary file not shown.