fp: Add scanlines for A E S T H E T I C S, Add increasing enemy health for waves

This commit is contained in:
Joshalosh 2023-10-06 08:49:04 +11:00
parent efaf2ac1cc
commit 7d0fde9a62
59 changed files with 670 additions and 61 deletions

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

View File

@ -172,12 +172,17 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle,
bool entity_collides_with_collider = true;
switch (entity->type) {
case FP_EntityType_Smoochie: {
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger)
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger || collider->type == FP_EntityType_Catfish)
entity_collides_with_collider = false;
} break;
case FP_EntityType_Clinger: {
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger)
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger || collider->type == FP_EntityType_Catfish)
entity_collides_with_collider = false;
} break;
case FP_EntityType_Catfish: {
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger || collider->type == FP_EntityType_Catfish)
entity_collides_with_collider = false;
} break;
@ -199,6 +204,9 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle,
case FP_EntityType_MerchantGym: break;
case FP_EntityType_MerchantPhoneCompany: break;
case FP_EntityType_Heart: break;
case FP_EntityType_AirportTerry: break;
case FP_EntityType_ChurchTerry: break;
case FP_EntityType_KennelTerry: break;
}
if (!entity_collides_with_collider)
@ -533,6 +541,17 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
}
} break;
case FP_EntityTerryState_AttackPhone: {
if (entering_new_state) {
uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
if (action_has_finished) {
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle);
}
} break;
case FP_EntityTerryState_Run: {
if (entering_new_state || action->sprite.anim->label != render_data.anim_name) {
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
@ -566,7 +585,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} break;
}
if (*state == FP_EntityTerryState_Attack) {
if (*state == FP_EntityTerryState_Attack || *state == FP_EntityTerryState_AttackPhone) {
DQN_ASSERT(action->sprite.anim);
uint64_t duration_ms = action->sprite.anim->count * action->sprite.anim->ms_per_frame;
@ -971,7 +990,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} break;
case FP_EntityType_Map: {
FP_EntityMapState *state = DQN_CAST(FP_EntityMapState *) & action->state;
FP_EntityMapState *state = DQN_CAST(FP_EntityMapState *) & action->state;
switch (*state) {
case FP_EntityMapState_Nil: {
FP_Game_EntityTransitionState(game, entity, FP_EntityMapState_Idle);
@ -988,7 +1007,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} break;
case FP_EntityType_Heart: {
FP_EntityHeartState *state = DQN_CAST(FP_EntityHeartState *) & action->state;
FP_EntityHeartState *state = DQN_CAST(FP_EntityHeartState *) & action->state;
switch (*state) {
case FP_EntityHeartState_Nil: {
FP_Game_EntityTransitionState(game, entity, FP_EntityHeartState_Idle);
@ -1003,7 +1022,162 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
}
} break;
case FP_EntityType_AirportTerry: {
FP_EntityAirportTerryState *state = DQN_CAST(FP_EntityAirportTerryState *)&action->state;
switch (*state) {
case FP_EntityAirportTerryState_Nil: {
FP_Game_EntityTransitionState(game, entity, FP_EntityAirportTerryState_Idle);
} break;
case FP_EntityAirportTerryState_Idle: {
if (entering_new_state) {
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
} break;
case FP_EntityAirportTerryState_FlyPassenger: {
} break;
}
} break;
case FP_EntityType_Catfish: {
FP_EntityCatfishState *state = DQN_CAST(FP_EntityCatfishState *) & action->state;
switch (*state) {
case FP_EntityCatfishState_Nil: {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Idle);
} break;
case FP_EntityCatfishState_Idle: {
if (entering_new_state) {
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
if (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Attack);
} else if (acceleration_meters_per_s->x || acceleration_meters_per_s->y) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Run);
}
}
if (entity_has_velocity) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Run);
}
} break;
case FP_EntityCatfishState_Attack: {
if (entering_new_state) {
uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
if (action_has_finished)
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Idle);
} break;
case FP_EntityCatfishState_Death: {
if (entering_new_state) {
uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
if (action_has_finished)
FP_Game_DeleteEntity(game, entity->handle);
} break;
case FP_EntityCatfishState_Run: {
if (entering_new_state || action->sprite.anim->label != render_data.anim_name) {
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, render_data.anim_name, render_data.flip);
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
}
if (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Attack);
}
}
if (!entity_has_velocity) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Idle);
}
} break;
}
if (entity->is_dying && *state != FP_EntityCatfishState_Death) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Death);
}
if (*state == FP_EntityCatfishState_Attack) { // NOTE: Position the attack box
entity->attack_box_size = entity->local_hit_box_size;
switch (entity->direction) {
case FP_GameDirection_Left: {
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
entity->local_hit_box_offset.y);
} break;
case FP_GameDirection_Right: {
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->attack_box_size.w,
entity->local_hit_box_offset.y);
} break;
case FP_GameDirection_Up: {
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
entity->local_hit_box_offset.y - entity->attack_box_size.h);
} break;
case FP_GameDirection_Down: {
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
entity->local_hit_box_offset.y + entity->attack_box_size.h);
} break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} else {
entity->attack_box_size = {};
}
} break;
case FP_EntityType_ChurchTerry: {
FP_EntityChurchTerryState *state = DQN_CAST(FP_EntityChurchTerryState *)&action->state;
switch (*state) {
case FP_EntityChurchTerryState_Nil: {
FP_Game_EntityTransitionState(game, entity, FP_EntityChurchTerryState_Idle);
} break;
case FP_EntityChurchTerryState_Idle: {
if (entering_new_state) {
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
} break;
case FP_EntityChurchTerryState_ConvertPatron: break;
}
} break;
case FP_EntityType_KennelTerry: {
FP_EntityKennelTerryState *state = DQN_CAST(FP_EntityKennelTerryState *)&action->state;
switch (*state) {
case FP_EntityKennelTerryState_Nil: {
FP_Game_EntityTransitionState(game, entity, FP_EntityKennelTerryState_Idle);
} break;
case FP_EntityKennelTerryState_Idle: {
if (entering_new_state) {
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
}
} break;
}
} break;
case FP_EntityType_Nil: break;
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
}
}
@ -1315,6 +1489,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
switch (entity->type) {
case FP_EntityType_Terry: /*FALLTHRU*/
case FP_EntityType_Smoochie: /*FALLTHRU*/
case FP_EntityType_Catfish: /*FALLTHRU*/
case FP_EntityType_Clinger: {
// TODO(doyle): We should check if it's valid to enter this new state
// from the entity's current state
@ -1322,6 +1497,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack);
} else if (entity->type == FP_EntityType_Smoochie) {
FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Attack);
} else if (entity->type == FP_EntityType_Catfish) {
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Attack);
} else {
DQN_ASSERT(entity->type == FP_EntityType_Clinger);
FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Attack);
@ -1340,6 +1517,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
case FP_EntityType_MerchantGraveyard: break;
case FP_EntityType_MerchantGym: break;
case FP_EntityType_MerchantPhoneCompany: break;
case FP_EntityType_AirportTerry:
case FP_EntityType_ChurchTerry:
case FP_EntityType_KennelTerry: break;
}
}
@ -1375,10 +1555,29 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (entity->flags & FP_GameEntityFlag_CameraTracking)
game->camera.world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle) - Dqn_V2_InitV2I(platform->core.window_size) * .5f;
game->build_mode_can_place_building = false;
if (game->build_mode) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_E)) {
Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle);
Dqn_V2 placement_pos = Dqn_Rect_Center(dest_rect);
Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle);
Dqn_V2 placement_pos = Dqn_Rect_Center(dest_rect);
for (FP_GameEntityIterator zone_it = {};
!game->build_mode_can_place_building && FP_Game_DFSPostOrderWalkEntityTree(game, &zone_it, game->root_entity);
) {
FP_GameEntity *zone = zone_it.entity;
if ((zone->flags & FP_GameEntityFlag_BuildZone) == 0)
continue;
Dqn_Rect zone_hit_box = FP_Game_CalcEntityWorldHitBox(game, zone->handle);
zone_hit_box.pos += dest_rect.size * .5f;
zone_hit_box.size -= dest_rect.size;
zone_hit_box.size = Dqn_V2_Max(zone_hit_box.size, Dqn_V2_Zero);
game->build_mode_can_place_building = Dqn_Rect_ContainsPoint(zone_hit_box, placement_pos);
}
if (game->build_mode_can_place_building &&
TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_E)) {
FP_Entity_CreateClubTerry(game, placement_pos, "Club Terry");
}
}
@ -1439,15 +1638,19 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
}
} else if (entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities
if (input->timer_s >= entity->next_spawn_timestamp_s) {
uint64_t hp_adjustment = entity->current_wave - 1;
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f);
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
FP_SentinelListLink<FP_GameEntityHandle> *link = FP_SentinelList_Make(&entity->spawn_list, game->chunk_pool);
if (Dqn_PCG32_NextF32(&game->rng) >= 0.5f)
link->data = FP_Entity_CreateClinger(game, entity_world_pos, "Clinger");
Dqn_f32 mob_choice = Dqn_PCG32_NextF32(&game->rng);
if (mob_choice <= 0.33f)
link->data = FP_Entity_CreateClinger(game, entity_world_pos, hp_adjustment, "Clinger");
else if (mob_choice <= 0.66f)
link->data = FP_Entity_CreateSmoochie(game, entity_world_pos, hp_adjustment, "Smoochie");
else
link->data = FP_Entity_CreateSmoochie(game, entity_world_pos, "Smoochie");
link->data = FP_Entity_CreateCatfish(game, entity_world_pos, hp_adjustment, "Catfish");
// NOTE: Setup the mob with waypoints
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
@ -1499,12 +1702,17 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
bool permit_attack = true;
switch (attacker->type) {
case FP_EntityType_Smoochie: {
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger)
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger || defender->type == FP_EntityType_Catfish)
permit_attack = false;
} break;
case FP_EntityType_Catfish: {
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger || defender->type == FP_EntityType_Catfish)
permit_attack = false;
} break;
case FP_EntityType_Clinger: {
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger)
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger || defender->type == FP_EntityType_Catfish)
permit_attack = false;
} break;
@ -1518,6 +1726,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
case FP_EntityType_MerchantGym: break;
case FP_EntityType_MerchantPhoneCompany: break;
case FP_EntityType_Heart: break;
case FP_EntityType_AirportTerry:
case FP_EntityType_ChurchTerry:
case FP_EntityType_KennelTerry: break;
}
if (!permit_attack)
@ -1767,6 +1978,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
FP_EntityRenderData club_terry_render_data = FP_Entity_GetRenderData(game, FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle, entity->direction);
Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle);
Dqn_V4 colour = game->build_mode_can_place_building ?
TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f) :
TELY_Colour_V4Alpha(TELY_COLOUR_RED_V4, 0.5f);
TELY_Render_RectColourV4(renderer, dest_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_BLUE_CADET_V4, 0.5f));
TELY_Render_TextureColourV4(renderer,
club_terry_render_data.sheet->tex_handle,
@ -1774,7 +1989,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
dest_rect,
Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/,
TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f));
colour);
}
// NOTE: Render hot/active entity ==========================================================
@ -1806,6 +2021,12 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
}
}
}
// NOTE: Add scanlines into the game for A E S T H E T I C S
Dqn_V2 screen_size = Dqn_V2_InitNx2(platform->core.window_size.w, platform->core.window_size.h);
Dqn_f32 scanline_gap = 4.0f;
Dqn_f32 scanline_thickness = 3.0f;
FP_GameRenderCameraFollowScanlines(renderer, screen_size, game->camera.world_pos, scanline_gap, scanline_thickness);
}
extern "C" __declspec(dllexport)

