Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d685fcdb9 | |||
| ebc969603d | |||
| d0b3ce1bd0 | |||
| 97a83b20f6 | |||
| 9ad3754829 | |||
| 0522e842c4 | |||
| 9ba6071f9b | |||
| 131323fbea | |||
| e77c85b004 | |||
| 11d5f34522 | |||
| 285cc9b5ad |
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+1
-1
Submodule External/tely updated: d82244653e...4a4c88a9f0
@@ -1,25 +1,5 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set script_dir_backslash=%~dp0
|
||||
set script_dir=%script_dir_backslash:~0,-1%
|
||||
set build_dir=%script_dir%\Build
|
||||
set code_dir=%script_dir%
|
||||
call build_all.bat %* --fast-dev-build || exit /b 1
|
||||
|
||||
REM Bootstrap a version
|
||||
git show -s --date=format:%%Y-%%m-%%d --format=%%cd HEAD> feely_pona_version.txt
|
||||
git rev-parse --short=8 HEAD>> feely_pona_version.txt
|
||||
git rev-list --count HEAD>> feely_pona_version.txt
|
||||
|
||||
REM Bootstrap the build program
|
||||
mkdir %build_dir% 2>nul
|
||||
pushd %build_dir%
|
||||
cl /nologo /Z7 /W4 %code_dir%\feely_pona_build.cpp || exit /B 1
|
||||
copy feely_pona_build.exe %code_dir% 1>nul
|
||||
popd
|
||||
|
||||
REM Run the build program
|
||||
%code_dir%\feely_pona_build.exe %* || exit /B 1
|
||||
|
||||
popd
|
||||
exit /B 1
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set script_dir_backslash=%~dp0
|
||||
set script_dir=%script_dir_backslash:~0,-1%
|
||||
set build_dir=%script_dir%\Build
|
||||
set code_dir=%script_dir%
|
||||
|
||||
REM Bootstrap a version
|
||||
git show -s --date=format:%%Y-%%m-%%d --format=%%cd HEAD> feely_pona_version.txt
|
||||
git rev-parse --short=8 HEAD>> feely_pona_version.txt
|
||||
git rev-list --count HEAD>> feely_pona_version.txt
|
||||
|
||||
REM Bootstrap the build program
|
||||
mkdir %build_dir% 2>nul
|
||||
pushd %build_dir%
|
||||
cl /nologo /Z7 /W4 %code_dir%\feely_pona_build.cpp || exit /B 1
|
||||
copy feely_pona_build.exe %code_dir% 1>nul
|
||||
popd
|
||||
|
||||
REM Run the build program
|
||||
%code_dir%\feely_pona_build.exe %* || exit /B 1
|
||||
|
||||
popd
|
||||
exit /B 1
|
||||
+4
-4
@@ -5,7 +5,7 @@ set script_dir_backslash=%~dp0
|
||||
set script_dir=%script_dir_backslash:~0,-1%
|
||||
set build_dir=%script_dir%\Build
|
||||
|
||||
scp -P 8110 %build_dir%\Terry_Cherry_Emscripten\Terry_Cherry.html doylet@doylet.dev:/selfhost/TerryCherry/index.html
|
||||
scp -P 8110 %build_dir%\Terry_Cherry_Emscripten\Terry_Cherry.data doylet@doylet.dev:/selfhost/TerryCherry/Terry_Cherry.data
|
||||
scp -P 8110 %build_dir%\Terry_Cherry_Emscripten\Terry_Cherry.js doylet@doylet.dev:/selfhost/TerryCherry/Terry_Cherry.js
|
||||
scp -P 8110 %build_dir%\Terry_Cherry_Emscripten\Terry_Cherry.wasm doylet@doylet.dev:/selfhost/TerryCherry/Terry_Cherry.wasm
|
||||
scp -P 8110 %build_dir%\Terry_Cherry\Terry_Cherry.html doylet@doylet.dev:/selfhost/TerryCherry/index.html
|
||||
scp -P 8110 %build_dir%\Terry_Cherry\Terry_Cherry.data doylet@doylet.dev:/selfhost/TerryCherry/Terry_Cherry.data
|
||||
scp -P 8110 %build_dir%\Terry_Cherry\Terry_Cherry.js doylet@doylet.dev:/selfhost/TerryCherry/Terry_Cherry.js
|
||||
scp -P 8110 %build_dir%\Terry_Cherry\Terry_Cherry.wasm doylet@doylet.dev:/selfhost/TerryCherry/Terry_Cherry.wasm
|
||||
|
||||
+344
-117
@@ -138,9 +138,9 @@ static void FP_SetDefaultGamepadBindings(FP_GameControls *controls)
|
||||
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)
|
||||
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 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) {
|
||||
FP_GameEntityHandle terry_handle = {};
|
||||
if (play->players.size) {
|
||||
@@ -197,11 +209,13 @@ static bool FP_ListenForNewPlayer(TELY_OSInput *input, FP_Game *game)
|
||||
FP_SetDefaultGamepadBindings(controls);
|
||||
}
|
||||
|
||||
result = true;
|
||||
result.yes = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t const FP_COOLDOWN_WAVE_TIME_MS = 30'000;
|
||||
static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
||||
{
|
||||
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_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 ===================================================================================
|
||||
{
|
||||
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.map);
|
||||
@@ -305,9 +330,22 @@ static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
||||
|
||||
Dqn_V2 base_mid_p = Dqn_V2_InitNx2(1580, 0.f);
|
||||
{
|
||||
Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(play->map->local_hit_box_size.w * -0.5f + 128.f, 0.f);
|
||||
Dqn_V2 bottom_lane_mob_spawner_pos = Dqn_V2_InitNx2(mid_lane_mob_spawner_pos.x, mid_lane_mob_spawner_pos.y + 932.f);
|
||||
Dqn_V2 top_lane_mob_spawner_pos = Dqn_V2_InitNx2(mid_lane_mob_spawner_pos.x, mid_lane_mob_spawner_pos.y - 915.f);
|
||||
Dqn_usize spawn_cap = 16;
|
||||
|
||||
// NOTE: Top lane spawner ===================================================================
|
||||
{
|
||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, top_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||
FP_Game_PushParentEntity(game, mob_spawner);
|
||||
FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, 0.f), "Waypoint");
|
||||
FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, +915.f), "Waypoint");
|
||||
FP_Game_PopParentEntity(game);
|
||||
Dqn_FArray_Add(&play->mob_spawners, mob_spawner);
|
||||
}
|
||||
|
||||
// NOTE: Mid lane mob spawner ==================================================================
|
||||
Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(play->map->local_hit_box_size.w * -0.5f + 128.f, 0.f);
|
||||
Dqn_usize spawn_cap = 16;
|
||||
{
|
||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, mid_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||
FP_Game_PushParentEntity(game, mob_spawner);
|
||||
@@ -317,8 +355,6 @@ static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
||||
}
|
||||
|
||||
// NOTE: Bottom lane spawner ===================================================================
|
||||
#if 1
|
||||
Dqn_V2 bottom_lane_mob_spawner_pos = Dqn_V2_InitNx2(mid_lane_mob_spawner_pos.x, mid_lane_mob_spawner_pos.y + 932.f);
|
||||
{
|
||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, bottom_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||
FP_Game_PushParentEntity(game, mob_spawner);
|
||||
@@ -327,30 +363,6 @@ static void FP_PlayReset(FP_Game *game, TELY_OS *os)
|
||||
FP_Game_PopParentEntity(game);
|
||||
Dqn_FArray_Add(&play->mob_spawners, mob_spawner);
|
||||
}
|
||||
|
||||
// NOTE: Top lane spawner ===================================================================
|
||||
Dqn_V2 top_lane_mob_spawner_pos = Dqn_V2_InitNx2(mid_lane_mob_spawner_pos.x, mid_lane_mob_spawner_pos.y - 915.f);
|
||||
{
|
||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, top_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||
FP_Game_PushParentEntity(game, mob_spawner);
|
||||
FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, 0.f), "Waypoint");
|
||||
FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, +915.f), "Waypoint");
|
||||
FP_Game_PopParentEntity(game);
|
||||
Dqn_FArray_Add(&play->mob_spawners, mob_spawner);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -385,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(-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(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 ================================================================================
|
||||
play->camera.world_pos = {};
|
||||
@@ -413,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;
|
||||
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->xlarge_talkco_font_size = DQN_CAST(uint16_t)(game->font_size * 2.f);
|
||||
|
||||
@@ -437,6 +449,12 @@ void TELY_OS_DLLInit(TELY_OS *os)
|
||||
game->audio[FP_GameAudio_PortalDestroy] = os->funcs.load_audio(assets, DQN_STR8("Portal Destroy"), DQN_STR8("Data/Audio/portal_destroy.ogg"));
|
||||
game->audio[FP_GameAudio_Smooch] = os->funcs.load_audio(assets, DQN_STR8("Smooch"), DQN_STR8("Data/Audio/smooch.ogg"));
|
||||
game->audio[FP_GameAudio_Woosh] = os->funcs.load_audio(assets, DQN_STR8("Woosh"), DQN_STR8("Data/Audio/woosh.ogg"));
|
||||
game->audio[FP_GameAudio_GameStart] = os->funcs.load_audio(assets, DQN_STR8("Game Start"), DQN_STR8("Data/Audio/game_start.ogg"));
|
||||
game->audio[FP_GameAudio_PerryStart] = os->funcs.load_audio(assets, DQN_STR8("Perry Start"), DQN_STR8("Data/Audio/perry_start.ogg"));
|
||||
game->audio[FP_GameAudio_Ambience1] = os->funcs.load_audio(assets, DQN_STR8("Ambience one"), DQN_STR8("Data/Audio/ambience_1.ogg"));
|
||||
game->audio[FP_GameAudio_Ambience2] = os->funcs.load_audio(assets, DQN_STR8("Ambience two"), DQN_STR8("Data/Audio/ambience_2.ogg"));
|
||||
game->audio[FP_GameAudio_Music1] = os->funcs.load_audio(assets, DQN_STR8("Music one"), DQN_STR8("Data/Audio/music_1.ogg"));
|
||||
game->audio[FP_GameAudio_Music2] = os->funcs.load_audio(assets, DQN_STR8("Music two"), DQN_STR8("Data/Audio/music_2.ogg"));
|
||||
|
||||
// NOTE: Load sprite sheets ====================================================================
|
||||
os->user_data = game;
|
||||
@@ -522,7 +540,7 @@ static void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_O
|
||||
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
|
||||
if (!FP_Game_IsNilEntity(portal_monkey)) {
|
||||
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;
|
||||
portal_monkey->local_pos = Dqn_Rect_InterpolatedPoint(hit_box, Dqn_V2_InitNx2(0.5f, -0.75f)) + offset;
|
||||
}
|
||||
@@ -652,23 +670,6 @@ static void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_O
|
||||
|
||||
entity->stamina -= FP_TERRY_DASH_STAMINA_COST;
|
||||
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);
|
||||
@@ -1298,19 +1299,24 @@ 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)
|
||||
{
|
||||
Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate);
|
||||
if (game->play.state == FP_GameState_Pause)
|
||||
return;
|
||||
|
||||
game->play.update_counter++;
|
||||
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);
|
||||
if (game->play.state == FP_GameState_Play && game->play.perry_joined != FP_GamePerryJoins_Enters) {
|
||||
game->play.clock_ms = DQN_CAST(uint64_t)(os->input.timer_s * 1000.f);
|
||||
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4127) // Conditional expression is constant 'FP_DEVELOPER_MODE'
|
||||
if (FP_DEVELOPER_MODE && TELY_OSInput_KeyIsReleased(input->mouse_left))
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
if (FP_DEVELOPER_MODE && game->play.clicked_entity.id) {
|
||||
if (TELY_OSInput_ScanKeyIsPressed(input, TELY_OSInputScanKey_Delete))
|
||||
@@ -1976,12 +1982,69 @@ 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
|
||||
// start the next wave.
|
||||
if (game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave && 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;
|
||||
game->play.current_wave++;
|
||||
// NOTE: Typically, the game starts the cooldown for the next wave after
|
||||
// all enemies are spawned which makes the game fast paced. However, to
|
||||
// give the player a break to reorganise, every 3 waves- we don't start
|
||||
// the wave countdown until all enemies are killed.
|
||||
bool all_enemies_spawned = game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave;
|
||||
bool advance_to_next_wave = false;
|
||||
bool current_wave_is_break_for_player = game->play.current_wave % 1 == 0;
|
||||
bool monkey_spawn = 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);
|
||||
|
||||
// NOTE: Count all the mobs spawned that are a foe
|
||||
// (e.g. churches that convert mobs don't count).
|
||||
for (FP_SentinelListLink<FP_GameEntityHandle> *link = nullptr; FP_SentinelList_Iterate<FP_GameEntityHandle>(&spawner->spawn_list, &link); ) {
|
||||
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
||||
if (mob->faction == FP_GameEntityFaction_Foe)
|
||||
enemy_count++;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
TELY_Audio_Stop(audio, game->audio[FP_GameAudio_Music1]);
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Music2], 1.f);
|
||||
} 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
|
||||
TELY_Audio_Stop(audio, game->audio[FP_GameAudio_Music2]);
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Music1], 1.f);
|
||||
|
||||
if (monkey_spawn && 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++;
|
||||
//TELY_Audio_Stop(audio, game->audio[FP_GameAudio_Music2]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2049,7 +2112,7 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
||||
}
|
||||
|
||||
// NOTE: Mob spawner =======================================================================
|
||||
if (entity->type == FP_EntityType_MobSpawner && !game->play.debug_disable_mobs) {
|
||||
if (entity->type == FP_EntityType_MobSpawner && !game->play.debug_disable_mobs && game->play.state != FP_GameState_Tutorial) {
|
||||
// NOTE: Flush any spawn entities that are dead
|
||||
for (FP_SentinelListLink<FP_GameEntityHandle> *link = nullptr; FP_SentinelList_Iterate<FP_GameEntityHandle>(&entity->spawn_list, &link); ) {
|
||||
FP_GameEntity *spawned_entity = FP_Game_GetEntity(game, link->data);
|
||||
@@ -2059,11 +2122,11 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
||||
|
||||
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
|
||||
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);
|
||||
for (Dqn_usize spawn_index = 0; spawn_index < spawn_count; spawn_index++) {
|
||||
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);
|
||||
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
@@ -2088,13 +2151,11 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
||||
mob->waypoints = FP_SentinelList_Init<FP_GameWaypoint>(game->play.chunk_pool);
|
||||
mob->flags |= FP_GameEntityFlag_Aggros;
|
||||
mob->flags |= FP_GameEntityFlag_RespondsToBuildings;
|
||||
mob->hp_cap *= hp_adjustment;
|
||||
mob->hp_cap *= hp_adjustment*2;
|
||||
mob->hp = mob->hp_cap;
|
||||
|
||||
FP_AppendMobSpawnerWaypoints(game, entity->handle, mob->handle);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2284,22 +2345,76 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
||||
}
|
||||
|
||||
// NOTE: Camera ================================================================================
|
||||
|
||||
FP_GamePlay *play = &game->play;
|
||||
FP_GameCamera *camera = &play->camera;
|
||||
camera->world_pos_target = {};
|
||||
for (FP_GameEntityHandle camera_entity : game->play.camera_tracking_entity) {
|
||||
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, camera_entity) * game->play.camera.scale;
|
||||
camera->world_pos_target += entity_pos;
|
||||
}
|
||||
|
||||
if (game->play.camera_tracking_entity.size)
|
||||
camera->world_pos_target /= DQN_CAST(Dqn_f32)game->play.camera_tracking_entity.size;
|
||||
if (game->play.state == FP_GameState_Tutorial) {
|
||||
Dqn_f32 arrival_dist = Dqn_V2_LengthSq(camera->world_pos_target - camera->world_pos);
|
||||
bool camera_arrived = arrival_dist < DQN_SQUARED(5.f);
|
||||
|
||||
switch (game->play.tutorial_state) {
|
||||
case FP_GameStateTutorial_ShowPlayer: {
|
||||
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.players.data[0]) * 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 + 3000;
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_GameStateTutorial_ShowPortalOneWait: /*FALLTHRU*/
|
||||
case FP_GameStateTutorial_ShowPortalTwoWait: /*FALLTHRU*/
|
||||
case FP_GameStateTutorial_ShowPortalThreeWait: /*FALLTHRU*/
|
||||
case FP_GameStateTutorial_ShowBillboardBuildWait: /*FALLTHRU*/
|
||||
case FP_GameStateTutorial_ShowPlayerWait: {
|
||||
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);
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_GameStateTutorial_ShowPortalOne: /*FALLTHRU*/
|
||||
case FP_GameStateTutorial_ShowPortalTwo: /*FALLTHRU*/
|
||||
case FP_GameStateTutorial_ShowPortalThree: {
|
||||
if (game->play.tutorial_state == FP_GameStateTutorial_ShowPortalOne) {
|
||||
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.mob_spawners.data[0]) * game->play.camera.scale;
|
||||
} else if (game->play.tutorial_state == FP_GameStateTutorial_ShowPortalTwo) {
|
||||
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.mob_spawners.data[1]) * game->play.camera.scale;
|
||||
} else {
|
||||
DQN_ASSERT(game->play.tutorial_state == FP_GameStateTutorial_ShowPortalThree);
|
||||
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, game->play.mob_spawners.data[2]) * 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 + 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;
|
||||
|
||||
case FP_GameStateTutorial_Count: {
|
||||
game->play.state = FP_GameState_Play;
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
camera->world_pos_target = {};
|
||||
for (FP_GameEntityHandle camera_entity : game->play.camera_tracking_entity) {
|
||||
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, camera_entity) * game->play.camera.scale;
|
||||
camera->world_pos_target += entity_pos;
|
||||
}
|
||||
|
||||
if (game->play.camera_tracking_entity.size)
|
||||
camera->world_pos_target /= DQN_CAST(Dqn_f32)game->play.camera_tracking_entity.size;
|
||||
}
|
||||
|
||||
// NOTE: Clamp camera to map bounds ============================================================
|
||||
{
|
||||
Dqn_V2 window_size = Dqn_V2_InitV2I(os->core.window_size);
|
||||
|
||||
Dqn_V2 window_size = Dqn_V2_InitV2I(os->core.window_size);
|
||||
camera->scale = window_size / camera->size;
|
||||
Dqn_V2 camera_size_screen = camera->size * camera->scale;
|
||||
|
||||
@@ -2313,7 +2428,7 @@ static void FP_Update(TELY_OS *os, FP_Game *game, TELY_OSInput *input, TELY_Audi
|
||||
camera->world_pos_target.y = DQN_MIN(camera->world_pos_target.y, half_map_screen_size.h - (camera_size_screen.h * .5f));
|
||||
}
|
||||
|
||||
camera->world_pos += (camera->world_pos_target - camera->world_pos) * (5.f * DQN_CAST(Dqn_f32)input->delta_s);
|
||||
camera->world_pos += (camera->world_pos_target - camera->world_pos) * DQN_MIN(1.f, (5.f * DQN_CAST(Dqn_f32)input->delta_s));
|
||||
Dqn_Profiler_EndZone(update_zone);
|
||||
}
|
||||
|
||||
@@ -2585,8 +2700,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) {
|
||||
FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index;
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
@@ -2643,13 +2758,13 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
dest_rect.size *= 1.5f;
|
||||
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));
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
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,
|
||||
game->atlas_sprite_sheet.tex_handle,
|
||||
@@ -2915,7 +3030,8 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
game->play.global_camera_trauma01 = 1.f;
|
||||
game->play.perry_join_splash_screen_shake_triggered = true;
|
||||
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;
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_PerryStart], 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2930,7 +3046,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);
|
||||
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 = {};
|
||||
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;
|
||||
@@ -2942,7 +3058,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;
|
||||
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);
|
||||
dest_rect.pos += game->play.perry_join_splash_pos_offset;
|
||||
if (dest_rect.pos.x < -dest_rect.size.x)
|
||||
@@ -2961,7 +3077,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
}
|
||||
|
||||
// NOTE: Render overlay UI =====================================================================
|
||||
if (!game->play.debug_hide_hud && (game->play.state == FP_GameState_Pause || game->play.state == FP_GameState_Play)) {
|
||||
if (!game->play.debug_hide_hud && (game->play.state == FP_GameState_Pause || game->play.state == FP_GameState_Play || game->play.state == FP_GameState_Tutorial)) {
|
||||
|
||||
// NOTE: Render the merchant menus for each player =========================================
|
||||
FP_GamePlay *play = &game->play;
|
||||
@@ -3006,7 +3122,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_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
|
||||
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||
TELY_Render_PushFontSize(renderer, game->talkco_font, game->font_size);
|
||||
@@ -3025,7 +3141,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_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;
|
||||
FP_ListenForNewPlayer(input, game);
|
||||
FP_ListenForNewPlayer(input, game, false /*tutorial_is_allowed*/);
|
||||
}
|
||||
|
||||
// NOTE: Render the player(s) HUD and merchant menu interaction ============================
|
||||
@@ -3120,7 +3236,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
merchant_menu_rect.pos = *mapping.menu_pos;
|
||||
|
||||
// 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;
|
||||
|
||||
TELY_Render_TextureColourV4(renderer,
|
||||
@@ -3180,7 +3296,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);
|
||||
|
||||
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_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
|
||||
@@ -3189,7 +3305,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -3295,7 +3411,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);
|
||||
|
||||
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_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
|
||||
@@ -3320,7 +3436,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
@@ -3764,17 +3880,129 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
#endif
|
||||
|
||||
|
||||
// NOTE: Render the other game state modes =====================================================
|
||||
Dqn_V4 const maroon_colour = TELY_Colour_V4InitRGBAU32(0x301010FF); // NOTE: Maroon
|
||||
if (game->play.state == FP_GameState_Tutorial) {
|
||||
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||
TELY_Render_PushFontSize(renderer, game->talkco_font, game->large_talkco_font_size);
|
||||
TELY_Render_PushColourV4(renderer, TELY_COLOUR_WHITE_V4);
|
||||
DQN_DEFER {
|
||||
TELY_Render_PopFont(renderer);
|
||||
TELY_Render_PopTransform(renderer);
|
||||
};
|
||||
|
||||
// NOTE: Calculate text bounds
|
||||
struct LineSize {
|
||||
Dqn_Str8 line;
|
||||
Dqn_V2 size;
|
||||
};
|
||||
|
||||
Dqn_FArray<LineSize, 8> lines = {};
|
||||
if (game->play.tutorial_state == FP_GameStateTutorial_ShowPlayer || game->play.tutorial_state == FP_GameStateTutorial_ShowPlayerWait) {
|
||||
LineSize *line_size = Dqn_FArray_Make(&lines, Dqn_ZeroMem_Yes);
|
||||
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) {
|
||||
LineSize *line_size = &line_size_;
|
||||
line_size->size = TELY_Asset_MeasureText(assets, TELY_Render_ActiveFont(renderer), line_size->line);
|
||||
text_bounding_size.x = DQN_MAX(text_bounding_size.x, line_size->size.x);
|
||||
}
|
||||
|
||||
// NOTE: Calculate terry avatar variables
|
||||
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.intro_screen_terry);
|
||||
Dqn_Rect tex_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
||||
Dqn_f32 desired_width = os->core.window_size.x * .1f;
|
||||
Dqn_f32 tex_scalar = desired_width / tex_rect.size.w;
|
||||
|
||||
Dqn_Rect dest_rect = {};
|
||||
dest_rect.size = tex_rect.size * tex_scalar;
|
||||
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 = dest_rect;
|
||||
|
||||
// NOTE: Draw text
|
||||
{
|
||||
Dqn_V2 draw_p = Dqn_Rect_InterpolatedPoint(terry_bounding_rect, Dqn_V2_InitNx2(1.0f, 0.5f));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
for (LineSize &line_size_ : lines) {
|
||||
LineSize *line_size = &line_size_;
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "%.*s", DQN_STR_FMT(line_size->line));
|
||||
draw_p.y += TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Draw terry
|
||||
TELY_Render_TextureColourV4(renderer,
|
||||
game->atlas_sprite_sheet.tex_handle,
|
||||
tex_rect,
|
||||
dest_rect,
|
||||
Dqn_V2_Zero /*rotate origin*/,
|
||||
0.f /*rotation*/,
|
||||
TELY_COLOUR_WHITE_V4);
|
||||
|
||||
}
|
||||
|
||||
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 ===================================
|
||||
if (game->play.state == FP_GameState_Play) {
|
||||
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_f32 scanline_gap = 4.0f;
|
||||
Dqn_f32 scanline_thickness = 3.0f;
|
||||
FP_GameRenderScanlines(renderer, scanline_gap, scanline_thickness, screen_size);
|
||||
}
|
||||
|
||||
// NOTE: Render the other game state modes =====================================================
|
||||
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());
|
||||
DQN_DEFER { TELY_Render_PopTransform(renderer); };
|
||||
|
||||
@@ -3784,7 +4012,7 @@ static void FP_Render(FP_Game *game, TELY_OS *os, TELY_Renderer *renderer, TELY_
|
||||
Dqn_V2 draw_p = Dqn_V2_InitNx2(min_inset, min_inset);
|
||||
TELY_Render_PushColourV4(renderer, TELY_COLOUR_WHITE_V4);
|
||||
|
||||
Dqn_V4 bg_colour = TELY_Colour_V4InitRGBAU32(0x301010FF); // NOTE: Maroon
|
||||
Dqn_V4 bg_colour = maroon_colour;
|
||||
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),
|
||||
@@ -3889,28 +4117,30 @@ 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);
|
||||
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_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_PopFont(renderer);
|
||||
|
||||
if (game->play.state == FP_GameState_LoseGame)
|
||||
FP_PlayReset(game, os);
|
||||
if (FP_ListenForNewPlayer(input, game))
|
||||
game->play.state = FP_GameState_Play;
|
||||
|
||||
} else if (game->play.state == FP_GameState_Pause) {
|
||||
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->large_font_size);
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Paused"); draw_p.y += TELY_Render_FontSize(renderer) * os->core.dpi_scale;
|
||||
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;
|
||||
FP_ListenForNewPlayerResult new_player = FP_ListenForNewPlayer(input, game, true /*tutorial_is_allowed*/);
|
||||
if (new_player.yes) {
|
||||
if (new_player.tutorial_requested) {
|
||||
game->play.state = FP_GameState_Tutorial;
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_GameStart], 1.f);
|
||||
} else {
|
||||
game->play.state = FP_GameState_Play;
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_GameStart], 1.f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DQN_ASSERT(game->play.state == FP_GameState_WinGame);
|
||||
TELY_Render_PushFontSize(renderer, game->inter_regular_font, game->large_font_size);
|
||||
@@ -4214,7 +4444,6 @@ TELY_OS_DLL_FUNCTION
|
||||
void TELY_OS_DLLFrameUpdate(TELY_OS *os)
|
||||
{
|
||||
TELY_OSInput *input = &os->input;
|
||||
TELY_Assets *assets = &os->assets;
|
||||
TELY_Renderer *renderer = &os->renderer;
|
||||
FP_Game *game = DQN_CAST(FP_Game *) os->user_data;
|
||||
|
||||
@@ -4232,15 +4461,15 @@ void TELY_OS_DLLFrameUpdate(TELY_OS *os)
|
||||
|
||||
TELY_Audio *audio = &os->audio;
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
if (audio->playback_size == 0) {
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_TestAudio], 1.f /*volume*/);
|
||||
//TELY_Audio_Play(audio, game->audio[FP_GameAudio_Music2], 1.f /*volume*/);
|
||||
}
|
||||
#endif
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
if (game->play.state == FP_GameState_Play) {
|
||||
if (game->play.state == FP_GameState_Play || game->play.state == FP_GameState_Tutorial) {
|
||||
if (TELY_OSInput_KeyWasDown(input->mouse_left) && TELY_OSInput_KeyIsDown(input->mouse_left)) {
|
||||
if (game->play.prev_active_entity.id)
|
||||
game->play.active_entity = game->play.prev_active_entity;
|
||||
@@ -4276,6 +4505,4 @@ void TELY_OS_DLLFrameUpdate(TELY_OS *os)
|
||||
}
|
||||
|
||||
FP_Render(game, os, renderer, audio);
|
||||
|
||||
TELY_Audio_MixPlaybackSamples(audio, assets);
|
||||
}
|
||||
|
||||
@@ -165,6 +165,12 @@ struct FP_ParticleDescriptor
|
||||
Dqn_usize duration_ms;
|
||||
};
|
||||
|
||||
struct FP_ListenForNewPlayerResult
|
||||
{
|
||||
bool yes;
|
||||
bool tutorial_requested;
|
||||
};
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
#define FP_DEVELOPER_MODE 0
|
||||
#else
|
||||
|
||||
+77
-10
@@ -229,6 +229,38 @@ int main(int argc, char const **argv)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: sokol_audio ================================================================================
|
||||
Dqn_Str8 sokol_audio_source_code_file = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/External/sokol/sokol_audio.c", DQN_STR_FMT(tely_dir));
|
||||
uint64_t sokol_audio_pc_timings[2] = {};
|
||||
Dqn_List<Dqn_Str8> sokol_audio_pc_output_files = Dqn_List_Init<Dqn_Str8>(scratch.arena, 16);
|
||||
{
|
||||
sokol_audio_pc_timings[0] = Dqn_OS_PerfCounterNow();
|
||||
DQN_DEFER { sokol_audio_pc_timings[1] = Dqn_OS_PerfCounterNow(); };
|
||||
|
||||
Dqn_CPPBuildContext build_context = {};
|
||||
build_context.compiler = Dqn_CPPBuildCompiler_MSVC;
|
||||
build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {
|
||||
Dqn_CPPBuildCompileFile{
|
||||
{}, // Args
|
||||
sokol_audio_source_code_file,
|
||||
},
|
||||
});
|
||||
|
||||
Dqn_List_Add(&sokol_audio_pc_output_files, DQN_STR8("sokol_audio.obj"));
|
||||
|
||||
Dqn_List<Dqn_Str8> compile_flags = Dqn_List_InitSliceCopy(scratch.arena, 16, common_compile_flags);
|
||||
Dqn_List_Add(&compile_flags, DQN_STR8("/c"));
|
||||
build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena);
|
||||
build_context.build_dir = build_dir;
|
||||
|
||||
if (dry_run) {
|
||||
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator);
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd));
|
||||
} else {
|
||||
Dqn_CPPBuild_ExecOrAbort(build_context, Dqn_CPPBuildMode_CacheBuild);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: QOI Converter =========================================================================
|
||||
uint64_t qoi_converter_timings[2] = {};
|
||||
{
|
||||
@@ -286,6 +318,8 @@ int main(int argc, char const **argv)
|
||||
{
|
||||
for (Dqn_ListIterator<Dqn_Str8> it = {}; Dqn_List_Iterate(&raylib_pc_output_files, &it, 0); )
|
||||
Dqn_List_Add(&link_flags, *it.data);
|
||||
for (Dqn_ListIterator<Dqn_Str8> it = {}; Dqn_List_Iterate(&sokol_audio_pc_output_files, &it, 0); )
|
||||
Dqn_List_Add(&link_flags, *it.data);
|
||||
Dqn_List_Add(&link_flags, DQN_STR8("gdi32.lib"));
|
||||
Dqn_List_Add(&link_flags, DQN_STR8("opengl32.lib"));
|
||||
Dqn_List_Add(&link_flags, DQN_STR8("winmm.lib"));
|
||||
@@ -392,7 +426,7 @@ int main(int argc, char const **argv)
|
||||
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator);
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd));
|
||||
} else {
|
||||
Dqn_Str8 exe_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/terry_cherry_dev_msvc.exe", DQN_STR_FMT(build_dir));
|
||||
Dqn_Str8 exe_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/terry_cherry_dev.exe", DQN_STR_FMT(build_dir));
|
||||
bool exe_is_locked = false;
|
||||
if (Dqn_Fs_Exists(exe_path)) {
|
||||
Dqn_FsFile exe_file = Dqn_Fs_OpenFile(exe_path, Dqn_FsFileOpen_OpenIfExist, Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write);
|
||||
@@ -434,9 +468,9 @@ int main(int argc, char const **argv)
|
||||
DQN_DEFER { raylib_emscripten_timings[1] = Dqn_OS_PerfCounterNow(); };
|
||||
|
||||
// NOTE: Setup build context ===========================================================
|
||||
Dqn_List<Dqn_Str8> raylib_emscripten_output_files = Dqn_List_Init<Dqn_Str8>(scratch.arena, 16);
|
||||
Dqn_CPPBuildContext raylib_emscripten_build_context = {};
|
||||
raylib_emscripten_build_context.compiler = Dqn_CPPBuildCompiler_GCC;
|
||||
Dqn_List<Dqn_Str8> emscripten_obj_files = Dqn_List_Init<Dqn_Str8>(scratch.arena, 16);
|
||||
Dqn_CPPBuildContext raylib_emscripten_build_context = {};
|
||||
raylib_emscripten_build_context.compiler = Dqn_CPPBuildCompiler_GCC;
|
||||
|
||||
for (Dqn_Str8 base_file : raylib_base_files) {
|
||||
Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(base_file);
|
||||
@@ -461,7 +495,7 @@ int main(int argc, char const **argv)
|
||||
raylib_emscripten_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file});
|
||||
raylib_emscripten_build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena);
|
||||
raylib_emscripten_build_context.build_dir = build_dir;
|
||||
Dqn_List_Add(&raylib_emscripten_output_files, build_file.output_file_path);
|
||||
Dqn_List_Add(&emscripten_obj_files, build_file.output_file_path);
|
||||
|
||||
if (dry_run) {
|
||||
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(raylib_emscripten_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator);
|
||||
@@ -471,13 +505,45 @@ int main(int argc, char const **argv)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Build the wasm raylib library =================================================
|
||||
{
|
||||
Dqn_Str8 base_file = sokol_audio_source_code_file;
|
||||
Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(base_file);
|
||||
|
||||
// NOTE: Append "emscripten" suffix to the object files
|
||||
Dqn_CPPBuildCompileFile build_file = {};
|
||||
build_file.input_file_path = base_file;
|
||||
build_file.output_file_path = Dqn_Str8_InitF(scratch.allocator, "sokol_audio_emscripten.o");
|
||||
|
||||
Dqn_List<Dqn_Str8> compile_flags = Dqn_List_InitCArrayCopy(scratch.arena, 32, {
|
||||
DQN_STR8("cmd"),
|
||||
DQN_STR8("/C"),
|
||||
DQN_STR8("emcc.bat"),
|
||||
DQN_STR8("-c"), // Compile and assemble, but do not link
|
||||
DQN_STR8("-Wall"),
|
||||
DQN_STR8("-Os"), // Optimize for size
|
||||
});
|
||||
Dqn_List_AddList(&compile_flags, build_specific_compile_flags);
|
||||
|
||||
raylib_emscripten_build_context.compile_files = Dqn_Slice_InitCArrayCopy(scratch.arena, {build_file});
|
||||
raylib_emscripten_build_context.compile_flags = Dqn_List_ToSliceCopy(&compile_flags, scratch.arena);
|
||||
raylib_emscripten_build_context.build_dir = build_dir;
|
||||
Dqn_List_Add(&emscripten_obj_files, build_file.output_file_path);
|
||||
|
||||
if (dry_run) {
|
||||
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(raylib_emscripten_build_context, Dqn_CPPBuildMode_AlwaysRebuild, scratch.allocator);
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd));
|
||||
} else {
|
||||
Dqn_CPPBuild_ExecOrAbort(raylib_emscripten_build_context, Dqn_CPPBuildMode_CacheBuild);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Build the wasm raylib+sokol_audio library =====================================
|
||||
{
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.allocator = scratch.allocator;
|
||||
Dqn_Str8Builder_AppendF(&builder, "cmd /C emar.bat rcs %.*s", DQN_STR_FMT(raylib_emscripten_lib_name));
|
||||
|
||||
for (Dqn_ListIterator<Dqn_Str8> it = {}; Dqn_List_Iterate(&raylib_emscripten_output_files, &it, 0); )
|
||||
for (Dqn_ListIterator<Dqn_Str8> it = {}; Dqn_List_Iterate(&emscripten_obj_files, &it, 0); )
|
||||
Dqn_Str8Builder_AppendF(&builder, " %.*s", DQN_STR_FMT(*it.data));
|
||||
|
||||
Dqn_Str8 cmd = Dqn_Str8Builder_Build(&builder, scratch.allocator);
|
||||
@@ -543,7 +609,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_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_STR8("cmd"), DQN_STR8("/C"), DQN_STR8("emcc.bat"),
|
||||
DQN_STR8("-o"), Dqn_Str8_InitF(scratch.allocator, "%.*s.html", DQN_STR_FMT(output_name)),
|
||||
@@ -588,7 +654,7 @@ int main(int argc, char const **argv)
|
||||
|
||||
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 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));
|
||||
if (dry_run) {
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "%.*s\n", DQN_STR_FMT(cmd));
|
||||
@@ -602,7 +668,8 @@ int main(int argc, char const **argv)
|
||||
build_timings[1] = Dqn_OS_PerfCounterNow();
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, "\n-- Dqn_CPPBuild Timings (%.2fms)", Dqn_OS_PerfCounterMs(build_timings[0], build_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " robocopy: %.2fms", Dqn_OS_PerfCounterMs(robocopy_timings[0], robocopy_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " raylib: %.2fms", Dqn_OS_PerfCounterMs(raylib_pc_timings[0], raylib_pc_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " raylib: %.2fms", Dqn_OS_PerfCounterMs(raylib_pc_timings[0], raylib_pc_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " sokol_audio: %.2fms", Dqn_OS_PerfCounterMs(sokol_audio_pc_timings[0], sokol_audio_pc_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " qoi_converter: %.2fms", Dqn_OS_PerfCounterMs(qoi_converter_timings[0], qoi_converter_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " feely pona sprite packer: %.2fms", Dqn_OS_PerfCounterMs(feely_pona_sprite_packer_timings[0], feely_pona_sprite_packer_timings[1]));
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Out, " feely pona (no dll): %.2fms", Dqn_OS_PerfCounterMs(feely_pona_no_dll_timings[0], feely_pona_no_dll_timings[1]));
|
||||
|
||||
@@ -185,6 +185,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerryInternal(FP_Game *game, Dqn_V2 p
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.5f, entity->sprite_height.meters * .6f);
|
||||
entity->hp_cap = FP_DEFAULT_DAMAGE * 3;
|
||||
entity->hp = entity->hp_cap;
|
||||
entity->coins = 10;
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
|
||||
@@ -250,6 +250,7 @@ static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_ATTRIB
|
||||
result->buildings_visited = FP_SentinelList_Init<FP_GameEntityHandle>(game->play.chunk_pool);
|
||||
result->action.sprite_alpha = 1.f;
|
||||
result->stamina_cap = 93;
|
||||
result->stamina = result->stamina_cap;
|
||||
|
||||
result->hp_cap = DQN_CAST(uint32_t)(FP_DEFAULT_DAMAGE * .8f);
|
||||
result->hp = result->hp_cap;
|
||||
|
||||
+44
-1
@@ -334,12 +334,19 @@ enum FP_GameAudio
|
||||
FP_GameAudio_Message,
|
||||
FP_GameAudio_Monkey,
|
||||
FP_GameAudio_PortalDestroy,
|
||||
FP_GameAudio_GameStart,
|
||||
FP_GameAudio_PerryStart,
|
||||
FP_GameAudio_Ambience1,
|
||||
FP_GameAudio_Ambience2,
|
||||
FP_GameAudio_Music1,
|
||||
FP_GameAudio_Music2,
|
||||
FP_GameAudio_Count,
|
||||
};
|
||||
|
||||
enum FP_GameState
|
||||
{
|
||||
FP_GameState_IntroScreen,
|
||||
FP_GameState_Tutorial,
|
||||
FP_GameState_Play,
|
||||
FP_GameState_Pause,
|
||||
FP_GameState_WinGame,
|
||||
@@ -365,6 +372,35 @@ struct FP_Particle
|
||||
Dqn_usize end_ms;
|
||||
};
|
||||
|
||||
enum FP_GameStateTutorial
|
||||
{
|
||||
FP_GameStateTutorial_ShowPlayer,
|
||||
FP_GameStateTutorial_ShowPlayerWait,
|
||||
FP_GameStateTutorial_ShowPortalOne,
|
||||
FP_GameStateTutorial_ShowPortalOneWait,
|
||||
FP_GameStateTutorial_ShowPortalTwo,
|
||||
FP_GameStateTutorial_ShowPortalTwoWait,
|
||||
FP_GameStateTutorial_ShowPortalThree,
|
||||
FP_GameStateTutorial_ShowPortalThreeWait,
|
||||
FP_GameStateTutorial_ShowBillboardBuild,
|
||||
FP_GameStateTutorial_ShowBillboardBuildWait,
|
||||
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
|
||||
{
|
||||
TELY_ChunkPool *chunk_pool;
|
||||
@@ -388,6 +424,7 @@ struct FP_GamePlay
|
||||
FP_GameEntityHandle merchant_graveyard;
|
||||
FP_GameEntityHandle merchant_gym;
|
||||
FP_GameEntityHandle merchant_phone_company;
|
||||
FP_GameEntityHandle billboard_build;
|
||||
|
||||
FP_GameEntityHandle clicked_entity;
|
||||
FP_GameEntityHandle hot_entity;
|
||||
@@ -429,6 +466,12 @@ struct FP_GamePlay
|
||||
|
||||
FP_Particle particles[256];
|
||||
uint32_t particle_next_index;
|
||||
|
||||
FP_GameStateTutorial tutorial_state;
|
||||
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
|
||||
@@ -475,7 +518,7 @@ FP_GamePlaceableBuilding const PLACEABLE_BUILDINGS[] = {
|
||||
{FP_EntityType_AirportTerry, FP_EntityAirportTerryState_Idle},
|
||||
{FP_EntityType_ChurchTerry, FP_EntityChurchTerryState_Idle},
|
||||
{FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle},
|
||||
{FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle},
|
||||
// {FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle},
|
||||
};
|
||||
|
||||
struct FP_GameCanMoveToPositionResult
|
||||
|
||||
@@ -25,7 +25,7 @@ forwards them to the command line).
|
||||
You must ensure you have MSVC's `cl.exe` on your path to build. This game was
|
||||
tested on MSVC 2023 (v14.34.31933).
|
||||
|
||||
For most intents and purposes you may simply execute `build.bat` on Windows
|
||||
For most intents and purposes you may simply execute `build_all.bat` on Windows
|
||||
which bootstraps the build program and automatically calls the meta-build
|
||||
program. With fast computers nowadays our default is to build all targets by
|
||||
default (except web due to extra toolchain requirements).
|
||||
@@ -57,7 +57,7 @@ then be done by
|
||||
Tools\emsdk\emsdk.bat activate latest
|
||||
|
||||
# Build the game
|
||||
build --web
|
||||
build_all --web
|
||||
```
|
||||
|
||||
This will produce at `Build\Terry_Cherry_Emscripten` a HTML and WASM file ready
|
||||
|
||||
Reference in New Issue
Block a user