orbit-plus/src/game/state/s_play.c

447 lines
9.7 KiB
C

/*
* DESCRIPTION:
* Main gameplay state.
*/
#include "s_play.h"
static s32 play_level_timer_get(struct Play* self);
static s32 play_spawn_timer_get(struct Play* self);
static void play_color_collide_delete_update(struct Play* self, struct CColorCollideDelete* colorCollideDelete);
static void play_damage_update(struct Play* self);
static void play_death_update(struct Play* self);
static void play_enemy_add(struct Play* self);
static void play_enemy_clear(struct Play* self, bool isParticle);
static void play_level_increment(struct Play* self);
static void play_score_set(struct Play* self, enum CColorType type);
static void play_score_update(struct Play* self);
static void play_spawn_update(struct Play* self);
static void play_start_update(struct Play* self);
/* Updates score graphic. */
static void
play_score_set(struct Play* self, enum CColorType type)
{
struct CScale* scale;
struct CColorChange* colorChange;
struct CText* text;
char scoreBuffer[PLAY_SCORE_BUFFER_SIZE];
memset(scoreBuffer, '\0', sizeof(char) * PLAY_SCORE_BUFFER_SIZE);
scale = ecs_get(self->scoreID, ECS_C_SCALE);
colorChange = ecs_get(self->scoreID, ECS_C_COLOR_CHANGE);
text = ecs_get(self->scoreID, ECS_C_TEXT);
snprintf(scoreBuffer, sizeof(char) * PLAY_SCORE_BUFFER_SIZE, "%u", self->score);
c_text_string_set(text, (char*)scoreBuffer, strlen(scoreBuffer));
c_scale_set
(
scale,
PLAY_SCORE_SCALE_BASE,
PLAY_SCORE_SCALE_VALUE,
PLAY_SCORE_SCALE_TIMER,
PLAY_SCORE_SCALE_TYPE
);
c_color_change_set
(
colorChange,
text->color,
C_COLOR_VALUES[type],
PLAY_SCORE_COLOR_CHANGE_TIMER,
PLAY_SCORE_COLOR_CHANGE_TYPE
);
}
/* What happens when a follower collides with an enemy? */
static void
play_color_collide_delete_update(struct Play* self, struct CColorCollideDelete* colorCollideDelete)
{
struct CColor* color;
enum CColorType type;
color = ecs_get(colorCollideDelete->id, ECS_C_COLOR);
type = color->type;
for (s32 i = 0; i < (s32)self->enemies.count; i++)
{
u32* id;
id = (u32*)vector_get(&self->enemies, i);
if (*id == colorCollideDelete->collideID)
vector_remove_from_data(&self->enemies, id);
}
self->score += self->level;
play_score_set(self, type);
}
/* Increment the level. */
static void
play_level_increment(struct Play* self)
{
struct CHealth* playerHealth;
struct CText* text;
char levelBuffer[PLAY_LEVEL_BUFFER_SIZE];
vec4 color;
memset(levelBuffer, '\0', sizeof(char) * PLAY_LEVEL_BUFFER_SIZE);
playerHealth = ecs_get(self->playerID, ECS_C_HEALTH);
text = ecs_get(self->levelID, ECS_C_TEXT);
if (playerHealth->isDead)
return;
if (self->level > 0)
sound_play(&game.sounds[SOUND_LEVEL], -1);
self->level++;
if (self->level <= C_COLOR_COUNT)
player_follower_add(self->playerID);
self->levelTimer = play_level_timer_get(self);
if (!text)
return;
snprintf(levelBuffer, sizeof(char) * PLAY_LEVEL_BUFFER_SIZE, "x%u", self->level);
c_text_string_set(text, (char*)levelBuffer, strlen(levelBuffer));
}
/* Updates play start state. */
static void
play_start_update(struct Play* self)
{
self->startTimer--;
if (self->startTimer <= 0)
{
self->isStart = false;
self->isSpawn = true;
}
}
/* Get level up timer. */
static s32
play_level_timer_get(struct Play* self)
{
return PLAY_LEVEL_TIMER_BASE;
}
/* Update play score. */
static void
play_score_update(struct Play* self)
{
/* Check each of the player's followers, did they collide with an enemy? */
struct COrbit* orbit;
orbit = ecs_get(self->playerID, ECS_C_ORBIT);
for (s32 i = 0; i < (s32)orbit->objects.count; i++)
{
struct CColorCollideDelete* colorCollideDelete;
u32 id;
id = *(u32*)vector_get(&orbit->objects, i);
colorCollideDelete = ecs_get(id, ECS_C_COLOR_COLLIDE_DELETE);
if (!colorCollideDelete)
continue;
if (colorCollideDelete->isCollided)
play_color_collide_delete_update(self, colorCollideDelete);
}
}
/* Update play level. */
static void
play_level_update(struct Play* self)
{
self->levelTimer--;
if (self->levelTimer <= 0)
play_level_increment(self);
}
/* Gets current spawn timer. */
static s32
play_spawn_timer_get(struct Play* self)
{
s32 timer;
timer = PLAY_SPAWN_TIMER_BASE;
timer -= (s32)(self->level * PLAY_LEVEL_SPAWN_TIMER_MULTIPLIER);
timer *= RANDOM_F32(PLAY_SPAWN_TIMER_MULTIPLIER_MIN, PLAY_SPAWN_TIMER_MULTIPLIER_MAX);
timer = MIN(timer, PLAY_SPAWN_TIMER_MIN);
return timer;
}
/* Updates spawn timer. */
static void
play_spawn_update(struct Play* self)
{
struct CStun* stun;
stun = ecs_get(self->playerID, ECS_C_STUN);
if (stun->isStun)
{
self->spawnTimer = PLAY_SPAWN_TIMER_STUN_TIME;
return;
}
self->spawnTimer--;
if (self->spawnTimer <= 0)
{
play_enemy_add(self);
self->spawnTimer = play_spawn_timer_get(self);
}
}
/* Updates damage; if player is damaged, remove all enemies. */
static void
play_damage_update(struct Play* self)
{
struct CDamage* damage;
struct CHealth* health;
damage = ecs_get(self->playerID, ECS_C_DAMAGE);
health = ecs_get(self->playerID, ECS_C_HEALTH);
if (damage->isDamageUpdate && !health->isDead)
play_enemy_clear(self, true);
}
/* Adds an enemy. */
static void
play_enemy_add(struct Play* self)
{
u32 enemyID;
u32 warningID;
vec3 position;
vec3 warningPosition;
enum CColorType color;
enum Direction direction;
u32 colorMax;
f32 speed;
enemyID = entity_add();
warningID = entity_add();
direction = (enum Direction)RANDOM_S32(0, DIRECTION_2D_COUNT - 1);
glm_vec3_zero(position);
glm_vec3_zero(warningPosition);
switch (direction)
{
case LEFT:
position[0] = game.bounds[0] - ENEMY_SIZE[0];
position[1] = RANDOM_F32(game.bounds[1] + ENEMY_SIZE[1], game.bounds[3] - ENEMY_SIZE[1]);
warningPosition[0] = game.bounds[0] + WARNING_SIZE[0];
warningPosition[1] = position[1];
break;
case RIGHT:
position[0] = game.bounds[2] + ENEMY_SIZE[0];
position[1] = RANDOM_F32(game.bounds[1] + ENEMY_SIZE[1], game.bounds[3] - ENEMY_SIZE[1]);
warningPosition[0] = game.bounds[2] - WARNING_SIZE[0];
warningPosition[1] = position[1];
break;
case UP:
position[0] = RANDOM_F32(game.bounds[0] + ENEMY_SIZE[0], game.bounds[2] - ENEMY_SIZE[0]);
position[1] = game.bounds[1] - ENEMY_SIZE[0];
warningPosition[0] = position[0];
warningPosition[1] = game.bounds[1] + WARNING_SIZE[0];
break;
case DOWN:
position[0] = RANDOM_F32(game.bounds[0] + ENEMY_SIZE[0], game.bounds[2] - ENEMY_SIZE[0]);
position[1] = game.bounds[3] + ENEMY_SIZE[0];
warningPosition[0] = position[0];
warningPosition[1] = game.bounds[3] - WARNING_SIZE[0];
break;
default:
break;
}
colorMax = self->level - 1;
colorMax = MAX(colorMax, C_COLOR_COUNT - 1);
color = (enum CColorType)RANDOM_S32(0, colorMax);
speed = PLAY_ENEMY_SPEED + ((self->level - 1) * PLAY_ENEMY_SPEED_LEVEL_MULTIPLIER);
enemy_init(enemyID, self->playerID, color, position, speed);
warning_init(warningID, warningPosition, color);
sound_play(&game.sounds[SOUND_WARNING], -1);
vector_push(&self->enemies, &enemyID);
}
/* Removes all enemies. */
static void
play_enemy_clear(struct Play* self, bool isSpawnParticle)
{
for (s32 i = 0; i < (s32)self->enemies.count; i++)
{
u32 id;
id = *(u32*)vector_get(&self->enemies, i);
if (isSpawnParticle)
{
struct CParticleSpawn* particleSpawn;
struct CPhysics* physics;
vec3 position;
particleSpawn = ecs_get(id, ECS_C_PARTICLE_SPAWN);
physics = ecs_get(id, ECS_C_PHYSICS);
if (!particleSpawn || !physics)
continue;
glm_vec3_copy(physics->position, position);
c_particle_spawn(particleSpawn, position);
}
entity_delete(id);
}
}
/* Check if the player's death animation has ended. */
static void
play_death_update(struct Play* self)
{
struct CPlayerDeath* playerDeath;
playerDeath = ecs_get(self->playerID, ECS_C_PLAYER_DEATH);
if (playerDeath->state == C_PLAYER_DEATH_END)
self->isOver = true;
}
/* Initialize play. */
void
play_init(struct Play* self)
{
struct CColorChange* messageColorChange;
memset(self, '\0', sizeof(struct Play));
self->playerID = entity_add();
self->scoreID = entity_add();
self->levelID = entity_add();
self->messageID = entity_add();
player_init(self->playerID, PLAY_PLAYER_POSITION);
text_init
(
self->scoreID,
PLAY_SCORE_ORIGIN,
game.fonts[PLAY_SCORE_FONT],
PLAY_SCORE_STRING,
strlen(PLAY_SCORE_STRING),
PLAY_SCORE_WRAP,
PLAY_SCORE_COLOR,
PLAY_SCORE_POSITION
);
play_score_set(self, C_COLOR_GREEN);
text_init
(
self->levelID,
PLAY_LEVEL_ORIGIN,
game.fonts[PLAY_LEVEL_FONT],
PLAY_LEVEL_STRING,
strlen(PLAY_LEVEL_STRING),
PLAY_LEVEL_WRAP,
PLAY_LEVEL_COLOR,
PLAY_LEVEL_POSITION
);
play_level_increment(self);
text_init
(
self->messageID,
PLAY_MESSAGE_ORIGIN,
game.fonts[PLAY_MESSAGE_FONT],
PLAY_MESSAGE_STRING,
strlen(PLAY_MESSAGE_STRING),
PLAY_MESSAGE_WRAP,
PLAY_MESSAGE_COLOR,
PLAY_MESSAGE_POSITION
);
messageColorChange = ecs_get(self->messageID, ECS_C_COLOR_CHANGE);
c_color_change_set
(
messageColorChange,
C_COLOR_VALUES[0],
COLOR_TRANSPARENT,
PLAY_START_TIMER_DEFAULT,
C_COLOR_CHANGE_TO_STICK
);
vector_init(&self->enemies, sizeof(u32), PLAY_ENEMY_LIMIT);
self->spawnTimer = PLAY_SPAWN_TIMER_DEFAULT;
self->startTimer = PLAY_START_TIMER_DEFAULT;
self->isSpawn = false;
self->isStart = true;
music_play(&game.music[MUSIC_ETHNICA], true);
}
/* Frees play. */
void
play_free(struct Play* self)
{
play_enemy_clear(self, false);
entity_delete(self->playerID);
entity_delete(self->scoreID);
entity_delete(self->levelID);
entity_delete(self->messageID);
}
/* Update play. */
void
play_update_f(struct Play* self)
{
if (keyboard_press(&game.keyboard, KEYBOARD_P))
game.isPaused = !game.isPaused;
if (game.isPaused)
return;
if (self->isStart)
play_start_update(self);
if (self->isSpawn)
play_spawn_update(self);
play_damage_update(self);
play_score_update(self);
play_level_update(self);
play_death_update(self);
}