View File

@ -23,30 +23,60 @@ struct FP_Meters
struct FP_GlobalAnimations
{
Dqn_String8 airport_terry_alive = DQN_STRING8("airport_terry_alive");
Dqn_String8 airport_terry_dark = DQN_STRING8("airport_terry_dark");
Dqn_String8 catfish_attack_down = DQN_STRING8("catfish_attack_down");
Dqn_String8 catfish_attack_side = DQN_STRING8("catfish_attack_side");
Dqn_String8 catfish_attack_up = DQN_STRING8("catfish_attack_up");
Dqn_String8 catfish_death = DQN_STRING8("catfish_death");
Dqn_String8 catfish_walk_up = DQN_STRING8("catfish_walk_up");
Dqn_String8 catfish_walk_side = DQN_STRING8("catfish_walk_side");
Dqn_String8 catfish_walk_down = DQN_STRING8("catfish_walk_down");
Dqn_String8 clinger_attack_down = DQN_STRING8("clinger_attack_down");
Dqn_String8 clinger_attack_side = DQN_STRING8("clinger_attack_side");
Dqn_String8 clinger_attack_up = DQN_STRING8("clinger_attack_up");
Dqn_String8 clinger_death = DQN_STRING8("clinger_death");
Dqn_String8 clinger_walk_up = DQN_STRING8("clinger_walk_up");
Dqn_String8 clinger_walk_down = DQN_STRING8("clinger_walk_down");
Dqn_String8 church_terry_alive = DQN_STRING8("church_terry_alive");
Dqn_String8 church_terry_dark = DQN_STRING8("church_terry_dark");
Dqn_String8 club_terry_alive = DQN_STRING8("club_terry_alive");
Dqn_String8 club_terry_dark = DQN_STRING8("club_terry_dark");
Dqn_String8 heart = DQN_STRING8("heart");
Dqn_String8 heart_bleed = DQN_STRING8("heart_bleed");
Dqn_String8 kennel_terry = DQN_STRING8("kennel_terry");
Dqn_String8 map = DQN_STRING8("map");
Dqn_String8 merchant_graveyard = DQN_STRING8("merchant_graveyard");
Dqn_String8 merchant_gym = DQN_STRING8("merchant_gym");
Dqn_String8 merchant_phone_company= DQN_STRING8("merchant_phone_company");
Dqn_String8 merchant_terry = DQN_STRING8("merchant_terry");
Dqn_String8 merchant_button_a = DQN_STRING8("merchant_button_a");
Dqn_String8 merchant_button_b = DQN_STRING8("merchant_button_b");
Dqn_String8 merchant_button_x = DQN_STRING8("merchant_button_x");
Dqn_String8 merchant_button_y = DQN_STRING8("merchant_button_y");
Dqn_String8 merchant_graveyard = DQN_STRING8("merchant_graveyard");
Dqn_String8 merchant_graveyard_menu = DQN_STRING8("merchant_graveyard");
Dqn_String8 merchant_gym = DQN_STRING8("merchant_gym");
Dqn_String8 merchant_gym_menu = DQN_STRING8("merchant_gym_menu");
Dqn_String8 merchant_phone_company = DQN_STRING8("merchant_phone_company");
Dqn_String8 merchant_phone_company_menu = DQN_STRING8("merchant_phone_company_menu");
Dqn_String8 merchant_terry = DQN_STRING8("merchant_terry");
Dqn_String8 merchant_terry_menu = DQN_STRING8("merchant_terry_menu");
Dqn_String8 shadow_long_circle = DQN_STRING8("shadow_long_circle");
Dqn_String8 shadow_tight_circle = DQN_STRING8("shadow_tight_circle");
Dqn_String8 shrubbery_bush_1 = DQN_STRING8("shrubbery_bush_1");
Dqn_String8 shrubbery_bush_2 = DQN_STRING8("shrubbery_bush_2");
Dqn_String8 shrubbery_island_1 = DQN_STRING8("shrubbery_island_1");
Dqn_String8 shrubbery_island_2 = DQN_STRING8("shrubbery_island_2");
Dqn_String8 shrubbery_island_3 = DQN_STRING8("shrubbery_island_3");
Dqn_String8 smoochie_walk_up = DQN_STRING8("smoochie_walk_up");
Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down");
Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left");
@ -56,15 +86,18 @@ struct FP_GlobalAnimations
Dqn_String8 smoochie_attack_heart = DQN_STRING8("smoochie_attack_heart");
Dqn_String8 smoochie_death = DQN_STRING8("smoochie_death");
Dqn_String8 terry_attack_up = DQN_STRING8("terry_attack_up");
Dqn_String8 terry_attack_side = DQN_STRING8("terry_attack_side");
Dqn_String8 terry_attack_down = DQN_STRING8("terry_attack_down");
Dqn_String8 terry_walk_idle = DQN_STRING8("terry_walk_idle");
Dqn_String8 terry_walk_up = DQN_STRING8("terry_walk_up");
Dqn_String8 terry_walk_down = DQN_STRING8("terry_walk_down");
Dqn_String8 terry_walk_left = DQN_STRING8("terry_walk_left");
Dqn_String8 terry_walk_right = DQN_STRING8("terry_walk_right");
Dqn_String8 terry_attack_up = DQN_STRING8("terry_attack_up");
Dqn_String8 terry_attack_side = DQN_STRING8("terry_attack_side");
Dqn_String8 terry_attack_down = DQN_STRING8("terry_attack_down");
Dqn_String8 terry_attack_phone_up = DQN_STRING8("terry_attack_phone_up");
Dqn_String8 terry_attack_phone_side = DQN_STRING8("terry_attack_phone_side");
Dqn_String8 terry_attack_phone_down = DQN_STRING8("terry_attack_phone_down");
Dqn_String8 terry_attack_phone_message = DQN_STRING8("terry_attack_phone_message");
Dqn_String8 terry_walk_idle = DQN_STRING8("terry_walk_idle");
Dqn_String8 terry_walk_up = DQN_STRING8("terry_walk_up");
Dqn_String8 terry_walk_down = DQN_STRING8("terry_walk_down");
Dqn_String8 terry_walk_left = DQN_STRING8("terry_walk_left");
Dqn_String8 terry_walk_right = DQN_STRING8("terry_walk_right");
}
g_anim_names;

