441 lines
13 KiB
C++
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);
|
|
}
|
|
}
|