fp: Improve tutorial, add a wave break every 3 waves
This commit is contained in:
parent
285cc9b5ad
commit
11d5f34522
296
feely_pona.cpp
296
feely_pona.cpp
@ -138,9 +138,9 @@ static void FP_SetDefaultGamepadBindings(FP_GameControls *controls)
|
|||||||
controls->move_building_ui_cursor_right.gamepad_key = TELY_OSInputGamepadKey_DRight;
|
controls->move_building_ui_cursor_right.gamepad_key = TELY_OSInputGamepadKey_DRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool FP_ListenForNewPlayer(TELY_OSInput *input, FP_Game *game)
|
static FP_ListenForNewPlayerResult FP_ListenForNewPlayer(TELY_OSInput *input, FP_Game *game, bool tutorial_is_allowed)
|
||||||
{
|
{
|
||||||
bool result = false;
|
FP_ListenForNewPlayerResult result = {};
|
||||||
if (game->play.players.size == 2)
|
if (game->play.players.size == 2)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -162,6 +162,18 @@ static bool FP_ListenForNewPlayer(TELY_OSInput *input, FP_Game *game)
|
|||||||
bool keyboard_pressed = !keyboard_already_allocated && TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_B);
|
bool keyboard_pressed = !keyboard_already_allocated && TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_B);
|
||||||
bool gamepad_pressed = TELY_OSInput_GamepadKeyIsPressed(input, gamepad_index, TELY_OSInputGamepadKey_Start);
|
bool gamepad_pressed = TELY_OSInput_GamepadKeyIsPressed(input, gamepad_index, TELY_OSInputGamepadKey_Start);
|
||||||
|
|
||||||
|
if (tutorial_is_allowed) {
|
||||||
|
if (!keyboard_already_allocated && TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_T)) {
|
||||||
|
keyboard_pressed = true;
|
||||||
|
result.tutorial_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TELY_OSInput_GamepadKeyIsPressed(input, gamepad_index, TELY_OSInputGamepadKey_Select)) {
|
||||||
|
gamepad_pressed = true;
|
||||||
|
result.tutorial_requested = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (keyboard_pressed || gamepad_pressed) {
|
if (keyboard_pressed || gamepad_pressed) {
|
||||||
FP_GameEntityHandle terry_handle = {};
|
FP_GameEntityHandle terry_handle = {};
|
||||||
if (play->players.size) {
|
if (play->players.size) {
|
||||||
@ -197,11 +209,13 @@ static bool FP_ListenForNewPlayer(TELY_OSInput *input, FP_Game *game)
|
|||||||
FP_SetDefaultGamepadBindings(controls);
|
FP_SetDefaultGamepadBindings(controls);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = true;
|
result.yes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t const FP_COOLDOWN_WAVE_TIME_MS = 30'000;
|
||||||
static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
||||||
{
|
{
|
||||||
FP_GamePlay *play = &game->play;
|
FP_GamePlay *play = &game->play;
|
||||||
@ -225,6 +239,17 @@ static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
|||||||
Dqn_FArray_Add(&play->parent_entity_stack, play->root_entity->handle);
|
Dqn_FArray_Add(&play->parent_entity_stack, play->root_entity->handle);
|
||||||
Dqn_PCG32_Seed(&play->rng, os->core.epoch_time);
|
Dqn_PCG32_Seed(&play->rng, os->core.epoch_time);
|
||||||
|
|
||||||
|
// NOTE: Seed the shuffled list with indexes
|
||||||
|
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(play->monkey_spawn_shuffled_list)) {
|
||||||
|
play->monkey_spawn_shuffled_list[index] = DQN_CAST(uint8_t)index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Fisher yates shuffle the list
|
||||||
|
for (Dqn_usize index = DQN_ARRAY_UCOUNT(play->monkey_spawn_shuffled_list) - 1; index < DQN_ARRAY_UCOUNT(play->monkey_spawn_shuffled_list); index--) {
|
||||||
|
uint32_t swap_index = Dqn_PCG32_Range(&play->rng, 0, DQN_CAST(uint32_t)index + 1);
|
||||||
|
DQN_SWAP(play->monkey_spawn_shuffled_list[swap_index], play->monkey_spawn_shuffled_list[index]);
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Map ===================================================================================
|
// NOTE: Map ===================================================================================
|
||||||
{
|
{
|
||||||
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.map);
|
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.map);
|
||||||
@ -340,18 +365,6 @@ static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Monkey ============================================================
|
|
||||||
{
|
|
||||||
Dqn_V2 monkey_base_p = Dqn_V2_InitNx2(base_mid_p.x + 400.f, base_mid_p.y + 16.f);
|
|
||||||
FP_GameEntityHandle monkey_a = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y), "Monkey A");
|
|
||||||
FP_GameEntityHandle monkey_b = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y + 100.f), "Monkey B");
|
|
||||||
FP_GameEntityHandle monkey_c = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y - 100.f), "Monkey C");
|
|
||||||
|
|
||||||
Dqn_FArray_Add(&play->portal_monkeys, monkey_a);
|
|
||||||
Dqn_FArray_Add(&play->portal_monkeys, monkey_b);
|
|
||||||
Dqn_FArray_Add(&play->portal_monkeys, monkey_c);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Dqn_V2 base_top_left_pos = Dqn_V2_InitNx2(1018, -335);
|
Dqn_V2 base_top_left_pos = Dqn_V2_InitNx2(1018, -335);
|
||||||
Dqn_V2 base_bottom_right_pos = Dqn_V2_InitNx2(2050, +351);
|
Dqn_V2 base_bottom_right_pos = Dqn_V2_InitNx2(2050, +351);
|
||||||
@ -384,7 +397,7 @@ static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
|||||||
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(2047, -720), FP_EntityBillboardState_RangeAttack, "Range Attack Billboard");
|
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(2047, -720), FP_EntityBillboardState_RangeAttack, "Range Attack Billboard");
|
||||||
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(-936, -500), FP_EntityBillboardState_Monkey, "Monkey Billboard");
|
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(-936, -500), FP_EntityBillboardState_Monkey, "Monkey Billboard");
|
||||||
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(1898, 771), FP_EntityBillboardState_Strafe, "Strafe Billboard");
|
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(1898, 771), FP_EntityBillboardState_Strafe, "Strafe Billboard");
|
||||||
FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(1068, 619), FP_EntityBillboardState_Build, "Build Billboard");
|
play->billboard_build = FP_Entity_CreateBillboard(game, Dqn_V2_InitNx2(1068, 619), FP_EntityBillboardState_Build, "Build Billboard");
|
||||||
|
|
||||||
// NOTE: Camera ================================================================================
|
// NOTE: Camera ================================================================================
|
||||||
play->camera.world_pos = {};
|
play->camera.world_pos = {};
|
||||||
@ -412,7 +425,7 @@ void TELY_OS_DLLInit(TELY_OS *os)
|
|||||||
|
|
||||||
Dqn_f32 font_scalar = FP_TARGET_VIEWPORT_SIZE.w / DQN_CAST(Dqn_f32)os->core.window_size.x;
|
Dqn_f32 font_scalar = FP_TARGET_VIEWPORT_SIZE.w / DQN_CAST(Dqn_f32)os->core.window_size.x;
|
||||||
game->font_size = DQN_CAST(uint16_t)(18 * font_scalar);
|
game->font_size = DQN_CAST(uint16_t)(18 * font_scalar);
|
||||||
game->large_font_size = DQN_CAST(uint16_t)(game->font_size * .5f);
|
game->large_font_size = DQN_CAST(uint16_t)(game->font_size * 5.f);
|
||||||
game->large_talkco_font_size = DQN_CAST(uint16_t)(game->font_size * 1.5f);
|
game->large_talkco_font_size = DQN_CAST(uint16_t)(game->font_size * 1.5f);
|
||||||
game->xlarge_talkco_font_size = DQN_CAST(uint16_t)(game->font_size * 2.f);
|
game->xlarge_talkco_font_size = DQN_CAST(uint16_t)(game->font_size * 2.f);
|
||||||
|
|
||||||
@ -521,7 +534,7 @@ static void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_O
|
|||||||
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
|
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
|
||||||
if (!FP_Game_IsNilEntity(portal_monkey)) {
|
if (!FP_Game_IsNilEntity(portal_monkey)) {
|
||||||
Dqn_Rect hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
Dqn_Rect hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
Dqn_f32 trig_t = DQN_CAST(Dqn_f32)input->timer_s * 2.f;
|
Dqn_f32 trig_t = DQN_CAST(Dqn_f32)game->play.clock_ms / 1000.f * 2.f;
|
||||||
Dqn_V2 offset = Dqn_V2_InitNx2(DQN_COSF(trig_t), DQN_SINF(trig_t)) * 18.f;
|
Dqn_V2 offset = Dqn_V2_InitNx2(DQN_COSF(trig_t), DQN_SINF(trig_t)) * 18.f;
|
||||||
portal_monkey->local_pos = Dqn_Rect_InterpolatedPoint(hit_box, Dqn_V2_InitNx2(0.5f, -0.75f)) + offset;
|
portal_monkey->local_pos = Dqn_Rect_InterpolatedPoint(hit_box, Dqn_V2_InitNx2(0.5f, -0.75f)) + offset;
|
||||||
}
|
}
|
||||||
@ -651,23 +664,6 @@ static void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_O
|
|||||||
|
|
||||||
entity->stamina -= FP_TERRY_DASH_STAMINA_COST;
|
entity->stamina -= FP_TERRY_DASH_STAMINA_COST;
|
||||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Woosh], 1.f);
|
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Woosh], 1.f);
|
||||||
|
|
||||||
#if 0
|
|
||||||
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.terry_ghost, TELY_AssetFlip_No);
|
|
||||||
cosmetic_sprite->started_at_clock_ms = game->play.clock_ms;
|
|
||||||
cosmetic_sprite->height.meters = entity->sprite_height.meters;
|
|
||||||
|
|
||||||
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->play.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->play.rng, DQN_CAST(uint32_t)0, max_rng_dist_y);
|
|
||||||
cosmetic_sprite->offset.y = -DQN_CAST(Dqn_f32)rng_y;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entity->action.sprite_alpha = Dqn_PCG32_NextF32(&game->play.rng);
|
entity->action.sprite_alpha = Dqn_PCG32_NextF32(&game->play.rng);
|
||||||
@ -1297,9 +1293,11 @@ static void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_O
|
|||||||
static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audio *audio)
|
static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audio *audio)
|
||||||
{
|
{
|
||||||
Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate);
|
Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate);
|
||||||
|
if (game->play.state == FP_GameState_Pause)
|
||||||
|
return;
|
||||||
|
|
||||||
game->play.update_counter++;
|
game->play.update_counter++;
|
||||||
game->play.clock_ms = DQN_CAST(uint64_t)(os->input.timer_s * 1000.f);
|
game->play.clock_ms += DQN_CAST(uint64_t)(os->input.delta_ms);
|
||||||
|
|
||||||
Dqn_ProfilerZone update_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STR8("FP_Update: Entity loop"), FP_ProfileZone_FPUpdate_EntityLoop);
|
Dqn_ProfilerZone update_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STR8("FP_Update: Entity loop"), FP_ProfileZone_FPUpdate_EntityLoop);
|
||||||
if (game->play.state == FP_GameState_Play && game->play.perry_joined != FP_GamePerryJoins_Enters) {
|
if (game->play.state == FP_GameState_Play && game->play.perry_joined != FP_GamePerryJoins_Enters) {
|
||||||
@ -1309,8 +1307,10 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
|||||||
if (FP_DEVELOPER_MODE && TELY_OSInput_KeyIsReleased(input->mouse_left))
|
if (FP_DEVELOPER_MODE && TELY_OSInput_KeyIsReleased(input->mouse_left))
|
||||||
game->play.clicked_entity = game->play.prev_active_entity;
|
game->play.clicked_entity = game->play.prev_active_entity;
|
||||||
|
|
||||||
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Escape))
|
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Escape)) {
|
||||||
game->play.state = FP_GameState_Pause;
|
game->play.state = FP_GameState_Pause;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (FP_DEVELOPER_MODE && game->play.clicked_entity.id) {
|
if (FP_DEVELOPER_MODE && game->play.clicked_entity.id) {
|
||||||
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Delete))
|
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Delete))
|
||||||
@ -1976,12 +1976,56 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: If all enemies for the current wave have been spawned and the cooldown has elapsed
|
// NOTE: Typically, the game starts the cooldown for the next wave after
|
||||||
// start the next wave.
|
// all enemies are spawned which makes the game fast paced. However, to
|
||||||
if (game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave && game->play.clock_ms >= game->play.wave_cooldown_timestamp_ms) {
|
// give the player a break to reorganise, every 3 waves- we don't start
|
||||||
game->play.enemies_per_wave = DQN_MAX(5 * DQN_CAST(uint32_t)game->play.mob_spawners.size, DQN_CAST(uint32_t)(game->play.enemies_per_wave * 1.5));
|
// the wave countdown until all enemies are killed.
|
||||||
game->play.enemies_spawned_this_wave = 0;
|
bool all_enemies_spawned = game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave;
|
||||||
game->play.current_wave++;
|
bool advance_to_next_wave = false;
|
||||||
|
bool current_wave_is_break_for_player = game->play.current_wave % 3 == 0;
|
||||||
|
if (current_wave_is_break_for_player) {
|
||||||
|
Dqn_usize enemy_count = 0;
|
||||||
|
for (FP_GameEntityHandle spawner_handle : game->play.mob_spawners) {
|
||||||
|
FP_GameEntity *spawner = FP_Game_GetEntity(game, spawner_handle);
|
||||||
|
enemy_count += spawner->spawn_list.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool all_enemies_killed = enemy_count == 0;
|
||||||
|
advance_to_next_wave = all_enemies_spawned && all_enemies_killed;
|
||||||
|
} else {
|
||||||
|
advance_to_next_wave = all_enemies_spawned;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advance_to_next_wave) {
|
||||||
|
// NOTE: If the cooldown timestamp is 0, the wave is complete and we
|
||||||
|
// haven't given the player cooldown yet, so we assign a timestamp for that
|
||||||
|
if (game->play.wave_cooldown_timestamp_ms == 0) {
|
||||||
|
game->play.wave_cooldown_timestamp_ms = game->play.clock_ms + FP_COOLDOWN_WAVE_TIME_MS;
|
||||||
|
} else {
|
||||||
|
// NOTE: Check if cooldown has elapsed, the next wave can start if so
|
||||||
|
if (game->play.clock_ms > game->play.wave_cooldown_timestamp_ms) {
|
||||||
|
game->play.enemies_per_wave = DQN_MAX(5 * DQN_CAST(uint32_t)game->play.mob_spawners.size, DQN_CAST(uint32_t)(game->play.enemies_per_wave * 1.5));
|
||||||
|
game->play.enemies_spawned_this_wave = 0; // Important! Reset the spawn count
|
||||||
|
game->play.wave_cooldown_timestamp_ms = 0; // Important! We reset the timestamp for the next wave
|
||||||
|
|
||||||
|
if (current_wave_is_break_for_player && game->play.current_wave != 0) {
|
||||||
|
// NOTE: We spawn a monkey at these wave intervals;
|
||||||
|
if (game->play.monkey_spawn_count < 3) {
|
||||||
|
DQN_ASSERT(game->play.monkey_spawn_count < DQN_ARRAY_UCOUNT(game->play.monkey_spawn_shuffled_list));
|
||||||
|
uint8_t spawn_pos_index = game->play.monkey_spawn_shuffled_list[game->play.monkey_spawn_count++];
|
||||||
|
DQN_ASSERT(spawn_pos_index < DQN_ARRAY_UCOUNT(FP_MONKEY_SPAWN_LOCATIONS));
|
||||||
|
|
||||||
|
Dqn_V2 spawn_pos = FP_MONKEY_SPAWN_LOCATIONS[spawn_pos_index];
|
||||||
|
FP_GameEntityHandle portal_monkey = FP_Entity_CreatePortalMonkey(game, spawn_pos, "Portal Monkey");
|
||||||
|
Dqn_FArray_Add(&game->play.portal_monkeys, portal_monkey);
|
||||||
|
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Monkey], 1.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game->play.current_wave++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2059,11 +2103,11 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
|||||||
|
|
||||||
if (entity->action.state != FP_EntityMobSpawnerState_Shutdown &&
|
if (entity->action.state != FP_EntityMobSpawnerState_Shutdown &&
|
||||||
game->play.enemies_spawned_this_wave < game->play.enemies_per_wave && entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities
|
game->play.enemies_spawned_this_wave < game->play.enemies_per_wave && entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities
|
||||||
if (input->timer_s >= entity->next_spawn_timestamp_s) {
|
if (game->play.clock_ms >= entity->next_spawn_timestamp_s) {
|
||||||
Dqn_usize spawn_count = DQN_MIN(game->play.current_wave + 1, 8);
|
Dqn_usize spawn_count = DQN_MIN(game->play.current_wave + 1, 8);
|
||||||
for (Dqn_usize spawn_index = 0; spawn_index < spawn_count; spawn_index++) {
|
for (Dqn_usize spawn_index = 0; spawn_index < spawn_count; spawn_index++) {
|
||||||
uint16_t hp_adjustment = DQN_CAST(uint16_t)game->play.current_wave;
|
uint16_t hp_adjustment = DQN_CAST(uint16_t)game->play.current_wave;
|
||||||
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 2.5f);
|
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(game->play.clock_ms + 2.5f);
|
||||||
|
|
||||||
FP_SentinelListLink<FP_GameEntityHandle> *link = FP_SentinelList_Make(&entity->spawn_list, game->play.chunk_pool);
|
FP_SentinelListLink<FP_GameEntityHandle> *link = FP_SentinelList_Make(&entity->spawn_list, game->play.chunk_pool);
|
||||||
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||||
@ -2093,8 +2137,6 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
|||||||
|
|
||||||
FP_AppendMobSpawnerWaypoints(game, entity->handle, mob->handle);
|
FP_AppendMobSpawnerWaypoints(game, entity->handle, mob->handle);
|
||||||
game->play.enemies_spawned_this_wave++;
|
game->play.enemies_spawned_this_wave++;
|
||||||
if (game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave)
|
|
||||||
game->play.wave_cooldown_timestamp_ms = game->play.clock_ms + 30'000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2295,16 +2337,17 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
|||||||
case FP_GameStateTutorial_ShowPlayer: {
|
case FP_GameStateTutorial_ShowPlayer: {
|
||||||
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.players.data[0]) * game->play.camera.scale;
|
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.players.data[0]) * game->play.camera.scale;
|
||||||
if (camera_arrived) {
|
if (camera_arrived) {
|
||||||
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
||||||
game->play.tutorial_wait_end_time_s = input->timer_s + 2.0;
|
game->play.tutorial_wait_end_time_ms = game->play.clock_ms + 3000;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_GameStateTutorial_ShowPortalOneWait: /*FALLTHRU*/
|
case FP_GameStateTutorial_ShowPortalOneWait: /*FALLTHRU*/
|
||||||
case FP_GameStateTutorial_ShowPortalTwoWait: /*FALLTHRU*/
|
case FP_GameStateTutorial_ShowPortalTwoWait: /*FALLTHRU*/
|
||||||
case FP_GameStateTutorial_ShowPortalThreeWait: /*FALLTHRU*/
|
case FP_GameStateTutorial_ShowPortalThreeWait: /*FALLTHRU*/
|
||||||
|
case FP_GameStateTutorial_ShowBillboardBuildWait: /*FALLTHRU*/
|
||||||
case FP_GameStateTutorial_ShowPlayerWait: {
|
case FP_GameStateTutorial_ShowPlayerWait: {
|
||||||
if (input->timer_s > game->play.tutorial_wait_end_time_s) {
|
if (game->play.clock_ms > game->play.tutorial_wait_end_time_ms) {
|
||||||
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -2323,7 +2366,15 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
|||||||
|
|
||||||
if (camera_arrived) {
|
if (camera_arrived) {
|
||||||
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
||||||
game->play.tutorial_wait_end_time_s = input->timer_s + 2.0;
|
game->play.tutorial_wait_end_time_ms = game->play.clock_ms + 2000;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameStateTutorial_ShowBillboardBuild: {
|
||||||
|
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.billboard_build) * game->play.camera.scale;
|
||||||
|
if (camera_arrived) {
|
||||||
|
game->play.tutorial_state = DQN_CAST(FP_GameStateTutorial)(DQN_CAST(uint32_t)game->play.tutorial_state + 1);
|
||||||
|
game->play.tutorial_wait_end_time_ms = game->play.clock_ms + 4000;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -2630,8 +2681,8 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
|
|
||||||
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
|
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
|
||||||
FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index;
|
FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index;
|
||||||
uint64_t elapsed_ms = game->play.clock_ms - sprite->started_at_clock_ms;
|
uint64_t elapsed_ms = game->play.clock_ms - sprite->started_at_clock_ms;
|
||||||
uint16_t raw_anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite->asset.anim->ms_per_frame);
|
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) {
|
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;
|
anim_index = Dqn_FArray_EraseRange(&entity->extra_cosmetic_anims, anim_index, 1, Dqn_ArrayErase_Unstable).it_index;
|
||||||
continue;
|
continue;
|
||||||
@ -2688,13 +2739,13 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
dest_rect.size *= 1.5f;
|
dest_rect.size *= 1.5f;
|
||||||
dest_rect.pos = Dqn_Rect_InterpolatedPoint(world_hit_box,
|
dest_rect.pos = Dqn_Rect_InterpolatedPoint(world_hit_box,
|
||||||
Dqn_V2_InitNx2(0.0f, 0.0f)) - (Dqn_V2_InitNx2(dest_rect.size.x * .6f, dest_rect.size.y * .7f));
|
Dqn_V2_InitNx2(0.0f, 0.0f)) - (Dqn_V2_InitNx2(dest_rect.size.x * .6f, dest_rect.size.y * .7f));
|
||||||
dest_rect.pos.y += (DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 2.f) + 1.f / 2.f) * dest_rect.size.y * .005f;
|
dest_rect.pos.y += (DQN_SINF(DQN_CAST(Dqn_f32)game->play.clock_ms / 1000.f * 2.f) + 1.f / 2.f) * dest_rect.size.y * .005f;
|
||||||
} else {
|
} else {
|
||||||
dest_rect.pos = Dqn_Rect_InterpolatedPoint(world_hit_box,
|
dest_rect.pos = Dqn_Rect_InterpolatedPoint(world_hit_box,
|
||||||
Dqn_V2_InitNx2(0.0f, 0.0f)) - (Dqn_V2_InitNx2(dest_rect.size.x * .7f, dest_rect.size.y * .9f));
|
Dqn_V2_InitNx2(0.0f, 0.0f)) - (Dqn_V2_InitNx2(dest_rect.size.x * .7f, dest_rect.size.y * .9f));
|
||||||
}
|
}
|
||||||
|
|
||||||
dest_rect.pos.y += (DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 2.f) + 1.f / 2.f) * dest_rect.size.y * .1f;
|
dest_rect.pos.y += (DQN_SINF(DQN_CAST(Dqn_f32)game->play.clock_ms / 1000.f * 2.f) + 1.f / 2.f) * dest_rect.size.y * .1f;
|
||||||
|
|
||||||
TELY_Render_TextureColourV4(renderer,
|
TELY_Render_TextureColourV4(renderer,
|
||||||
game->atlas_sprite_sheet.tex_handle,
|
game->atlas_sprite_sheet.tex_handle,
|
||||||
@ -2960,7 +3011,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
game->play.global_camera_trauma01 = 1.f;
|
game->play.global_camera_trauma01 = 1.f;
|
||||||
game->play.perry_join_splash_screen_shake_triggered = true;
|
game->play.perry_join_splash_screen_shake_triggered = true;
|
||||||
game->play.perry_join_flash_alpha = 1.f;
|
game->play.perry_join_flash_alpha = 1.f;
|
||||||
game->play.perry_join_splash_screen_end_ms = DQN_CAST(uint64_t)((DQN_CAST(Dqn_f32)input->timer_s * 1000.f) + 2000);
|
game->play.perry_join_splash_screen_end_ms = game->play.clock_ms + 2000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2975,7 +3026,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.intro_screen_perry_joins_the_fight);
|
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.intro_screen_perry_joins_the_fight);
|
||||||
Dqn_Rect tex_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
Dqn_Rect tex_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
||||||
|
|
||||||
Dqn_f32 sin_t = (DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 3.f) + 1) / 2.f;
|
Dqn_f32 sin_t = (DQN_SINF(DQN_CAST(Dqn_f32)game->play.clock_ms / 1000.f * 3.f) + 1) / 2.f;
|
||||||
Dqn_Rect dest_rect = {};
|
Dqn_Rect dest_rect = {};
|
||||||
dest_rect.size = tex_rect.size * (tex_scalar * 1.5f) + (tex_rect.size * (0.005f * sin_t));
|
dest_rect.size = tex_rect.size * (tex_scalar * 1.5f) + (tex_rect.size * (0.005f * sin_t));
|
||||||
dest_rect.pos = Dqn_Rect_InterpolatedPoint(window_rect, Dqn_V2_InitNx2(1, 1)) - dest_rect.size;
|
dest_rect.pos = Dqn_Rect_InterpolatedPoint(window_rect, Dqn_V2_InitNx2(1, 1)) - dest_rect.size;
|
||||||
@ -2987,7 +3038,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
shake_offset.y = (Dqn_PCG32_NextF32(&game->play.rng) * max_shake_dist - half_shake_dist) * game->play.global_camera_trauma01;
|
shake_offset.y = (Dqn_PCG32_NextF32(&game->play.rng) * max_shake_dist - half_shake_dist) * game->play.global_camera_trauma01;
|
||||||
dest_rect.pos += shake_offset;
|
dest_rect.pos += shake_offset;
|
||||||
|
|
||||||
if ((input->timer_s * 1000) > (game->play.perry_join_splash_screen_end_ms)) {
|
if (game->play.clock_ms > game->play.perry_join_splash_screen_end_ms) {
|
||||||
game->play.perry_join_splash_pos_offset.x -= dest_rect.size.x * (12.f * DQN_CAST(Dqn_f32)input->delta_s);
|
game->play.perry_join_splash_pos_offset.x -= dest_rect.size.x * (12.f * DQN_CAST(Dqn_f32)input->delta_s);
|
||||||
dest_rect.pos += game->play.perry_join_splash_pos_offset;
|
dest_rect.pos += game->play.perry_join_splash_pos_offset;
|
||||||
if (dest_rect.pos.x < -dest_rect.size.x)
|
if (dest_rect.pos.x < -dest_rect.size.x)
|
||||||
@ -3051,7 +3102,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
DQN_ASSERT(game->play.players.size <= DQN_ARRAY_UCOUNT(player_avatar_base_pos));
|
DQN_ASSERT(game->play.players.size <= DQN_ARRAY_UCOUNT(player_avatar_base_pos));
|
||||||
|
|
||||||
DQN_ASSERTF(game->play.players.size <= 2, "We hardcode 2 player support");
|
DQN_ASSERTF(game->play.players.size <= 2, "We hardcode 2 player support");
|
||||||
if (game->play.players.size == 1) {
|
if (game->play.players.size == 1 && game->play.state != FP_GameState_Tutorial) {
|
||||||
// NOTE: We show the Press <BTN> to join for the remaining 2nd player
|
// NOTE: We show the Press <BTN> to join for the remaining 2nd player
|
||||||
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||||
TELY_Render_PushFontSize(renderer, game->talkco_font, game->font_size);
|
TELY_Render_PushFontSize(renderer, game->talkco_font, game->font_size);
|
||||||
@ -3070,7 +3121,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
Dqn_f32 font_height = TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
Dqn_f32 font_height = TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
||||||
Dqn_V2 base_p = player_avatar_base_pos[game->play.players.size];
|
Dqn_V2 base_p = player_avatar_base_pos[game->play.players.size];
|
||||||
TELY_Render_TextF(renderer, base_p, Dqn_V2_Zero, "Press %.*s", DQN_STR_FMT(join_game_key)); base_p.y += font_height;
|
TELY_Render_TextF(renderer, base_p, Dqn_V2_Zero, "Press %.*s", DQN_STR_FMT(join_game_key)); base_p.y += font_height;
|
||||||
FP_ListenForNewPlayer(input, game);
|
FP_ListenForNewPlayer(input, game, false /*tutorial_is_allowed*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Render the player(s) HUD and merchant menu interaction ============================
|
// NOTE: Render the player(s) HUD and merchant menu interaction ============================
|
||||||
@ -3165,7 +3216,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
merchant_menu_rect.pos = *mapping.menu_pos;
|
merchant_menu_rect.pos = *mapping.menu_pos;
|
||||||
|
|
||||||
// NOTE: Bob the merchant menu
|
// NOTE: Bob the merchant menu
|
||||||
Dqn_f32 sin_t = DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 3.f);
|
Dqn_f32 sin_t = DQN_SINF(DQN_CAST(Dqn_f32)game->play.clock_ms / 1000.f * 3.f);
|
||||||
merchant_menu_rect.pos.y += sin_t * 4.f;
|
merchant_menu_rect.pos.y += sin_t * 4.f;
|
||||||
|
|
||||||
TELY_Render_TextureColourV4(renderer,
|
TELY_Render_TextureColourV4(renderer,
|
||||||
@ -3225,7 +3276,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
interact_btn_rect.size = TELY_Asset_MeasureText(assets, font_size, key_bind_label);
|
interact_btn_rect.size = TELY_Asset_MeasureText(assets, font_size, key_bind_label);
|
||||||
|
|
||||||
Dqn_Rect key_bind_rect = interact_btn_rect;
|
Dqn_Rect key_bind_rect = interact_btn_rect;
|
||||||
key_bind_rect.pos.y += interact_btn_rect.size.y * .3f;
|
key_bind_rect.pos.y += interact_btn_rect.size.y * .8f;
|
||||||
|
|
||||||
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(key_bind_rect, 2.f), TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(keybind_btn_shadow_colour, tex_mod_colour.a));
|
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(key_bind_rect, 2.f), TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(keybind_btn_shadow_colour, tex_mod_colour.a));
|
||||||
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
|
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
|
||||||
@ -3234,7 +3285,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Render the $ cost
|
// NOTE: Render the $ cost
|
||||||
dollar_text_label_pos = Dqn_Rect_InterpolatedPoint(interact_btn_rect, Dqn_V2_InitNx2(0.5f, -0.8f));
|
dollar_text_label_pos = Dqn_Rect_InterpolatedPoint(interact_btn_rect, Dqn_V2_InitNx2(0.5f, -1.5f));
|
||||||
TELY_Render_TextF(renderer, dollar_text_label_pos, Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.building_base_price);
|
TELY_Render_TextF(renderer, dollar_text_label_pos, Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.building_base_price);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3340,7 +3391,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
interact_btn_rect.size = TELY_Asset_MeasureText(assets, font_size, key_bind_label);
|
interact_btn_rect.size = TELY_Asset_MeasureText(assets, font_size, key_bind_label);
|
||||||
|
|
||||||
Dqn_Rect key_bind_rect = interact_btn_rect;
|
Dqn_Rect key_bind_rect = interact_btn_rect;
|
||||||
key_bind_rect.pos.y += interact_btn_rect.size.y * .3f;
|
key_bind_rect.pos.y += interact_btn_rect.size.y * .8f;
|
||||||
|
|
||||||
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(key_bind_rect, 2.f), TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(keybind_btn_shadow_colour, tex_mod_colour.a));
|
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(key_bind_rect, 2.f), TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(keybind_btn_shadow_colour, tex_mod_colour.a));
|
||||||
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
|
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
|
||||||
@ -3365,7 +3416,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Render the $ cost
|
// NOTE: Render the $ cost
|
||||||
dollar_text_label_pos = Dqn_Rect_InterpolatedPoint(interact_btn_rect, Dqn_V2_InitNx2(1.f, -0.8f));
|
dollar_text_label_pos = Dqn_Rect_InterpolatedPoint(interact_btn_rect, Dqn_V2_InitNx2(1.f, -1.5f));
|
||||||
TELY_Render_TextF(renderer, dollar_text_label_pos, Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.upgrade_base_price);
|
TELY_Render_TextF(renderer, dollar_text_label_pos, Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.upgrade_base_price);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -3824,19 +3875,34 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
struct LineSize {
|
struct LineSize {
|
||||||
Dqn_Str8 line;
|
Dqn_Str8 line;
|
||||||
Dqn_V2 size;
|
Dqn_V2 size;
|
||||||
} lines[] = {
|
|
||||||
{DQN_STR8("Defend Terry's heart from the oncoming hordes!"), Dqn_V2_Zero},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Dqn_V2 origin = Dqn_V2_InitV2I(os->core.window_size) * Dqn_V2_InitNx2(.5f, .5f);
|
Dqn_FArray<LineSize, 8> lines = {};
|
||||||
Dqn_f32 scaled_font_size = TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
if (game->play.tutorial_state == FP_GameStateTutorial_ShowPlayer || game->play.tutorial_state == FP_GameStateTutorial_ShowPlayerWait) {
|
||||||
Dqn_V2 text_min = Dqn_V2_InitNx2(origin.x, origin.y - scaled_font_size * .5f);
|
LineSize *line_size = Dqn_FArray_Make(&lines, Dqn_ZeroMem_Yes);
|
||||||
Dqn_V2 text_max = Dqn_V2_InitNx2(text_min.x, text_min.y + DQN_ARRAY_UCOUNT(lines) * scaled_font_size);
|
line_size->line = DQN_STR8("Defend Terry's heart from the oncoming cherries!");
|
||||||
|
} else if (game->play.tutorial_state == FP_GameStateTutorial_ShowBillboardBuild || game->play.tutorial_state == FP_GameStateTutorial_ShowBillboardBuildWait) {
|
||||||
|
{
|
||||||
|
LineSize *line_size = Dqn_FArray_Make(&lines, Dqn_ZeroMem_Yes);
|
||||||
|
line_size->line = DQN_STR8("Lookout for billboards for tips!");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
LineSize *line_size = Dqn_FArray_Make(&lines, Dqn_ZeroMem_Yes);
|
||||||
|
line_size->line = DQN_STR8("Build buildings to slow cherries down and afford more time");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LineSize *line_size = Dqn_FArray_Make(&lines, Dqn_ZeroMem_Yes);
|
||||||
|
line_size->line = DQN_STR8("Defeat the cherries spawning from the portals");
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_f32 scaled_font_size = TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
||||||
|
Dqn_V2 text_bounding_size = {};
|
||||||
|
text_bounding_size.y = lines.size * scaled_font_size;
|
||||||
|
|
||||||
for (LineSize &line_size_ : lines) {
|
for (LineSize &line_size_ : lines) {
|
||||||
LineSize *line_size = &line_size_;
|
LineSize *line_size = &line_size_;
|
||||||
line_size->size = TELY_Asset_MeasureText(assets, TELY_Render_ActiveFont(renderer), line_size->line);
|
line_size->size = TELY_Asset_MeasureText(assets, TELY_Render_ActiveFont(renderer), line_size->line);
|
||||||
text_min.x = DQN_MIN(origin.x, origin.x - line_size->size.x * .5f);
|
text_bounding_size.x = DQN_MAX(text_bounding_size.x, line_size->size.x);
|
||||||
text_max.x = DQN_MAX(origin.x, origin.x + line_size->size.x * .58f); // TODO: Wrong calc, but too lazy to figure it out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Calculate terry avatar variables
|
// NOTE: Calculate terry avatar variables
|
||||||
@ -3847,19 +3913,17 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
|
|
||||||
Dqn_Rect dest_rect = {};
|
Dqn_Rect dest_rect = {};
|
||||||
dest_rect.size = tex_rect.size * tex_scalar;
|
dest_rect.size = tex_rect.size * tex_scalar;
|
||||||
dest_rect.pos = text_min - Dqn_V2_InitNx2(dest_rect.size.x * 1.f, dest_rect.size.y * .5f);
|
dest_rect.pos = Dqn_V2_InitV2I(os->core.window_size) * Dqn_V2_InitNx2(0.5f, 0.8f) - (dest_rect.size * .5f) - Dqn_V2_InitNx2(text_bounding_size.x * .5f, 0.f);
|
||||||
|
|
||||||
Dqn_Rect terry_bounding_rect = Dqn_Rect_ExpandV2(dest_rect, Dqn_V2_InitNx2(scaled_font_size * .5f, scaled_font_size * .25f));
|
Dqn_Rect terry_bounding_rect = dest_rect;
|
||||||
|
|
||||||
// NOTE: Draw text
|
// NOTE: Draw text
|
||||||
{
|
{
|
||||||
Dqn_V2 draw_p = Dqn_Rect_InterpolatedPoint(terry_bounding_rect, Dqn_V2_InitNx2(1.1f, 0.5f));
|
Dqn_V2 draw_p = Dqn_Rect_InterpolatedPoint(terry_bounding_rect, Dqn_V2_InitNx2(1.0f, 0.5f));
|
||||||
Dqn_Rect bounding_rect = Dqn_Rect_InitV2x2(text_min, text_max - text_min);
|
|
||||||
bounding_rect = Dqn_Rect_ExpandV2(bounding_rect, Dqn_V2_InitNx2(scaled_font_size * .1f, scaled_font_size * .1f));
|
Dqn_Rect bounding_rect = Dqn_Rect_InitV2x2(draw_p, text_bounding_size);
|
||||||
|
bounding_rect = Dqn_Rect_ExpandV2(bounding_rect, Dqn_V2_InitNx2(scaled_font_size * 1.5f, scaled_font_size * .1f));
|
||||||
|
|
||||||
Dqn_RectMinMax min_max_bounds = Dqn_Rect_MinMax(bounding_rect);
|
|
||||||
min_max_bounds.max.y = DQN_MAX(min_max_bounds.max.y, terry_bounding_rect.pos.y + terry_bounding_rect.size.y);
|
|
||||||
bounding_rect = Dqn_Rect_InitV2x2(min_max_bounds.min, min_max_bounds.max - min_max_bounds.min);
|
|
||||||
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(bounding_rect, 5.f), TELY_RenderShapeMode_Fill, TELY_COLOUR_BLACK_V4);
|
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(bounding_rect, 5.f), TELY_RenderShapeMode_Fill, TELY_COLOUR_BLACK_V4);
|
||||||
TELY_Render_RectColourV4(renderer, bounding_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(maroon_colour, 1.f));
|
TELY_Render_RectColourV4(renderer, bounding_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(maroon_colour, 1.f));
|
||||||
|
|
||||||
@ -3871,8 +3935,6 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Draw terry
|
// NOTE: Draw terry
|
||||||
TELY_Render_RectColourV4(renderer, Dqn_Rect_Expand(terry_bounding_rect, 5.f), TELY_RenderShapeMode_Fill, TELY_COLOUR_BLACK_V4);
|
|
||||||
TELY_Render_RectColourV4(renderer, terry_bounding_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(maroon_colour, 1.f));
|
|
||||||
TELY_Render_TextureColourV4(renderer,
|
TELY_Render_TextureColourV4(renderer,
|
||||||
game->atlas_sprite_sheet.tex_handle,
|
game->atlas_sprite_sheet.tex_handle,
|
||||||
tex_rect,
|
tex_rect,
|
||||||
@ -3883,6 +3945,35 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (game->play.state == FP_GameState_Pause) {
|
||||||
|
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||||
|
TELY_Render_PushColourV4(renderer, TELY_COLOUR_WHITE_V4);
|
||||||
|
DQN_DEFER {
|
||||||
|
TELY_Render_PopColourV4(renderer);
|
||||||
|
TELY_Render_PopTransform(renderer);
|
||||||
|
};
|
||||||
|
|
||||||
|
TELY_Render_RectColourV4(
|
||||||
|
renderer,
|
||||||
|
Dqn_Rect_InitNx4(0, 0, DQN_CAST(Dqn_f32)os->core.window_size.x, DQN_CAST(Dqn_f32)os->core.window_size.y),
|
||||||
|
TELY_RenderShapeMode_Fill,
|
||||||
|
TELY_Colour_V4Alpha(TELY_COLOUR_BLACK_V4, .8f));
|
||||||
|
|
||||||
|
Dqn_V2 draw_p = Dqn_V2_InitV2I(os->core.window_size) * Dqn_V2_InitNx2(0.5f, 0.5f);
|
||||||
|
|
||||||
|
TELY_Render_PushFontSize(renderer, game->talkco_font, game->xlarge_talkco_font_size);
|
||||||
|
TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx1(0.5f), "Paused"); draw_p.y += TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
||||||
|
TELY_Render_PopFont(renderer);
|
||||||
|
|
||||||
|
TELY_Render_PushFontSize(renderer, game->talkco_font, game->large_talkco_font_size);
|
||||||
|
TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx1(0.5f), "Press enter to resume"); draw_p.y += TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
||||||
|
TELY_Render_PopFont(renderer);
|
||||||
|
TELY_Render_PopColourV4(renderer);
|
||||||
|
|
||||||
|
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Return))
|
||||||
|
game->play.state = FP_GameState_Play;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Add scanlines into the game for A E S T H E T I C S ===================================
|
// NOTE: Add scanlines into the game for A E S T H E T I C S ===================================
|
||||||
if (game->play.state == FP_GameState_Play || game->play.state == FP_GameState_Tutorial) {
|
if (game->play.state == FP_GameState_Play || game->play.state == FP_GameState_Tutorial) {
|
||||||
Dqn_V2 screen_size = Dqn_V2_InitNx2(os->core.window_size.w, os->core.window_size.h);
|
Dqn_V2 screen_size = Dqn_V2_InitNx2(os->core.window_size.w, os->core.window_size.h);
|
||||||
@ -3891,8 +3982,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
FP_GameRenderScanlines(renderer, scanline_gap, scanline_thickness, screen_size);
|
FP_GameRenderScanlines(renderer, scanline_gap, scanline_thickness, screen_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game->play.state == FP_GameState_IntroScreen || game->play.state == FP_GameState_WinGame || game->play.state == FP_GameState_LoseGame || game->play.state == FP_GameState_Pause) {
|
if (game->play.state == FP_GameState_IntroScreen || game->play.state == FP_GameState_WinGame || game->play.state == FP_GameState_LoseGame) {
|
||||||
|
|
||||||
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||||
DQN_DEFER { TELY_Render_PopTransform(renderer); };
|
DQN_DEFER { TELY_Render_PopTransform(renderer); };
|
||||||
|
|
||||||
@ -4007,7 +4097,13 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->font_size);
|
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->font_size);
|
||||||
Dqn_f32 t = (DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 5.f) + 1.f) / 2.f;
|
Dqn_f32 t = (DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 5.f) + 1.f) / 2.f;
|
||||||
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, t));
|
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, t));
|
||||||
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(window_rect, Dqn_V2_InitNx2(0.5f, 0.925f)), Dqn_V2_InitNx1(0.5f), "Press <B> or <Gamepad: Start> to %s", game->play.state == FP_GameState_IntroScreen ? "start" : "restart");
|
|
||||||
|
Dqn_V2 text_p = Dqn_Rect_InterpolatedPoint(window_rect, Dqn_V2_InitNx2(0.5f, 0.925f));
|
||||||
|
TELY_Render_TextF(renderer, text_p, Dqn_V2_InitNx1(0.5f), "Press <B> or <Gamepad: Start> to %s", game->play.state == FP_GameState_IntroScreen ? "start" : "restart");
|
||||||
|
text_p.y += TELY_Render_ActiveFont(renderer).size * os->core.dpi_scale;
|
||||||
|
|
||||||
|
TELY_Render_TextF(renderer, text_p, Dqn_V2_InitNx1(0.5f), "Press <T> or <Gamepad: Select> for the tutorial");
|
||||||
|
text_p.y += TELY_Render_ActiveFont(renderer).size * os->core.dpi_scale;
|
||||||
|
|
||||||
TELY_Render_PopColourV4(renderer);
|
TELY_Render_PopColourV4(renderer);
|
||||||
TELY_Render_PopFont(renderer);
|
TELY_Render_PopFont(renderer);
|
||||||
@ -4015,22 +4111,14 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
|||||||
if (game->play.state == FP_GameState_LoseGame)
|
if (game->play.state == FP_GameState_LoseGame)
|
||||||
FP_PlayReset(game, os);
|
FP_PlayReset(game, os);
|
||||||
|
|
||||||
if (FP_ListenForNewPlayer(input, game))
|
FP_ListenForNewPlayerResult new_player = FP_ListenForNewPlayer(input, game, true /*tutorial_is_allowed*/);
|
||||||
game->play.state = FP_GameState_Tutorial;
|
if (new_player.yes) {
|
||||||
|
if (new_player.tutorial_requested) {
|
||||||
} else if (game->play.state == FP_GameState_Pause) {
|
game->play.state = FP_GameState_Tutorial;
|
||||||
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->large_font_size);
|
} else {
|
||||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Paused"); draw_p.y += TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
game->play.state = FP_GameState_Play;
|
||||||
TELY_Render_PopFont(renderer);
|
}
|
||||||
|
}
|
||||||
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->font_size);
|
|
||||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Press enter to resume"); draw_p.y += TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
|
||||||
TELY_Render_PopFont(renderer);
|
|
||||||
TELY_Render_PopColourV4(renderer);
|
|
||||||
|
|
||||||
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Return))
|
|
||||||
game->play.state = FP_GameState_Play;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
DQN_ASSERT(game->play.state == FP_GameState_WinGame);
|
DQN_ASSERT(game->play.state == FP_GameState_WinGame);
|
||||||
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->large_font_size);
|
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->large_font_size);
|
||||||
|
@ -165,6 +165,12 @@ struct FP_ParticleDescriptor
|
|||||||
Dqn_usize duration_ms;
|
Dqn_usize duration_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FP_ListenForNewPlayerResult
|
||||||
|
{
|
||||||
|
bool yes;
|
||||||
|
bool tutorial_requested;
|
||||||
|
};
|
||||||
|
|
||||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||||
#define FP_DEVELOPER_MODE 0
|
#define FP_DEVELOPER_MODE 0
|
||||||
#else
|
#else
|
||||||
|
@ -543,7 +543,7 @@ int main(int argc, char const **argv)
|
|||||||
Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.cpp", DQN_STR_FMT(code_dir)) },
|
Dqn_CPPBuildCompileFile{{}, Dqn_FsPath_ConvertF(scratch.arena, "%.*s/feely_pona_unity.cpp", DQN_STR_FMT(code_dir)) },
|
||||||
});
|
});
|
||||||
|
|
||||||
Dqn_Str8 output_name = DQN_STR8("Terry_Cherry_Emscripten");
|
Dqn_Str8 output_name = DQN_STR8("Terry_Cherry");
|
||||||
Dqn_List<Dqn_Str8> compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, {
|
Dqn_List<Dqn_Str8> compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, {
|
||||||
DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"),
|
DQN_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"),
|
||||||
DQN_STR8("-o"), Dqn_Str8_InitF(scratch.allocator, "%.*s.html", DQN_STR_FMT(output_name)),
|
DQN_STR8("-o"), Dqn_Str8_InitF(scratch.allocator, "%.*s.html", DQN_STR_FMT(output_name)),
|
||||||
@ -588,7 +588,7 @@ int main(int argc, char const **argv)
|
|||||||
|
|
||||||
for (Dqn_Str8 file_ext : generated_file_extension) {
|
for (Dqn_Str8 file_ext : generated_file_extension) {
|
||||||
Dqn_Str8 src_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.%.*s", DQN_STR_FMT(build_dir), DQN_STR_FMT(output_name), DQN_STR_FMT(file_ext));
|
Dqn_Str8 src_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.%.*s", DQN_STR_FMT(build_dir), DQN_STR_FMT(output_name), DQN_STR_FMT(file_ext));
|
||||||
Dqn_Str8 dest_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/Terry_Cherry.%.*s", DQN_STR_FMT(folder_path), DQN_STR_FMT(file_ext));
|
Dqn_Str8 dest_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.%.*s", DQN_STR_FMT(folder_path), DQN_STR_FMT(output_name), DQN_STR_FMT(file_ext));
|
||||||
Dqn_Str8 cmd = Dqn_Str8_InitF(scratch.allocator, "cmd /C move /Y %.*s %.*s", DQN_STR_FMT(src_path), DQN_STR_FMT(dest_path));
|
Dqn_Str8 cmd = Dqn_Str8_InitF(scratch.allocator, "cmd /C move /Y %.*s %.*s", DQN_STR_FMT(src_path), DQN_STR_FMT(dest_path));
|
||||||
if (dry_run) {
|
if (dry_run) {
|
||||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd));
|
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd));
|
||||||
|
@ -376,9 +376,25 @@ enum FP_GameStateTutorial
|
|||||||
FP_GameStateTutorial_ShowPortalTwoWait,
|
FP_GameStateTutorial_ShowPortalTwoWait,
|
||||||
FP_GameStateTutorial_ShowPortalThree,
|
FP_GameStateTutorial_ShowPortalThree,
|
||||||
FP_GameStateTutorial_ShowPortalThreeWait,
|
FP_GameStateTutorial_ShowPortalThreeWait,
|
||||||
|
FP_GameStateTutorial_ShowBillboardBuild,
|
||||||
|
FP_GameStateTutorial_ShowBillboardBuildWait,
|
||||||
FP_GameStateTutorial_Count,
|
FP_GameStateTutorial_Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Dqn_V2 const FP_MONKEY_SPAWN_LOCATIONS[] =
|
||||||
|
{
|
||||||
|
Dqn_V2_InitNx2(-592, 538),
|
||||||
|
Dqn_V2_InitNx2(-1503, -568),
|
||||||
|
Dqn_V2_InitNx2(1890, 1150),
|
||||||
|
Dqn_V2_InitNx2(1815, -1192),
|
||||||
|
Dqn_V2_InitNx2(520, 1230),
|
||||||
|
Dqn_V2_InitNx2(-934, -238),
|
||||||
|
Dqn_V2_InitNx2(1915, 15),
|
||||||
|
Dqn_V2_InitNx2(247, 560),
|
||||||
|
Dqn_V2_InitNx2(-290, -1120),
|
||||||
|
Dqn_V2_InitNx2(1126, -646),
|
||||||
|
};
|
||||||
|
|
||||||
struct FP_GamePlay
|
struct FP_GamePlay
|
||||||
{
|
{
|
||||||
TELY_ChunkPool *chunk_pool;
|
TELY_ChunkPool *chunk_pool;
|
||||||
@ -402,6 +418,7 @@ struct FP_GamePlay
|
|||||||
FP_GameEntityHandle merchant_graveyard;
|
FP_GameEntityHandle merchant_graveyard;
|
||||||
FP_GameEntityHandle merchant_gym;
|
FP_GameEntityHandle merchant_gym;
|
||||||
FP_GameEntityHandle merchant_phone_company;
|
FP_GameEntityHandle merchant_phone_company;
|
||||||
|
FP_GameEntityHandle billboard_build;
|
||||||
|
|
||||||
FP_GameEntityHandle clicked_entity;
|
FP_GameEntityHandle clicked_entity;
|
||||||
FP_GameEntityHandle hot_entity;
|
FP_GameEntityHandle hot_entity;
|
||||||
@ -445,7 +462,10 @@ struct FP_GamePlay
|
|||||||
uint32_t particle_next_index;
|
uint32_t particle_next_index;
|
||||||
|
|
||||||
FP_GameStateTutorial tutorial_state;
|
FP_GameStateTutorial tutorial_state;
|
||||||
Dqn_f64 tutorial_wait_end_time_s;
|
uint64_t tutorial_wait_end_time_ms;
|
||||||
|
|
||||||
|
uint8_t monkey_spawn_shuffled_list[DQN_ARRAY_UCOUNT(FP_MONKEY_SPAWN_LOCATIONS)];
|
||||||
|
uint8_t monkey_spawn_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_Game
|
struct FP_Game
|
||||||
@ -492,7 +512,7 @@ FP_GamePlaceableBuilding const PLACEABLE_BUILDINGS[] = {
|
|||||||
{FP_EntityType_AirportTerry, FP_EntityAirportTerryState_Idle},
|
{FP_EntityType_AirportTerry, FP_EntityAirportTerryState_Idle},
|
||||||
{FP_EntityType_ChurchTerry, FP_EntityChurchTerryState_Idle},
|
{FP_EntityType_ChurchTerry, FP_EntityChurchTerryState_Idle},
|
||||||
{FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle},
|
{FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle},
|
||||||
{FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle},
|
// {FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameCanMoveToPositionResult
|
struct FP_GameCanMoveToPositionResult
|
||||||
|
Loading…
Reference in New Issue
Block a user