View File

@ -6,16 +6,20 @@
enum FP_EntityType
{
FP_EntityType_Nil,
FP_EntityType_AirportTerry,
FP_EntityType_Catfish,
FP_EntityType_ChurchTerry,
FP_EntityType_Clinger,
FP_EntityType_ClubTerry,
FP_EntityType_Heart,
FP_EntityType_KennelTerry,
FP_EntityType_Map,
FP_EntityType_Terry,
FP_EntityType_Smoochie,
FP_EntityType_MerchantTerry,
FP_EntityType_MerchantGraveyard,
FP_EntityType_MerchantGym,
FP_EntityType_MerchantPhoneCompany,
FP_EntityType_ClubTerry,
FP_EntityType_Clinger,
FP_EntityType_Heart,
FP_EntityType_MerchantTerry,
FP_EntityType_Smoochie,
FP_EntityType_Terry,
FP_EntityType_Count,
};
@ -24,6 +28,7 @@ enum FP_EntityTerryState
FP_EntityTerryState_Nil,
FP_EntityTerryState_Idle,
FP_EntityTerryState_Attack,
FP_EntityTerryState_AttackPhone,
FP_EntityTerryState_Run,
FP_EntityTerryState_Dash,
};
@ -38,6 +43,15 @@ enum FP_EntitySmoochieState
FP_EntitySmoochieState_Run,
};
enum FP_EntityCatfishState
{
FP_EntityCatfishState_Nil,
FP_EntityCatfishState_Idle,
FP_EntityCatfishState_Attack,
FP_EntityCatfishState_Death,
FP_EntityCatfishState_Run,
};
enum FP_EntityClingerState
{
FP_EntityClingerState_Nil,
@ -78,6 +92,26 @@ enum FP_EntityClubTerryState
FP_EntityClubTerryState_PartyTime,
};
enum FP_EntityAirportTerryState
{
FP_EntityAirportTerryState_Nil,
FP_EntityAirportTerryState_Idle,
FP_EntityAirportTerryState_FlyPassenger,
};
enum FP_EntityChurchTerryState
{
FP_EntityChurchTerryState_Nil,
FP_EntityChurchTerryState_Idle,
FP_EntityChurchTerryState_ConvertPatron,
};
enum FP_EntityKennelTerryState
{
FP_EntityKennelTerryState_Nil,
FP_EntityKennelTerryState_Idle,
};
enum FP_EntityMapState
{
FP_EntityMapState_Nil,

View File

@ -39,7 +39,17 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u
}
} break;
case FP_EntityTerryState_Run: /*FALLTHRU*/
case FP_EntityTerryState_AttackPhone: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_attack_phone_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.terry_attack_phone_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_attack_phone_side; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_attack_phone_side; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_Run: /*FALLTHRU*/
case FP_EntityTerryState_Dash: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_walk_up; break;
@ -160,6 +170,63 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u
}
} break;
case FP_EntityType_AirportTerry: {
result.height.meters = 4.f;
FP_EntityAirportTerryState state = DQN_CAST(FP_EntityAirportTerryState)raw_state;
switch (state) {
case FP_EntityAirportTerryState_Nil: break;
case FP_EntityAirportTerryState_Idle: result.anim_name = g_anim_names.airport_terry_dark; break;
case FP_EntityAirportTerryState_FlyPassenger: result.anim_name = g_anim_names.airport_terry_alive; break;
}
} break;
case FP_EntityType_Catfish: {
result.height.meters = 1.6f;
FP_EntityCatfishState state = DQN_CAST(FP_EntityCatfishState)raw_state;
switch (state) {
case FP_EntityCatfishState_Nil:
case FP_EntityCatfishState_Idle: result.anim_name = g_anim_names.catfish_walk_down; break;
case FP_EntityCatfishState_Attack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.catfish_attack_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.catfish_attack_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.catfish_attack_side; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.catfish_attack_side; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityCatfishState_Death: result.anim_name = g_anim_names.catfish_death; break;
case FP_EntityCatfishState_Run: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.catfish_walk_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.catfish_walk_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.catfish_walk_side; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.catfish_walk_side; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
}
} break;
case FP_EntityType_ChurchTerry: {
result.height.meters = 4.f;
FP_EntityChurchTerryState state = DQN_CAST(FP_EntityChurchTerryState)raw_state;
switch (state) {
case FP_EntityChurchTerryState_Nil: break;
case FP_EntityChurchTerryState_Idle: result.anim_name = g_anim_names.church_terry_dark; break;
case FP_EntityChurchTerryState_ConvertPatron: result.anim_name = g_anim_names.church_terry_alive; break;
}
} break;
case FP_EntityType_KennelTerry: {
result.height.meters = 4.f;
FP_EntityKennelTerryState state = DQN_CAST(FP_EntityKennelTerryState)raw_state;
switch (state) {
case FP_EntityKennelTerryState_Nil: break;
case FP_EntityKennelTerryState_Idle: result.anim_name = g_anim_names.kennel_terry; break;
}
} break;
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
}
@ -207,7 +274,7 @@ static FP_GameEntityHandle FP_Entity_CreateWaypointF(FP_Game *game, Dqn_V2 pos,
return result;
}
static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, uint64_t hp_adjustment, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
@ -216,7 +283,7 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ
va_end(args);
entity->type = FP_EntityType_Clinger;
entity->hp = 1;
entity->hp = 1 + hp_adjustment;
entity->is_dying = false;
entity->base_acceleration_per_s.meters = 8.f;
entity->local_pos = pos;
@ -230,7 +297,7 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ
return result;
}
static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, uint64_t hp_adjustment, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
@ -240,7 +307,29 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D
entity->type = FP_EntityType_Smoochie;
entity->base_acceleration_per_s.meters = 8.f;
entity->hp = 1;
entity->hp = 1 + hp_adjustment;
entity->is_dying = false;
entity->local_pos = pos;
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
entity->attack_cooldown_ms = 1000;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .6f);
FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_Attackable;
return result;
}
static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, uint64_t hp_adjustment, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntityHandle result = entity->handle;
va_end(args);
entity->type = FP_EntityType_Catfish;
entity->base_acceleration_per_s.meters = 8.f;
entity->hp = 1 + hp_adjustment;
entity->is_dying = false;
entity->local_pos = pos;
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
@ -499,3 +588,75 @@ static FP_GameEntityHandle FP_Entity_CreateHeart(FP_Game *game, Dqn_V2 pos, DQN_
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w - (sprite_rect_scaled.w * .3f), sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateChurchTerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntityHandle result = entity->handle;
va_end(args);
entity->type = FP_EntityType_ChurchTerry;
entity->local_pos = pos;
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.church_terry_alive);
Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index];
Dqn_f32 size_scale = FP_Entity_CalcSpriteScaleForDesiredHeight(game, entity->sprite_height, sprite_rect);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w, sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateKennelTerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntityHandle result = entity->handle;
va_end(args);
entity->type = FP_EntityType_KennelTerry;
entity->local_pos = pos;
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.church_terry_alive);
Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index];
Dqn_f32 size_scale = FP_Entity_CalcSpriteScaleForDesiredHeight(game, entity->sprite_height, sprite_rect);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w, sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateAirportTerry(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntityHandle result = entity->handle;
va_end(args);
entity->type = FP_EntityType_AirportTerry;
entity->local_pos = pos;
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.church_terry_alive);
Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index];
Dqn_f32 size_scale = FP_Entity_CalcSpriteScaleForDesiredHeight(game, entity->sprite_height, sprite_rect);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w, sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
return result;
}

