Files
snivy/src/state.cpp

441 lines
13 KiB
C++

#include "state.h"
#include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_sdl3.h>
#include <imgui.h>
#include "util/math_.h"
using namespace glm;
using namespace game::resource;
using namespace game::util;
namespace game
{
constexpr auto TICK_RATE = 30;
constexpr auto TICK_INTERVAL = (1000 / TICK_RATE);
constexpr auto UPDATE_RATE = 60;
constexpr auto UPDATE_INTERVAL = (1000 / UPDATE_RATE);
State::State(SDL_Window* inWindow, SDL_GLContext inContext, vec2 size)
: window(inWindow), context(inContext), canvas(size, true)
{
}
void State::tick()
{
for (auto& item : items)
item.tick();
character.tick();
if (character.isJustDigestionStart)
resources.sound_play(audio::GURGLES[(int)math::random_roll(std::size(audio::GURGLES))]);
if (character.is_event("Burp")) resources.sound_play(audio::BURPS[(int)math::random_roll(std::size(audio::BURPS))]);
if (character.isJustDigestionEnd && !character.isJustStageUp)
{
character.state_set(Character::PAT, true);
textWindow.set_random(resources.dialogue.postDigestIDs, resources, character);
}
cursor.tick();
textWindow.tick(resources, character);
}
void State::update()
{
static bool isRubbing{};
auto& inventory = mainMenuWindow.inventory;
auto& dialogue = resources.dialogue;
int width{};
int height{};
SDL_GetWindowSize(window, &width, &height);
SDL_Event event;
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL3_ProcessEvent(&event);
if (event.type == SDL_EVENT_QUIT) isRunning = false;
}
if (!isRunning) return;
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
auto style = ImGui::GetStyle();
if (textWindow.isFlagActivated)
{
switch (textWindow.flag)
{
case Dialogue::Entry::ACTIVATE_WINDOWS:
isInfo = true;
isMainMenu = true;
break;
case Dialogue::Entry::DEACTIVATE_WINDOWS:
isInfo = false;
isMainMenu = false;
break;
case Dialogue::Entry::ONLY_INFO:
isInfo = true;
isMainMenu = false;
break;
case Dialogue::Entry::ACTIVATE_CHEATS:
mainMenuWindow.isCheats = true;
break;
default:
break;
}
}
auto infoSize = ImVec2(width * 0.5f, ImGui::GetTextLineHeightWithSpacing() * 3.5);
auto infoPos = style.WindowPadding;
if (isInfo) infoWindow.update(resources, gameData, character, infoSize, infoPos);
auto textSize = ImVec2(width - style.WindowPadding.x * 2, ImGui::GetTextLineHeightWithSpacing() * 8);
auto textPos = ImVec2(style.WindowPadding.x, height - textSize.y - style.WindowPadding.y);
if (isText) textWindow.update(resources, character, textSize, textPos);
auto mainSize =
ImVec2((width * 0.5f) - style.WindowPadding.x * 3, height - textSize.y - (style.WindowPadding.y * 3));
auto mainPos = ImVec2(infoPos.x + infoSize.x + style.WindowPadding.x, style.WindowPadding.y);
if (isMainMenu) mainMenuWindow.update(resources, character, gameData, textWindow, mainSize, mainPos);
/* Inventory */
if (inventory.isQueued)
{
if (items.size() > Item::DEPLOYED_MAX)
{
inventory.isQueued = false;
inventory.queuedItemType = Item::NONE;
resources.sound_play(audio::MISS);
}
else
{
auto& type = inventory.queuedItemType;
auto position = glm::vec2(((Item::SPAWN_X_MAX - Item::SPAWN_X_MIN) * math::random()) + Item::SPAWN_X_MIN,
((Item::SPAWN_Y_MAX - Item::SPAWN_Y_MIN) * math::random()) + Item::SPAWN_Y_MIN);
items.emplace_back(&resources.anm2s[anm2::ITEMS], position, type);
inventory.values[type]--;
if (inventory.values[type] == 0) inventory.values.erase(type);
type = Item::NONE;
inventory.isQueued = false;
}
}
/* Item */
Item::hoveredItem = nullptr;
Item::heldItemPrevious = Item::heldItem;
bool isSpeak{};
std::vector<int>* dialogueIDs{};
for (int i = 0; i < items.size(); i++)
{
auto& item = items[i];
item.update(resources);
if (&item == Item::heldItem && &item != &items.back())
{
std::swap(items[i], items.back());
Item::heldItem = &items.back();
continue;
}
if (item.isToBeDeleted)
{
items.erase(items.begin() + i--);
continue;
}
if (Item::queuedReturnItem == &item)
{
if (Item::queuedReturnItem->state == Item::DEFAULT)
{
inventory.values[item.type]++;
resources.sound_play(audio::RETURN);
}
else
resources.sound_play(audio::DISPOSE);
items.erase(items.begin() + i--);
Item::queuedReturnItem = nullptr;
continue;
}
}
auto& item = Item::heldItem;
auto isHeldItemChanged = item != Item::heldItemPrevious;
if (item && character.state != Character::STAGE_UP)
{
auto& type = item->type;
auto& calories = Item::CALORIES[type];
auto& category = Item::CATEGORIES[type];
auto& position = item->position;
auto caloriesChew = Item::CALORIES[type] / (Item::CHEW_COUNT_MAX + 1);
auto digestionRateBonusChew = Item::DIGESTION_RATE_BONUSES[type] / (Item::CHEW_COUNT_MAX + 1);
auto eatSpeedBonusChew = Item::EAT_SPEED_BONUSES[type] / (Item::CHEW_COUNT_MAX + 1);
auto isAbleToEat = character.calories + caloriesChew <= character.max_capacity();
if (category == Item::FOOD)
{
auto isByMouth = math::is_point_in_rectf(character.mouth_rect_get(), position);
if (character.state == Character::EAT && !isHeldItemChanged)
{
if (!isByMouth)
{
if (character.is_over_capacity())
{
dialogueIDs = &dialogue.foodEasedIDs;
character.state_set(Character::IDLE);
}
else
{
dialogueIDs = &dialogue.foodStolenIDs;
character.state_set(Character::ANGRY);
}
isSpeak = true;
}
if (character.is_event(Character::EVENT_EAT))
{
item->chewCount++;
if (digestionRateBonusChew > 0 || digestionRateBonusChew < 0)
{
character.digestionRate =
glm::clamp(Character::DIGESTION_RATE_MIN, character.digestionRate + digestionRateBonusChew,
Character::DIGESTION_RATE_MAX);
}
if (eatSpeedBonusChew > 0)
{
character.eatSpeedMultiplier =
glm::clamp(Character::EAT_SPEED_MULTIPLIER_MIN, character.eatSpeedMultiplier + eatSpeedBonusChew,
Character::EAT_SPEED_MULTIPLIER_MAX);
}
character.calories += caloriesChew;
character.totalCaloriesConsumed += caloriesChew;
character.consume_played_event();
if (item->chewCount > Item::CHEW_COUNT_MAX)
{
character.isFinishedFood = true;
character.foodItemsEaten++;
item->isToBeDeleted = true;
Item::heldItem = nullptr;
cursor.play(Cursor::ANIMATION_DEFAULT);
}
else
item->state_set(item->chewCount == 1 ? Item::CHEW_1
: item->chewCount == 2 ? Item::CHEW_2
: Item::DEFAULT);
if (character.calories + caloriesChew >= character.max_capacity() && Item::heldItem)
{
character.state_set(Character::SHOCKED);
dialogueIDs = &dialogue.fullIDs;
isSpeak = true;
}
}
}
else if (character.state == Character::ANGRY)
{
}
else
{
cursor.play(Cursor::ANIMATION_GRAB);
if (!isAbleToEat)
{
character.state_set(Character::SHOCKED);
if (caloriesChew > character.max_capacity())
dialogueIDs = &dialogue.capacityLowIDs;
else
dialogueIDs = &dialogue.fullIDs;
}
else if (character.is_over_capacity())
dialogueIDs = &dialogue.feedFullIDs;
else
{
character.state_set(Character::EAGER);
dialogueIDs = &dialogue.feedHungryIDs;
}
if (isHeldItemChanged) isSpeak = true;
if (isAbleToEat && isByMouth)
if (character.state != Character::EAT) character.state_set(Character::EAT);
isRubbing = false;
}
}
}
else if (isHeldItemChanged && character.state != Character::ANGRY && character.state != Character::STAGE_UP)
character.state_set(Character::IDLE);
/* Character */
if (character.isFinishedFood && character.state == Character::IDLE)
{
if (!character.is_over_capacity()) dialogueIDs = &dialogue.eatHungryIDs;
if (math::random_percent_roll(Character::BURP_BIG_CHANCE))
{
character.state_set(Character::BURP_BIG);
dialogueIDs = &dialogue.burpBigIDs;
}
else if (math::random_percent_roll(Character::BURP_SMALL_CHANCE))
{
character.state_set(Character::BURP_SMALL);
dialogueIDs = &dialogue.burpSmallIDs;
}
else if (!character.is_over_capacity() && math::random_percent_roll(Character::PAT_CHANCE))
character.state_set(Character::PAT);
character.isFinishedFood = false;
isSpeak = true;
}
/* Character */
if (character.isJustAppeared)
{
textWindow.set(dialogue.get("Start"), character);
isText = true;
character.isJustAppeared = false;
}
if (character.isJustFinalThreshold && !character.isFinalThresholdReached)
{
Item::heldItem = nullptr;
Item::heldItemPrevious = nullptr;
textWindow.set(dialogue.get("End"), character);
items.clear();
character.isFinalThresholdReached = true;
}
/* Dialogue */
if (isSpeak && dialogueIDs) textWindow.set_random(*dialogueIDs, resources, character);
/* Rubbing/Grabbing */
bool isHeadRubPossible = math::is_point_in_rectf(character.head_rect_get(), cursor.position) && !Item::heldItem;
bool isBellyRubPossible = math::is_point_in_rectf(character.belly_rect_get(), cursor.position) && !Item::heldItem;
bool isTailRubPossible = math::is_point_in_rectf(character.tail_rect_get(), cursor.position) && !Item::heldItem;
auto isRubPossible = isHeadRubPossible || isBellyRubPossible || isTailRubPossible;
if (isRubPossible)
{
if (!isRubbing) cursor.play(Cursor::ANIMATION_HOVER);
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
{
isRubbing = true;
resources.sound_play(audio::RUB);
if (isHeadRubPossible)
{
character.state_set(Character::HEAD_RUB);
cursor.play(Cursor::ANIMATION_RUB);
}
else if (isBellyRubPossible)
{
character.state_set(Character::BELLY_RUB);
cursor.play(Cursor::ANIMATION_GRAB);
}
else if (isTailRubPossible)
{
character.state_set(Character::TAIL_RUB);
cursor.play(Cursor::ANIMATION_GRAB);
}
}
}
else if (Item::hoveredItem)
cursor.play(Cursor::ANIMATION_HOVER);
else if (!Item::heldItem)
cursor.play(Cursor::ANIMATION_DEFAULT);
if (isRubbing)
{
if (isBellyRubPossible && !character.isDigesting)
{
auto delta = ImGui::GetIO().MouseDelta;
auto power = fabs(delta.x) + fabs(delta.y);
if (character.calories > 0) character.digestionProgress += power * Character::DIGESTION_RUB_BONUS;
}
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) || !isRubPossible)
{
isRubbing = false;
character.state_set(Character::IDLE);
}
}
SDL_HideCursor();
cursor.update();
}
void State::render()
{
SDL_GL_MakeCurrent(window, context);
int width{};
int height{};
SDL_GetWindowSize(window, &width, &height);
auto& textureShader = resources.shaders[shader::TEXTURE];
auto& rectShader = resources.shaders[shader::RECT];
canvas.bind();
canvas.clear(glm::vec4(0, 0, 0, 1));
auto bgModel = math::quad_model_get(vec2(width, height));
canvas.texture_render(textureShader, resources.textures[texture::BG].id, bgModel);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
character.render(textureShader, rectShader, canvas);
for (auto& item : items)
item.render(textureShader, rectShader, canvas);
cursor.render(textureShader, rectShader, canvas);
canvas.unbind();
SDL_GL_SwapWindow(window);
}
void State::loop()
{
auto currentTick = SDL_GetTicks();
auto currentUpdate = SDL_GetTicks();
if (currentUpdate - previousUpdate >= UPDATE_INTERVAL)
{
update();
render();
previousUpdate = currentUpdate;
}
if (currentTick - previousTick >= TICK_INTERVAL)
{
tick();
previousTick = currentTick;
}
SDL_Delay(1);
}
}