diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2022-11-30 23:25:35 -0800 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2022-12-02 12:09:43 -0800 |
commit | c87a603089a8dad5122d2cf4abcb99d39c79b164 (patch) | |
tree | aeea784a4ff76fd7e9ddd15db8ee28d605d7aa03 | |
parent | 1d18b18e06bd0ea38e48265a09d129406a346e87 (diff) |
game: respawn babies on a timer, and detect immediate infections
Previously respawned babies could respawn on a virus without
becoming infected, and this actually had a calming effect on the
late stage of the game which is the opposite of what should be
happening as the field fills up with viruses.
What would happen is eventually most of the active set of babies
would be sitting on viruses and isolated by viruses so they
wouldn't likely be resuced, but they also weren't becoming
infected despite being on viruses.
With this change, baby respawning is handled strictly by a timer,
currently every .5s. In that timer handler the number of babies
off from the GAME_NUM_BABIES (10 presently) are attempted to be
respawned. Any of those which are immediately respawned onto
viruses are immediately infected. Then the same thing is
revisted on the next timer expiration.
So it doesn't try very hard to maintain GAME_NUM_BABIES as alive
at all times - this actually becomes impossible when the field is
too crowded to even find spots to respawn without infection.
Instead, what will happen, is in the end game you just have
constant babies respawning and dying every .5s.
The gameplay is dramatically different now, and it's potentially
too difficult now to actually manage to hoard 256 rolls of
TP before dying. I haven't managed it in my testing so far...
so the probabilities may need to be revisited again.
However, the end game /feels/ much more _right_ after this
change. You end up with just tons of viruses and death, and if
you've managed to hoard a bunch of toilet paper, it's a grim
satirical scene of zero living babies, viruses everywhere, but a
happy pile of TP icons. I can only imagine how ridiculous it
looks if you manage to actually win and get the animated
celebratory wall of TP over the sea of death.
Now the dead babies turned viruses just need their own unique
sprite showing actual infected corpses instead of the same virus
sprite. So it's actually visibly a sea of infected bodies...
-rw-r--r-- | src/game.c | 76 |
1 files changed, 60 insertions, 16 deletions
@@ -54,6 +54,9 @@ #define GAME_ENTITIES_DELAY_MS 20 #define GAME_ENTITIES_TIMER PLAY_TICKS_TIMER1 +#define GAME_NEWBABIES_DELAY_MS 500 +#define GAME_NEWBABIES_TIMER PLAY_TICKS_TIMER6 + #define GAME_MASK_PROTECTION 3 #define GAME_TV_DELAY_MS 3000 @@ -127,9 +130,11 @@ typedef struct mask_t { entity_any_t entity; } mask_t; -typedef struct baby_t { +typedef struct baby_t baby_t; +struct baby_t { entity_any_t entity; -} baby_t; + baby_t *rescues_next; +}; typedef struct virus_t { entity_any_t entity; @@ -194,6 +199,8 @@ typedef struct game_t { unsigned teepee_cnt; teepee_icon_t *teepee_head; entity_any_t *flashers_on_head, *flashers_off_head; + baby_t *rescues_head; + unsigned babies_cnt; adult_t *adult; tv_t *tv; @@ -258,16 +265,19 @@ static adult_t * adult_new(game_t *game, stage_t *parent) } -static baby_t * baby_new(game_t *game, stage_t *parent) +static baby_t * baby_new(game_t *game, stage_t *parent, baby_t *rescue) { - baby_t *baby; + baby_t *baby = rescue; - baby = pad_get(game->pad, sizeof(entity_t)); - fatal_if(!baby, "unable to allocate baby_t"); + if (!baby) { + baby = pad_get(game->pad, sizeof(entity_t)); + fatal_if(!baby, "unable to allocate baby_t"); + baby->entity.type = ENTITY_TYPE_BABY; + baby->entity.node = baby_node_new(&(stage_conf_t){ .parent = parent, .name = "baby", .active = 1, .alpha = 1.f }, &game->sars->projection_x, &baby->entity.model_x); + baby->entity.scale = GAME_BABY_SCALE; + } else + stage_set_active(baby->entity.node, 1); - baby->entity.type = ENTITY_TYPE_BABY; - baby->entity.node = baby_node_new(&(stage_conf_t){ .parent = parent, .name = "baby", .active = 1, .alpha = 1.f }, &game->sars->projection_x, &baby->entity.model_x); - baby->entity.scale = GAME_BABY_SCALE; baby->entity.position.x = randf(); baby->entity.position.y = randf(); entity_update_x(game, &baby->entity); @@ -519,6 +529,10 @@ static ix2_search_status_t tv_search(void *cb_context, ix2_object_t *ix2_object, v2f_t delta; float len; + /* skip inactive babies, since rescues can linger inactive til respawned */ + if (!stage_get_active(entity->any.node)) + return IX2_SEARCH_MORE_MISS; + /* skip held baby */ if (game->adult->holding == entity) return IX2_SEARCH_MORE_MISS; @@ -540,7 +554,7 @@ static ix2_search_status_t tv_search(void *cb_context, ix2_object_t *ix2_object, if (ix2_search_by_aabb(game->ix2, NULL, NULL, &entity->any.aabb_x, baby_search, &search)) { /* baby hit a virus; infect it and spawn a replacement */ infect_entity(game, entity, "baby-virus"); - (void) baby_new(game, game->babies_node); + game->babies_cnt--; } return IX2_SEARCH_MORE_HIT; @@ -569,6 +583,10 @@ static ix2_search_status_t mask_search(void *cb_context, ix2_object_t *ix2_objec switch (entity->any.type) { case ENTITY_TYPE_BABY: + /* skip inactive babies, since rescues can linger inactive til respawned */ + if (!stage_get_active(entity->any.node)) + return IX2_SEARCH_MORE_MISS; + if (stage_get_active(game->mask->entity.node)) hat_baby(game, &entity->baby, game->mask); @@ -604,9 +622,13 @@ static ix2_search_status_t virus_search(void *cb_context, ix2_object_t *ix2_obje switch (entity->any.type) { case ENTITY_TYPE_BABY: + /* skip inactive babies, since rescues can linger inactive til respawned */ + if (!stage_get_active(entity->any.node)) + return IX2_SEARCH_MORE_MISS; + /* virus hit a baby; infect it and spawn a replacement */ infect_entity(search->game, entity, "baby-virus"); - (void) baby_new(search->game, search->game->babies_node); + search->game->babies_cnt--; return IX2_SEARCH_MORE_HIT; @@ -891,10 +913,13 @@ static void game_move_adult(game_t *game, v2f_t *dir) /* rescued baby */ sfx_play(sfx.baby_rescued); - game->adult->holding->any.position.x = randf(); - game->adult->holding->any.position.y = randf(); - entity_update_x(game, &game->adult->holding->any); + /* make the rescued baby available for respawn reuse */ game->adult->holding->any.flashes_remaining = 0; + stage_set_active(game->adult->holding->any.node, 0); + game->adult->holding->baby.rescues_next = game->rescues_head; + game->rescues_head = &game->adult->holding->baby; + game->babies_cnt--; + game->adult->holding = NULL; game->adult->rescues++; } @@ -923,6 +948,7 @@ static void reset_game(game_t *game) game->teepee_cnt = 0; game->teepee_head = NULL; game->flashers_on_head = game->flashers_off_head = NULL; + game->rescues_head = NULL; game->tv = tv_new(game, game->game_node); game->teepee = teepee_new(game, game->game_node); game->mask = mask_new(game, game->game_node); @@ -930,8 +956,9 @@ static void reset_game(game_t *game) for (int i = 0; i < NELEMS(game->viruses); i++) game->viruses[i] = virus_new(game, game->viruses_node); - for (int i = 0; i < GAME_NUM_BABIES; i++) - (void) baby_new(game, game->babies_node); + game->babies_cnt = GAME_NUM_BABIES; + for (int i = 0; i < game->babies_cnt; i++) + (void) baby_new(game, game->babies_node, NULL); stage_set_active(game->adult->entity.node, 1); stage_set_active(game->babies_node, 1); @@ -1097,6 +1124,23 @@ static void game_update(play_t *play, void *context) game->flashers_on_head = new_on; } + if (play_ticks_elapsed(play, GAME_NEWBABIES_TIMER, GAME_NEWBABIES_DELAY_MS)) { + for (unsigned n = GAME_NUM_BABIES - game->babies_cnt; n > 0; n--) { + baby_search_t search = { .game = game, .baby = game->rescues_head }; + + if (search.baby) + game->rescues_head = search.baby->rescues_next; + + search.baby = baby_new(game, game->babies_node, search.baby); + + /* check if the new baby is immediately infected */ + if (ix2_search_by_aabb(game->ix2, NULL, NULL, &search.baby->entity.aabb_x, baby_search, &search)) + infect_entity(game, (entity_t *)search.baby, "baby-virus"); + else + game->babies_cnt++; + } + } + break; } |