View File

@ -767,3 +767,33 @@ static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity,
// NOTE: If no returns are hit above we proceed with the state change
entity->action.next_state = desired_state;
}
static void FP_GameRenderScanlines(TELY_Renderer *renderer, Dqn_f32 scanline_gap, Dqn_f32 scanline_thickness,
Dqn_V2 screen_size, Dqn_V2 camera_offset)
{
Dqn_f32 scanline_interval = scanline_gap + scanline_thickness;
Dqn_f32 y_offset = fmodf(camera_offset.y, scanline_interval);
for (Dqn_f32 y = -y_offset; y < screen_size.h; y += scanline_interval)
{
Dqn_V2 start = Dqn_V2_InitNx2(0, y);
Dqn_V2 end = Dqn_V2_InitNx2(screen_size.w, y);
TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f), scanline_thickness);
}
}
static void FP_GameRenderCameraFollowScanlines(TELY_Renderer *renderer, Dqn_V2 screen_size, Dqn_V2 camera_offset, Dqn_f32 scanline_gap, Dqn_f32 scanline_thickness)
{
Dqn_f32 y_offset = camera_offset.y;
// Adjust starting y to be more negative
Dqn_f32 starting_y = -screen_size.h - 150 - y_offset;
for (Dqn_f32 y = starting_y; y < screen_size.h; y += scanline_gap + scanline_thickness)
{
Dqn_V2 start = Dqn_V2_InitNx2(camera_offset.x, y + camera_offset.y);
Dqn_V2 end = Dqn_V2_InitNx2(screen_size.w + camera_offset.x, y + camera_offset.y);
TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_BLACK_V4, 0.5f), scanline_thickness);
}
}

View File

@ -256,7 +256,8 @@ struct FP_Game
uint64_t clock_ms;
Dqn_PCG32 rng;
bool build_mode;
bool build_mode;
bool build_mode_can_place_building;
};
struct FP_GameAStarNode

Binary file not shown.