The Snivy Video Game Is Complete
This commit is contained in:
375
src/state.cpp
375
src/state.cpp
@@ -4,14 +4,17 @@
|
||||
#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 = 120;
|
||||
constexpr auto UPDATE_RATE = 60;
|
||||
constexpr auto UPDATE_INTERVAL = (1000 / UPDATE_RATE);
|
||||
|
||||
State::State(SDL_Window* inWindow, SDL_GLContext inContext, vec2 size)
|
||||
@@ -19,10 +22,37 @@ namespace game
|
||||
{
|
||||
}
|
||||
|
||||
void State::tick() { actor.tick(); }
|
||||
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))
|
||||
@@ -36,15 +66,322 @@ namespace game
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::Begin("Metrics");
|
||||
ImGui::Text("Time: %f", actor.time);
|
||||
ImGui::Text("IS Playing: %s", actor.isPlaying ? "true" : "false");
|
||||
auto style = ImGui::GetStyle();
|
||||
|
||||
auto animation = actor.animation_get();
|
||||
ImGui::Text("Animation: %s", animation ? animation->name.c_str() : "null");
|
||||
ImGui::End();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
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()
|
||||
@@ -55,12 +392,28 @@ namespace game
|
||||
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));
|
||||
actor.render(resources.shaders[shader::TEXTURE], canvas);
|
||||
|
||||
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();
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user