Add breathing bubbles, remaining air count and breathable bubbles

This commit is contained in:
Lucas S. Vieira 2024-12-21 01:15:11 -03:00
parent 7d98ff3d48
commit 90164a1b97
7 changed files with 80 additions and 6 deletions

BIN
assets/sfx/BUBBLE.ogg Normal file

Binary file not shown.

BIN
assets/sfx/COUNT.ogg Normal file

Binary file not shown.

View file

@ -86,7 +86,8 @@ typedef struct {
// TODO: Change this to player value modes.
// For now we're only storing info on whether
// player is underwater
uint8_t underwater;
uint8_t underwater;
uint16_t remaining_air_frames;
// Collision modes
CollMode gsmode;

View file

@ -310,6 +310,12 @@
<file name="SPLASH.VAG"
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sfx/SPLASH.VAG" />
<file name="COUNT.VAG"
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sfx/COUNT.VAG" />
<file name="BUBBLE.VAG"
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sfx/BUBBLE.VAG" />
</dir>
<dir name="BGM">

View file

@ -14,6 +14,8 @@
#include "screen.h"
#include "screens/level.h"
#define ANIM_WALKING 0x0854020e
// Extern elements
extern Player player;
extern Camera camera;
@ -29,6 +31,7 @@ extern SoundEffect sfx_ringl;
extern SoundEffect sfx_shield;
extern SoundEffect sfx_yea;
extern SoundEffect sfx_switch;
extern SoundEffect sfx_bubble;
extern int debug_mode;
@ -726,7 +729,7 @@ _bubble_patch_update(ObjectState *state, ObjectTableEntry *, VECTOR *pos)
}
static void
_bubble_update(ObjectState *state, ObjectTableEntry *, VECTOR *)
_bubble_update(ObjectState *state, ObjectTableEntry *, VECTOR *pos)
{
// NOTE: this object can only exist as a free object. Do not insist.
@ -786,7 +789,21 @@ _bubble_update(ObjectState *state, ObjectTableEntry *, VECTOR *)
// technically have a whole area available to add it, but I felt like I
// shouldn't create a whole new texture this time just because of a single
// bubble frame.
if(state->anim_state.animation == 2 && state->anim_state.frame == 5) {
// TODO
if((state->anim_state.animation == 2) && (state->anim_state.frame == 5)) {
// Bubble has an active trigger area of 32x16 at its bottom so we
// always overlap Sonic's mouth.
if(aabb_intersects(player_vx, player_vy, player_width, player_height,
pos->vx - 16, pos->vy - 16, 32, 16)) {
state->props |= OBJ_FLAG_DESTROYED;
player.remaining_air_frames = 1800;
// TODO: Cancel any drowning music.
// TODO: Setup proper action.
player.action = ACTION_NONE;
player_set_animation_direct(&player, ANIM_WALKING); // TODO!!!
player.grnd = 0;
player.vel.vy = 0;
sound_play_vag(sfx_bubble, 0);
return;
}
}
}

View file

@ -47,6 +47,8 @@ SoundEffect sfx_shield = { 0 };
SoundEffect sfx_yea = { 0 };
SoundEffect sfx_switch = { 0 };
SoundEffect sfx_splash = { 0 };
SoundEffect sfx_count = { 0 };
SoundEffect sfx_bubble = { 0 };
// TODO: Maybe shouldn't be extern?
extern TileMap16 map16;
@ -76,6 +78,7 @@ load_player(Player *player,
player->underwater = 0;
player->gsmode = CDIR_FLOOR;
player->psmode = CDIR_FLOOR;
player->remaining_air_frames = 1800; // 30 seconds
player_set_animation_direct(player, ANIM_STOPPED);
player->anim_frame = player->anim_timer = 0;
@ -108,6 +111,8 @@ load_player(Player *player,
if(sfx_yea.addr == 0) sfx_yea = sound_load_vag("\\SFX\\YEA.VAG;1");
if(sfx_switch.addr == 0) sfx_switch = sound_load_vag("\\SFX\\SWITCH.VAG;1");
if(sfx_splash.addr == 0) sfx_splash = sound_load_vag("\\SFX\\SPLASH.VAG;1");
if(sfx_count.addr == 0) sfx_count = sound_load_vag("\\SFX\\COUNT.VAG;1");
if(sfx_bubble.addr == 0) sfx_bubble = sound_load_vag("\\SFX\\BUBBLE.VAG;1");
}
void
@ -1001,6 +1006,50 @@ player_update(Player *player)
explosion->state.anim_state.animation = 2; // Water splash
sound_play_vag(sfx_splash, 0);
}
if(!player->underwater)
// Reset to 30 secs of air
player->remaining_air_frames = 1800;
else {
if(player->remaining_air_frames > 0) player->remaining_air_frames--;
uint8_t emit_bubble = !((player->remaining_air_frames + 1) % 120)
&& (player->remaining_air_frames > 0);
switch(player->remaining_air_frames) {
case (25 * 60):
case (20 * 60):
case (15 * 60):
// TODO: Warning chime
sound_play_vag(sfx_count, 0);
break;
case (12 * 60):
// TODO: Start drowning music
// TODO: Warning bubble "5"
emit_bubble = 1;
break;
case (10 * 60): emit_bubble = 1; break; // TODO: Warning bubble "4"
case (8 * 60): emit_bubble = 1; break; // TODO: Warning bubble "3"
case (6 * 60): emit_bubble = 1; break; // TODO: Warning bubble "2"
case (4 * 60): emit_bubble = 1; break; // TODO: Warning bubble "1"
case (2 * 60): emit_bubble = 1; break; // TODO: Warning bubble "0"
case 0: break; // TODO: DROWNED!
}
// Bubble emission
if(emit_bubble) {
// TODO: Either emit 1 or 2 bubbles. In case of 2,
// First one has 1/4 chance of being a number bubble,
// and the second is definitely a number bubble if not emitted
// previously.
// For now we emit a single small bubble.
PoolObject *bubble = object_pool_create(OBJ_BUBBLE);
if(bubble) {
bubble->state.anim_state.animation = 0;
bubble->freepos.vx = player->pos.vx + (0x6000 * player->anim_dir);
bubble->freepos.vy = player->pos.vy;
}
}
}
}
// Screen transform

View file

@ -576,7 +576,7 @@ screen_level_draw(void *d)
"SPD %08x %08x\n"
"ANG %08x G.P %s %s %3d\n"
"POS %08x %08x\n"
"ACT %02u\n"
"ACT %02u AIR %02u\n"
,
player.vel.vz,
player.vel.vx, player.vel.vy,
@ -601,7 +601,8 @@ screen_level_draw(void *d)
: " ",
(int32_t)(((int32_t)player.angle * (int32_t)(360 << 12)) >> 24), // angle in deg
player.pos.vx, player.pos.vy,
player.action
player.action,
player.remaining_air_frames / 60
);
font_draw_sm(buffer, 8, 12);
}