Inventory updates, API updates, lots of file renaming

This commit is contained in:
2026-03-17 04:05:04 -04:00
parent b060784bb7
commit fb6f902f28
63 changed files with 820 additions and 553 deletions

View File

@@ -109,10 +109,12 @@ set (TINYXML2_SOURCES ${TINYXML2_DIR}/tinyxml2.cpp)
file(GLOB PROJECT_SRC CONFIGURE_DEPENDS file(GLOB PROJECT_SRC CONFIGURE_DEPENDS
include/*.cpp include/*.cpp
src/*.cpp src/*.cpp
src/render/*.cpp
src/resource/*.cpp src/resource/*.cpp
src/resource/xml/*.cpp src/resource/xml/*.cpp
src/state/*.cpp src/state/*.cpp
src/state/main/*.cpp src/state/play/*.cpp
src/state/play/arcade/*.cpp
src/state/select/*.cpp src/state/select/*.cpp
src/entity/*.cpp src/entity/*.cpp
src/window/*.cpp src/window/*.cpp

View File

@@ -2,7 +2,7 @@
#include <unordered_set> #include <unordered_set>
#include "../canvas.hpp" #include "../render/canvas.hpp"
#include "../resource/xml/anm2.hpp" #include "../resource/xml/anm2.hpp"
namespace game::entity namespace game::entity

View File

@@ -335,12 +335,14 @@ namespace game::entity
void Character::queue_play(QueuedPlay play) void Character::queue_play(QueuedPlay play)
{ {
if (isStageUp) return;
queuedPlay = play; queuedPlay = play;
queuedPlay.animation = animation_name_convert(queuedPlay.animation); queuedPlay.animation = animation_name_convert(queuedPlay.animation);
} }
void Character::queue_idle_animation() void Character::queue_idle_animation()
{ {
if (isStageUp) return;
if (data.animations.idle.empty()) return; if (data.animations.idle.empty()) return;
queue_play( queue_play(
{is_over_capacity() && !data.animations.idleFull.empty() ? data.animations.idleFull : data.animations.idle}); {is_over_capacity() && !data.animations.idleFull.empty() ? data.animations.idleFull : data.animations.idle});
@@ -348,6 +350,7 @@ namespace game::entity
void Character::queue_interact_area_animation(resource::xml::Character::InteractArea& interactArea) void Character::queue_interact_area_animation(resource::xml::Character::InteractArea& interactArea)
{ {
if (isStageUp) return;
if (interactArea.animation.empty()) return; if (interactArea.animation.empty()) return;
queue_play({is_over_capacity() && !interactArea.animationFull.empty() ? interactArea.animationFull queue_play({is_over_capacity() && !interactArea.animationFull.empty() ? interactArea.animationFull
: interactArea.animation}); : interactArea.animation});

View File

@@ -47,6 +47,7 @@ namespace game::entity
std::vector<int> animationBlinkDurations{}; std::vector<int> animationBlinkDurations{};
bool isStageUp{}; bool isStageUp{};
bool isStageUpDuring{};
bool isJustStageUp{}; bool isJustStageUp{};
bool isJustStageFinal{}; bool isJustStageFinal{};

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include "../util/interact_type.hpp"
#include "actor.hpp" #include "actor.hpp"
namespace game::entity namespace game::entity
@@ -16,11 +15,11 @@ namespace game::entity
}; };
State state{DEFAULT}; State state{DEFAULT};
InteractType mode{InteractType::RUB}; int interactTypeID{-1};
Cursor() = default; Cursor() = default;
Cursor(resource::xml::Anm2&); Cursor(resource::xml::Anm2&);
void tick(); void tick();
void update(); void update();
}; };
} }

View File

@@ -3,8 +3,8 @@
#include <utility> #include <utility>
#include "util/imgui.hpp" #include "../util/imgui.hpp"
#include "util/math.hpp" #include "../util/math.hpp"
using namespace glm; using namespace glm;
using namespace game::resource; using namespace game::resource;

View File

@@ -6,7 +6,7 @@
#include <glad/glad.h> #include <glad/glad.h>
#endif #endif
#include "resource/shader.hpp" #include "../resource/shader.hpp"
#include <glm/ext/matrix_clip_space.hpp> #include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <glm/glm.hpp> #include <glm/glm.hpp>

View File

@@ -41,6 +41,8 @@ namespace game::resource::xml
query_anm2(root, "Anm2", archive, textureRootPath, anm2); query_anm2(root, "Anm2", archive, textureRootPath, anm2);
query_string_attribute(root, "Name", &name); query_string_attribute(root, "Name", &name);
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
root->QueryFloatAttribute("Weight", &weight); root->QueryFloatAttribute("Weight", &weight);
root->QueryFloatAttribute("Capacity", &capacity); root->QueryFloatAttribute("Capacity", &capacity);
@@ -159,6 +161,15 @@ namespace game::resource::xml
if (auto element = root->FirstChildElement("InteractAreas")) if (auto element = root->FirstChildElement("InteractAreas"))
{ {
auto interact_type_id_get = [&](const std::string& typeName)
{
for (int i = 0; i < (int)interactTypeNames.size(); i++)
if (interactTypeNames[i] == typeName) return i;
interactTypeNames.emplace_back(typeName);
return (int)interactTypeNames.size() - 1;
};
for (auto child = element->FirstChildElement("InteractArea"); child; for (auto child = element->FirstChildElement("InteractArea"); child;
child = child->NextSiblingElement("InteractArea")) child = child->NextSiblingElement("InteractArea"))
{ {
@@ -173,6 +184,7 @@ namespace game::resource::xml
query_string_attribute(child, "AnimationCursorActive", &interactArea.animationCursorActive); query_string_attribute(child, "AnimationCursorActive", &interactArea.animationCursorActive);
query_sound_entry_collection(child, "Sound", archive, soundRootPath, interactArea.sound, "Path"); query_sound_entry_collection(child, "Sound", archive, soundRootPath, interactArea.sound, "Path");
dialogue.query_pool_id(child, "DialoguePoolID", interactArea.pool.id); dialogue.query_pool_id(child, "DialoguePoolID", interactArea.pool.id);
query_bool_attribute(child, "IsHold", &interactArea.isHold);
child->QueryFloatAttribute("DigestionBonusRub", &interactArea.digestionBonusRub); child->QueryFloatAttribute("DigestionBonusRub", &interactArea.digestionBonusRub);
child->QueryFloatAttribute("DigestionBonusClick", &interactArea.digestionBonusClick); child->QueryFloatAttribute("DigestionBonusClick", &interactArea.digestionBonusClick);
child->QueryFloatAttribute("Time", &interactArea.time); child->QueryFloatAttribute("Time", &interactArea.time);
@@ -181,9 +193,7 @@ namespace game::resource::xml
std::string typeString{}; std::string typeString{};
query_string_attribute(child, "Type", &typeString); query_string_attribute(child, "Type", &typeString);
if (!typeString.empty()) interactArea.typeID = interact_type_id_get(typeString);
for (int i = 0; i < (int)std::size(INTERACT_TYPE_STRINGS); i++)
if (typeString == INTERACT_TYPE_STRINGS[i]) interactArea.type = (InteractType)i;
interactAreas.emplace_back(std::move(interactArea)); interactAreas.emplace_back(std::move(interactArea));
} }
@@ -210,10 +220,10 @@ namespace game::resource::xml
else else
logger.warning(std::format("No character cursor.xml file found: {}", path.string())); logger.warning(std::format("No character cursor.xml file found: {}", path.string()));
if (auto playSchemaPath = physfs::Path(archive + "/" + "play.xml"); playSchemaPath.is_valid()) if (auto skillCheckSchemaPath = physfs::Path(archive + "/" + "skill_check.xml"); skillCheckSchemaPath.is_valid())
playSchema = Play(playSchemaPath, dialogue); skillCheckSchema = SkillCheck(skillCheckSchemaPath, dialogue);
else else
logger.warning(std::format("No character play.xml file found: {}", path.string())); logger.warning(std::format("No character skill_check.xml file found: {}", path.string()));
logger.info(std::format("Initialized character: {}", name)); logger.info(std::format("Initialized character: {}", name));

View File

@@ -3,7 +3,6 @@
#include <filesystem> #include <filesystem>
#include <vector> #include <vector>
#include "../../util/interact_type.hpp"
#include "../audio.hpp" #include "../audio.hpp"
#include "animation_entry.hpp" #include "animation_entry.hpp"
#include "anm2.hpp" #include "anm2.hpp"
@@ -12,7 +11,7 @@
#include "dialogue.hpp" #include "dialogue.hpp"
#include "item.hpp" #include "item.hpp"
#include "menu.hpp" #include "menu.hpp"
#include "play.hpp" #include "skill_check.hpp"
#include "save.hpp" #include "save.hpp"
namespace game::resource::xml namespace game::resource::xml
@@ -51,7 +50,8 @@ namespace game::resource::xml
int nullID{-1}; int nullID{-1};
int layerID{-1}; int layerID{-1};
InteractType type{(InteractType)-1}; int typeID{-1};
bool isHold{};
Dialogue::PoolReference pool{-1}; Dialogue::PoolReference pool{-1};
float digestionBonusRub{}; float digestionBonusRub{};
@@ -97,7 +97,7 @@ namespace game::resource::xml
Item itemSchema{}; Item itemSchema{};
Menu menuSchema{}; Menu menuSchema{};
Cursor cursorSchema{}; Cursor cursorSchema{};
Play playSchema{}; SkillCheck skillCheckSchema{};
Save save{}; Save save{};
@@ -107,9 +107,12 @@ namespace game::resource::xml
Sounds sounds{}; Sounds sounds{};
glm::vec3 color{0.120f, 0.515f, 0.115f};
std::vector<Stage> stages{}; std::vector<Stage> stages{};
std::vector<ExpandArea> expandAreas{}; std::vector<ExpandArea> expandAreas{};
std::vector<EatArea> eatAreas{}; std::vector<EatArea> eatAreas{};
std::vector<std::string> interactTypeNames{};
std::vector<InteractArea> interactAreas{}; std::vector<InteractArea> interactAreas{};
AlternateSpritesheet alternateSpritesheet{}; AlternateSpritesheet alternateSpritesheet{};

View File

@@ -42,6 +42,7 @@ namespace game::resource::xml
query_string_attribute(root, "Name", &name); query_string_attribute(root, "Name", &name);
query_string_attribute(root, "Description", &description); query_string_attribute(root, "Description", &description);
query_string_attribute(root, "Author", &author); query_string_attribute(root, "Author", &author);
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
root->QueryFloatAttribute("Weight", &weight); root->QueryFloatAttribute("Weight", &weight);
if (auto element = root->FirstChildElement("Stages")) if (auto element = root->FirstChildElement("Stages"))

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include <glm/glm.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -22,6 +23,7 @@ namespace game::resource::xml
Texture portrait{}; Texture portrait{};
Texture render{}; Texture render{};
Save save{}; Save save{};
glm::vec3 color{0.120f, 0.515f, 0.115f};
int stages{1}; int stages{1};

View File

@@ -142,7 +142,7 @@ namespace game::resource::xml
query_int_optional_attribute(child, "UpgradeCount", item.upgradeCount); query_int_optional_attribute(child, "UpgradeCount", item.upgradeCount);
} }
query_bool_attribute(child, "IsPlayReward", &item.isPlayReward); query_bool_attribute(child, "IsSkillCheckReward", &item.isSkillCheckReward);
query_bool_attribute(child, "IsToggleSpritesheet", &item.isToggleSpritesheet); query_bool_attribute(child, "IsToggleSpritesheet", &item.isToggleSpritesheet);
std::string categoryString{}; std::string categoryString{};
@@ -173,7 +173,7 @@ namespace game::resource::xml
{ {
auto& item = items[i]; auto& item = items[i];
pools[item.rarityID].emplace_back(i); pools[item.rarityID].emplace_back(i);
if (item.isPlayReward) rewardItemPool.emplace_back(i); if (item.isSkillCheckReward) skillCheckRewardItemPool.emplace_back(i);
} }
for (int i = 0; i < (int)rarities.size(); i++) for (int i = 0; i < (int)rarities.size(); i++)

View File

@@ -50,7 +50,7 @@ namespace game::resource::xml
std::optional<float> digestionBonus{}; std::optional<float> digestionBonus{};
std::optional<float> gravity{}; std::optional<float> gravity{};
std::optional<int> chewCount{}; std::optional<int> chewCount{};
bool isPlayReward{}; bool isSkillCheckReward{};
bool isToggleSpritesheet{}; bool isToggleSpritesheet{};
}; };
@@ -85,7 +85,7 @@ namespace game::resource::xml
std::vector<int> rarityIDsSortedByChance{}; std::vector<int> rarityIDsSortedByChance{};
std::unordered_map<int, Pool> pools{}; std::unordered_map<int, Pool> pools{};
Pool rewardItemPool{}; Pool skillCheckRewardItemPool{};
Animations animations{}; Animations animations{};
Sounds sounds{}; Sounds sounds{};

View File

@@ -22,15 +22,13 @@ namespace game::resource::xml
XMLDocument document; XMLDocument document;
auto pathString = path.string(); auto pathString = path.string();
// Fail silently if there's no save.
auto result = document.LoadFile(pathString.c_str()); auto result = document.LoadFile(pathString.c_str());
if (result == XML_ERROR_FILE_NOT_FOUND || result == XML_ERROR_FILE_COULD_NOT_BE_OPENED) return; if (result == XML_ERROR_FILE_NOT_FOUND || result == XML_ERROR_FILE_COULD_NOT_BE_OPENED) return;
if (result != XML_SUCCESS) if (result != XML_SUCCESS)
{ {
logger.error( logger.error(std::format("Could not initialize character save file: {} ({})", pathString, document.ErrorStr()));
std::format("Could not initialize character save file: {} ({})", pathString, document.ErrorStr()));
return; return;
} }
@@ -54,7 +52,9 @@ namespace game::resource::xml
element->QueryIntAttribute("TotalFoodItemsEaten", &totalFoodItemsEaten); element->QueryIntAttribute("TotalFoodItemsEaten", &totalFoodItemsEaten);
} }
if (auto element = root->FirstChildElement("Play")) auto element = root->FirstChildElement("SkillCheck");
if (!element) element = root->FirstChildElement("Play");
if (element)
{ {
element->QueryIntAttribute("TotalPlays", &totalPlays); element->QueryIntAttribute("TotalPlays", &totalPlays);
element->QueryIntAttribute("HighScore", &highScore); element->QueryIntAttribute("HighScore", &highScore);
@@ -130,13 +130,13 @@ namespace game::resource::xml
characterElement->SetAttribute("TotalCaloriesConsumed", totalCaloriesConsumed); characterElement->SetAttribute("TotalCaloriesConsumed", totalCaloriesConsumed);
characterElement->SetAttribute("TotalFoodItemsEaten", totalFoodItemsEaten); characterElement->SetAttribute("TotalFoodItemsEaten", totalFoodItemsEaten);
auto playElement = element->InsertNewChildElement("Play"); auto skillCheckElement = element->InsertNewChildElement("SkillCheck");
playElement->SetAttribute("TotalPlays", totalPlays); skillCheckElement->SetAttribute("TotalPlays", totalPlays);
playElement->SetAttribute("HighScore", highScore); skillCheckElement->SetAttribute("HighScore", highScore);
playElement->SetAttribute("BestCombo", bestCombo); skillCheckElement->SetAttribute("BestCombo", bestCombo);
auto gradesElement = playElement->InsertNewChildElement("Grades"); auto gradesElement = skillCheckElement->InsertNewChildElement("Grades");
for (auto& [i, count] : gradeCounts) for (auto& [i, count] : gradeCounts)
{ {

View File

@@ -23,8 +23,7 @@ namespace game::resource::xml
if (document.LoadFile(pathString.c_str()) != XML_SUCCESS) if (document.LoadFile(pathString.c_str()) != XML_SUCCESS)
{ {
logger.error( logger.error(std::format("Could not initialize character save file: {} ({})", pathString, document.ErrorStr()));
std::format("Could not initialize character save file: {} ({})", pathString, document.ErrorStr()));
return; return;
} }
@@ -34,13 +33,10 @@ namespace game::resource::xml
query_string_attribute(root, "MeasurementSystem", &measurementSystemString); query_string_attribute(root, "MeasurementSystem", &measurementSystemString);
measurementSystem = measurementSystemString == "Imperial" ? measurement::IMPERIAL : measurement::METRIC; measurementSystem = measurementSystemString == "Imperial" ? measurement::IMPERIAL : measurement::METRIC;
root->QueryIntAttribute("Volume", &volume); root->QueryIntAttribute("Volume", &volume);
root->QueryFloatAttribute("ColorR", &color.r); query_vec3(root, "ColorR", "ColorG", "ColorB", color);
root->QueryFloatAttribute("ColorG", &color.g); query_vec2(root, "WindowX", "WindowY", windowPosition);
root->QueryFloatAttribute("ColorB", &color.b); query_ivec2(root, "WindowW", "WindowH", windowSize);
root->QueryFloatAttribute("WindowX", &windowPosition.x); query_bool_attribute(root, "IsUseCharacterColor", &isUseCharacterColor);
root->QueryFloatAttribute("WindowY", &windowPosition.y);
root->QueryIntAttribute("WindowW", &windowSize.x);
root->QueryIntAttribute("WindowH", &windowSize.y);
} }
logger.info(std::format("Initialized settings: {}", pathString)); logger.info(std::format("Initialized settings: {}", pathString));
@@ -59,13 +55,10 @@ namespace game::resource::xml
element->SetAttribute("MeasurementSystem", measurementSystem == measurement::IMPERIAL ? "Imperial" : "Metric"); element->SetAttribute("MeasurementSystem", measurementSystem == measurement::IMPERIAL ? "Imperial" : "Metric");
element->SetAttribute("Volume", volume); element->SetAttribute("Volume", volume);
element->SetAttribute("ColorR", color.r); set_vec3_attribute(element, "ColorR", "ColorG", "ColorB", color);
element->SetAttribute("ColorG", color.g); set_vec2_attribute(element, "WindowX", "WindowY", windowPosition);
element->SetAttribute("ColorB", color.b); set_ivec2_attribute(element, "WindowW", "WindowH", windowSize);
element->SetAttribute("WindowX", windowPosition.x); set_bool_attribute(element, "IsUseCharacterColor", isUseCharacterColor);
element->SetAttribute("WindowY", windowPosition.y);
element->SetAttribute("WindowW", windowSize.x);
element->SetAttribute("WindowH", windowSize.y);
document.InsertFirstChild(element); document.InsertFirstChild(element);

View File

@@ -21,6 +21,7 @@ namespace game::resource::xml
util::measurement::System measurementSystem{util::measurement::METRIC}; util::measurement::System measurementSystem{util::measurement::METRIC};
int volume{50}; int volume{50};
bool isUseCharacterColor{true};
glm::vec3 color{0.120f, 0.515f, 0.115f}; glm::vec3 color{0.120f, 0.515f, 0.115f};
glm::ivec2 windowSize{1600, 900}; glm::ivec2 windowSize{1600, 900};

View File

@@ -1,4 +1,4 @@
#include "play.hpp" #include "skill_check.hpp"
#include "../../log.hpp" #include "../../log.hpp"
#include "util.hpp" #include "util.hpp"
@@ -10,7 +10,7 @@ using namespace game::util;
namespace game::resource::xml namespace game::resource::xml
{ {
Play::Play(const physfs::Path& path, Dialogue& dialogue) SkillCheck::SkillCheck(const physfs::Path& path, Dialogue& dialogue)
{ {
XMLDocument document; XMLDocument document;
@@ -62,8 +62,8 @@ namespace game::resource::xml
} }
isValid = true; isValid = true;
logger.info(std::format("Initialized play schema: {}", path.c_str())); logger.info(std::format("Initialized skill check schema: {}", path.c_str()));
} }
bool Play::is_valid() const { return isValid; }; bool SkillCheck::is_valid() const { return isValid; };
} }

View File

@@ -8,7 +8,7 @@
namespace game::resource::xml namespace game::resource::xml
{ {
class Play class SkillCheck
{ {
public: public:
struct Grade struct Grade
@@ -48,8 +48,8 @@ namespace game::resource::xml
bool isValid{}; bool isValid{};
Play() = default; SkillCheck() = default;
Play(const util::physfs::Path&, Dialogue&); SkillCheck(const util::physfs::Path&, Dialogue&);
bool is_valid() const; bool is_valid() const;
}; };

View File

@@ -12,6 +12,8 @@ using namespace game::util;
namespace game::resource::xml namespace game::resource::xml
{ {
XMLError query_result_merge(XMLError result, XMLError next) { return result == XML_SUCCESS ? next : result; }
XMLError query_string_attribute(XMLElement* element, const char* attribute, std::string* value) XMLError query_string_attribute(XMLElement* element, const char* attribute, std::string* value)
{ {
const char* temp = nullptr; const char* temp = nullptr;
@@ -45,6 +47,60 @@ namespace game::resource::xml
return result; return result;
} }
XMLError query_ivec2(XMLElement* element, const char* attributeX, const char* attributeY, glm::ivec2& value)
{
auto result = element->QueryIntAttribute(attributeX, &value.x);
result = query_result_merge(result, element->QueryIntAttribute(attributeY, &value.y));
return result;
}
XMLError query_vec2(XMLElement* element, const char* attributeX, const char* attributeY, glm::vec2& value)
{
auto result = element->QueryFloatAttribute(attributeX, &value.x);
result = query_result_merge(result, element->QueryFloatAttribute(attributeY, &value.y));
return result;
}
XMLError query_vec3(XMLElement* element, const char* attributeX, const char* attributeY, const char* attributeZ,
glm::vec3& value)
{
auto result = element->QueryFloatAttribute(attributeX, &value.x);
result = query_result_merge(result, element->QueryFloatAttribute(attributeY, &value.y));
result = query_result_merge(result, element->QueryFloatAttribute(attributeZ, &value.z));
return result;
}
XMLError set_bool_attribute(XMLElement* element, const char* attribute, bool value)
{
element->SetAttribute(attribute, value ? "true" : "false");
return XML_SUCCESS;
}
XMLError set_ivec2_attribute(XMLElement* element, const char* attributeX, const char* attributeY,
const glm::ivec2& value)
{
element->SetAttribute(attributeX, value.x);
element->SetAttribute(attributeY, value.y);
return XML_SUCCESS;
}
XMLError set_vec2_attribute(XMLElement* element, const char* attributeX, const char* attributeY,
const glm::vec2& value)
{
element->SetAttribute(attributeX, value.x);
element->SetAttribute(attributeY, value.y);
return XML_SUCCESS;
}
XMLError set_vec3_attribute(XMLElement* element, const char* attributeX, const char* attributeY,
const char* attributeZ, const glm::vec3& value)
{
element->SetAttribute(attributeX, value.x);
element->SetAttribute(attributeY, value.y);
element->SetAttribute(attributeZ, value.z);
return XML_SUCCESS;
}
XMLError query_float_optional_attribute(XMLElement* element, const char* attribute, std::optional<float>& value) XMLError query_float_optional_attribute(XMLElement* element, const char* attribute, std::optional<float>& value)
{ {
value.emplace(); value.emplace();
@@ -84,102 +140,139 @@ namespace game::resource::xml
return result; return result;
} }
void query_event_id(XMLElement* element, const char* name, const Anm2& anm2, int& eventID) XMLError query_event_id(XMLElement* element, const char* name, const Anm2& anm2, int& eventID)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
if (anm2.eventMap.contains(string)) if (anm2.eventMap.contains(string))
{
eventID = anm2.eventMap.at(string); eventID = anm2.eventMap.at(string);
return XML_SUCCESS;
}
else else
{ {
logger.error(std::format("Could not query anm2 event ID: {} ({})", string, anm2.path)); logger.error(std::format("Could not query anm2 event ID: {} ({})", string, anm2.path));
eventID = -1; eventID = -1;
return XML_ERROR_PARSING_ATTRIBUTE;
} }
} }
void query_layer_id(XMLElement* element, const char* name, const Anm2& anm2, int& layerID) XMLError query_layer_id(XMLElement* element, const char* name, const Anm2& anm2, int& layerID)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
if (anm2.layerMap.contains(string)) if (anm2.layerMap.contains(string))
{
layerID = anm2.layerMap.at(string); layerID = anm2.layerMap.at(string);
return XML_SUCCESS;
}
else else
{ {
logger.error(std::format("Could not query anm2 layer ID: {} ({})", string, anm2.path)); logger.error(std::format("Could not query anm2 layer ID: {} ({})", string, anm2.path));
layerID = -1; layerID = -1;
return XML_ERROR_PARSING_ATTRIBUTE;
} }
} }
void query_null_id(XMLElement* element, const char* name, const Anm2& anm2, int& nullID) XMLError query_null_id(XMLElement* element, const char* name, const Anm2& anm2, int& nullID)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
if (anm2.nullMap.contains(string)) if (anm2.nullMap.contains(string))
{
nullID = anm2.nullMap.at(string); nullID = anm2.nullMap.at(string);
return XML_SUCCESS;
}
else else
{ {
logger.error(std::format("Could not query anm2 null ID: {} ({})", string, anm2.path)); logger.error(std::format("Could not query anm2 null ID: {} ({})", string, anm2.path));
nullID = -1; nullID = -1;
return XML_ERROR_PARSING_ATTRIBUTE;
} }
} }
void query_anm2(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath, XMLError query_anm2(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath,
Anm2& anm2, Anm2::Flags flags) Anm2& anm2, Anm2::Flags flags)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
anm2 = Anm2(physfs::Path(archive + "/" + rootPath + "/" + string), flags); anm2 = Anm2(physfs::Path(archive + "/" + rootPath + "/" + string), flags);
return XML_SUCCESS;
} }
void query_texture(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath, XMLError query_texture(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath,
Texture& texture) Texture& texture)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
texture = Texture(physfs::Path(archive + "/" + rootPath + "/" + string)); texture = Texture(physfs::Path(archive + "/" + rootPath + "/" + string));
return XML_SUCCESS;
} }
void query_sound(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath, XMLError query_sound(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath,
Audio& sound) Audio& sound)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
sound = Audio(physfs::Path(archive + "/" + rootPath + "/" + string)); sound = Audio(physfs::Path(archive + "/" + rootPath + "/" + string));
return XML_SUCCESS;
} }
void query_font(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath, XMLError query_font(XMLElement* element, const char* name, const std::string& archive, const std::string& rootPath,
Font& font) Font& font)
{ {
std::string string{}; std::string string{};
query_string_attribute(element, name, &string); auto result = query_string_attribute(element, name, &string);
if (result != XML_SUCCESS) return result;
font = Font(physfs::Path(archive + "/" + rootPath + "/" + string)); font = Font(physfs::Path(archive + "/" + rootPath + "/" + string));
return XML_SUCCESS;
} }
void query_animation_entry(XMLElement* element, AnimationEntry& animationEntry) XMLError query_animation_entry(XMLElement* element, AnimationEntry& animationEntry)
{ {
query_string_attribute(element, "Animation", &animationEntry.animation); auto result = query_string_attribute(element, "Animation", &animationEntry.animation);
element->QueryFloatAttribute("Weight", &animationEntry.weight); result = query_result_merge(result, element->QueryFloatAttribute("Weight", &animationEntry.weight));
return result;
} }
void query_animation_entry_collection(XMLElement* element, const char* name, XMLError query_animation_entry_collection(XMLElement* element, const char* name,
AnimationEntryCollection& animationEntryCollection) AnimationEntryCollection& animationEntryCollection)
{ {
auto result = XML_SUCCESS;
for (auto child = element->FirstChildElement(name); child; child = child->NextSiblingElement(name)) for (auto child = element->FirstChildElement(name); child; child = child->NextSiblingElement(name))
query_animation_entry(child, animationEntryCollection.emplace_back()); result = query_result_merge(result, query_animation_entry(child, animationEntryCollection.emplace_back()));
return result;
} }
void query_sound_entry(XMLElement* element, const std::string& archive, const std::string& rootPath, XMLError query_sound_entry(XMLElement* element, const std::string& archive, const std::string& rootPath,
SoundEntry& soundEntry, const std::string& attributeName) SoundEntry& soundEntry, const std::string& attributeName)
{ {
query_sound(element, attributeName.c_str(), archive, rootPath, soundEntry.sound); auto result = query_sound(element, attributeName.c_str(), archive, rootPath, soundEntry.sound);
element->QueryFloatAttribute("Weight", &soundEntry.weight); result = query_result_merge(result, element->QueryFloatAttribute("Weight", &soundEntry.weight));
return result;
} }
void query_sound_entry_collection(XMLElement* element, const char* name, const std::string& archive, XMLError query_sound_entry_collection(XMLElement* element, const char* name, const std::string& archive,
const std::string& rootPath, SoundEntryCollection& soundEntryCollection, const std::string& rootPath, SoundEntryCollection& soundEntryCollection,
const std::string& attributeName) const std::string& attributeName)
{ {
auto result = XML_SUCCESS;
for (auto child = element->FirstChildElement(name); child; child = child->NextSiblingElement(name)) for (auto child = element->FirstChildElement(name); child; child = child->NextSiblingElement(name))
query_sound_entry(child, archive, rootPath, soundEntryCollection.emplace_back(), attributeName); result = query_result_merge(
result, query_sound_entry(child, archive, rootPath, soundEntryCollection.emplace_back(), attributeName));
return result;
} }
} }

View File

@@ -4,6 +4,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <glm/glm.hpp>
#include <tinyxml2.h> #include <tinyxml2.h>
#include "animation_entry.hpp" #include "animation_entry.hpp"
@@ -19,33 +20,43 @@ namespace game::resource::xml
tinyxml2::XMLError query_bool_attribute(tinyxml2::XMLElement*, const char*, bool*); tinyxml2::XMLError query_bool_attribute(tinyxml2::XMLElement*, const char*, bool*);
tinyxml2::XMLError query_path_attribute(tinyxml2::XMLElement*, const char*, std::filesystem::path*); tinyxml2::XMLError query_path_attribute(tinyxml2::XMLElement*, const char*, std::filesystem::path*);
tinyxml2::XMLError query_color_attribute(tinyxml2::XMLElement*, const char*, float*); tinyxml2::XMLError query_color_attribute(tinyxml2::XMLElement*, const char*, float*);
tinyxml2::XMLError query_ivec2(tinyxml2::XMLElement*, const char*, const char*, glm::ivec2&);
tinyxml2::XMLError query_vec2(tinyxml2::XMLElement*, const char*, const char*, glm::vec2&);
tinyxml2::XMLError query_vec3(tinyxml2::XMLElement*, const char*, const char*, const char*, glm::vec3&);
tinyxml2::XMLError set_bool_attribute(tinyxml2::XMLElement*, const char*, bool);
tinyxml2::XMLError set_ivec2_attribute(tinyxml2::XMLElement*, const char*, const char*, const glm::ivec2&);
tinyxml2::XMLError set_vec2_attribute(tinyxml2::XMLElement*, const char*, const char*, const glm::vec2&);
tinyxml2::XMLError set_vec3_attribute(tinyxml2::XMLElement*, const char*, const char*, const char*,
const glm::vec3&);
tinyxml2::XMLError query_float_optional_attribute(tinyxml2::XMLElement* element, const char* attribute, tinyxml2::XMLError query_float_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
std::optional<float>& value); std::optional<float>& value);
tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute, tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
std::optional<int>& value); std::optional<int>& value);
void query_event_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& eventID); tinyxml2::XMLError query_event_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& eventID);
void query_layer_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& layerID); tinyxml2::XMLError query_layer_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& layerID);
void query_null_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& nullID); tinyxml2::XMLError query_null_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& nullID);
void query_anm2(tinyxml2::XMLElement* element, const char* name, const std::string& archive, tinyxml2::XMLError query_anm2(tinyxml2::XMLElement* element, const char* name, const std::string& archive,
const std::string& rootPath, Anm2& anm2, Anm2::Flags flags = {}); const std::string& rootPath, Anm2& anm2, Anm2::Flags flags = {});
void query_texture(tinyxml2::XMLElement* element, const char* name, const std::string& archive, tinyxml2::XMLError query_texture(tinyxml2::XMLElement* element, const char* name, const std::string& archive,
const std::string& rootPath, Texture& texture); const std::string& rootPath, Texture& texture);
void query_sound(tinyxml2::XMLElement* element, const char* name, const std::string& archive, tinyxml2::XMLError query_sound(tinyxml2::XMLElement* element, const char* name, const std::string& archive,
const std::string& rootPath, Audio& sound); const std::string& rootPath, Audio& sound);
void query_font(tinyxml2::XMLElement* element, const char* name, const std::string& archive, tinyxml2::XMLError query_font(tinyxml2::XMLElement* element, const char* name, const std::string& archive,
const std::string& rootPath, Font& font); const std::string& rootPath, Font& font);
void query_animation_entry(tinyxml2::XMLElement* element, AnimationEntry& animationEntry); tinyxml2::XMLError query_animation_entry(tinyxml2::XMLElement* element, AnimationEntry& animationEntry);
void query_animation_entry_collection(tinyxml2::XMLElement* element, const char* name, tinyxml2::XMLError query_animation_entry_collection(tinyxml2::XMLElement* element, const char* name,
AnimationEntryCollection& animationEntryCollection); AnimationEntryCollection& animationEntryCollection);
void query_sound_entry(tinyxml2::XMLElement* element, const std::string& archive, const std::string& rootPath, tinyxml2::XMLError query_sound_entry(tinyxml2::XMLElement* element, const std::string& archive,
SoundEntry& soundEntry, const std::string& attributeName = "Sound"); const std::string& rootPath, SoundEntry& soundEntry,
void query_sound_entry_collection(tinyxml2::XMLElement* element, const char* name, const std::string& archive, const std::string& attributeName = "Sound");
const std::string& rootPath, SoundEntryCollection& soundEntryCollection, tinyxml2::XMLError query_sound_entry_collection(tinyxml2::XMLElement* element, const char* name,
const std::string& attributeName = "Sound"); const std::string& archive, const std::string& rootPath,
SoundEntryCollection& soundEntryCollection,
const std::string& attributeName = "Sound");
tinyxml2::XMLError document_load(const util::physfs::Path&, tinyxml2::XMLDocument&); tinyxml2::XMLError document_load(const util::physfs::Path&, tinyxml2::XMLDocument&);
} }

View File

@@ -31,8 +31,8 @@ namespace game
case SELECT: case SELECT:
select.tick(); select.tick();
break; break;
case MAIN: case PLAY:
main.tick(resources); play.tick(resources);
break; break;
default: default:
break; break;
@@ -52,7 +52,7 @@ namespace game
ImGui_ImplSDL3_ProcessEvent(&event); ImGui_ImplSDL3_ProcessEvent(&event);
if (event.type == SDL_EVENT_QUIT) if (event.type == SDL_EVENT_QUIT)
{ {
if (type == MAIN) main.exit(resources); if (type == PLAY) play.exit(resources);
isRunning = false; isRunning = false;
} }
if (!isRunning) return; if (!isRunning) return;
@@ -68,30 +68,30 @@ namespace game
select.update(resources); select.update(resources);
if (select.info.isNewGame || select.info.isContinue) if (select.info.isNewGame || select.info.isContinue)
{ {
Main::Game game = select.info.isNewGame ? Main::NEW_GAME : Main::CONTINUE; Play::Game game = select.info.isNewGame ? Play::NEW_GAME : Play::CONTINUE;
if (game == Main::NEW_GAME) resources.character_save_set(select.characterIndex, resource::xml::Save()); if (game == Play::NEW_GAME) resources.character_save_set(select.characterIndex, resource::xml::Save());
main.set(resources, select.characterIndex, game); play.set(resources, select.characterIndex, game);
type = MAIN; type = PLAY;
select.info.isNewGame = false; select.info.isNewGame = false;
select.info.isContinue = false; select.info.isContinue = false;
} }
break; break;
case MAIN: case PLAY:
main.update(resources); play.update(resources);
if (main.menu.configuration.isGoToSelect) if (play.menu.settingsMenu.isGoToSelect)
{ {
main.exit(resources); play.exit(resources);
type = SELECT; type = SELECT;
main.menu.configuration.isGoToSelect = false; play.menu.settingsMenu.isGoToSelect = false;
} }
break; break;
default: default:
break; break;
} }
auto isHideCursor = type == MAIN; auto isHideCursor = type == PLAY;
if (isHideCursor != isCursorHidden) if (isHideCursor != isCursorHidden)
{ {
if (isHideCursor) if (isHideCursor)
@@ -104,6 +104,8 @@ namespace game
void State::render() void State::render()
{ {
auto& color =
resources.settings.isUseCharacterColor && type == PLAY ? play.character.data.color : resources.settings.color;
auto windowSize = resources.settings.windowSize; auto windowSize = resources.settings.windowSize;
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
SDL_GetWindowSize(window, &windowSize.x, &windowSize.y); SDL_GetWindowSize(window, &windowSize.x, &windowSize.y);
@@ -111,7 +113,7 @@ namespace game
canvas.bind(); canvas.bind();
canvas.size_set(windowSize); canvas.size_set(windowSize);
canvas.clear(vec4(resources.settings.color, 1.0f)); canvas.clear(vec4(color, 1.0f));
canvas.unbind(); canvas.unbind();
switch (type) switch (type)
@@ -119,8 +121,8 @@ namespace game
case SELECT: case SELECT:
select.render(resources, canvas); select.render(resources, canvas);
break; break;
case MAIN: case PLAY:
main.render(resources, canvas); play.render(resources, canvas);
break; break;
default: default:
break; break;

View File

@@ -2,10 +2,10 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "canvas.hpp" #include "render/canvas.hpp"
#include "resources.hpp" #include "resources.hpp"
#include "state/main.hpp" #include "state/play.hpp"
#include "state/select.hpp" #include "state/select.hpp"
#include "entity/cursor.hpp" #include "entity/cursor.hpp"
@@ -22,7 +22,7 @@ namespace game
enum Type enum Type
{ {
MAIN, PLAY,
SELECT SELECT
}; };
@@ -30,7 +30,7 @@ namespace game
Resources resources; Resources resources;
state::Main main; state::Play play;
state::Select select; state::Select select;
void tick(); void tick();

View File

@@ -1,228 +0,0 @@
#include "inventory.hpp"
#include <cmath>
#include <format>
#include <ranges>
#include "../../util/color.hpp"
#include "../../util/imgui.hpp"
#include "../../util/imgui/widget.hpp"
#include "../../util/math.hpp"
using namespace game::util;
using namespace game::util::imgui;
using namespace game::entity;
using namespace game::resource;
using namespace glm;
namespace game::state::main
{
void Inventory::tick()
{
for (auto& [i, actor] : actors)
actor.tick();
}
void Inventory::update(Resources& resources, ItemManager& itemManager, entity::Character& character)
{
auto& schema = character.data.itemSchema;
if (!itemManager.returnItemIDs.empty())
{
for (auto& id : itemManager.returnItemIDs)
values[id]++;
itemManager.returnItemIDs.clear();
}
if (ImGui::BeginChild("##Inventory Child"))
{
auto cursorPos = ImGui::GetCursorPos();
auto cursorStartX = ImGui::GetCursorPosX();
auto size = ImVec2(SIZE, SIZE);
for (int i = 0; i < (int)schema.items.size(); i++)
{
auto& item = schema.items[i];
auto& quantity = values[i];
auto& category = schema.categories[item.categoryID];
auto& calories = item.calories;
auto& digestionBonus = item.digestionBonus;
auto& eatSpeedBonus = item.eatSpeedBonus;
auto& rarity = schema.rarities[item.rarityID];
quantity = glm::clamp(0, quantity, schema.quantityMax);
if (rarity.isHidden && quantity <= 0) continue;
ImGui::PushID(i);
ImGui::SetCursorPos(cursorPos);
auto cursorScreenPos = ImGui::GetCursorScreenPos();
if (!actors.contains(i))
{
actors[i] = Actor(schema.anm2s[i], {}, Actor::SET);
rects[i] = actors[i].rect();
}
auto& rect = rects[i];
auto rectSize = vec2(rect.z, rect.w);
auto previewScale = (size.x <= 0.0f || size.y <= 0.0f || rectSize.x <= 0.0f || rectSize.y <= 0.0f ||
!std::isfinite(rectSize.x) || !std::isfinite(rectSize.y))
? 0.0f
: std::min(size.x / rectSize.x, size.y / rectSize.y);
auto previewSize = rectSize * previewScale;
auto canvasSize = ivec2(std::max(1.0f, previewSize.x), std::max(1.0f, previewSize.y));
if (!canvases.contains(i)) canvases.emplace((int)i, Canvas(canvasSize, Canvas::FLIP));
auto& canvas = canvases[i];
bool isPossibleToUpgrade = item.upgradeID.has_value() && item.upgradeCount.has_value() &&
schema.idToStringMap.contains(*item.upgradeID);
bool isAbleToUpgrade = isPossibleToUpgrade && quantity >= *item.upgradeCount;
canvas.zoom = math::to_percent(previewScale);
canvas.pan = vec2(rect.x, rect.y);
canvas.bind();
canvas.size_set(canvasSize);
canvas.clear();
actors[i].render(resources.shaders[shader::TEXTURE], resources.shaders[shader::RECT], canvas);
canvas.unbind();
ImGui::BeginDisabled(quantity < 1);
if (WIDGET_FX(ImGui::ImageButton("##Image Button", canvas.texture, size, ImVec2(), ImVec2(1, 1), ImVec4(),
quantity <= 0 ? ImVec4(0, 0, 0, 1) : ImVec4(1, 1, 1, 1))) &&
quantity > 0)
{
if (ImGui::IsKeyDown(ImGuiMod_Shift))
{
if (isAbleToUpgrade)
{
if (ImGui::IsKeyDown(ImGuiMod_Ctrl))
{
while (quantity >= *item.upgradeCount)
{
values.at(*item.upgradeID)++;
quantity -= *item.upgradeCount;
}
}
else
{
values.at(*item.upgradeID)++;
quantity -= *item.upgradeCount;
}
schema.sounds.upgrade.play();
}
else
schema.sounds.upgradeFail.play();
}
else if (category.isEdible)
{
if (itemManager.items.size() + 1 >= ItemManager::LIMIT)
character.data.itemSchema.sounds.dispose.play();
else
{
character.data.itemSchema.sounds.summon.play();
itemManager.queuedItemIDs.emplace_back(i);
quantity--;
}
}
else if (item.isToggleSpritesheet)
{
character.spritesheet_set(character.spritesheetType == Character::NORMAL ? Character::ALTERNATE
: Character::NORMAL);
character.data.alternateSpritesheet.sound.play();
quantity--;
}
}
ImGui::EndDisabled();
if (ImGui::BeginItemTooltip())
{
if (quantity > 0)
{
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::Text("%s (x%i)", item.name.c_str(), quantity);
ImGui::Separator();
ImGui::PopFont();
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
ImGui::Text("-- %s (%s) --", category.name.c_str(), rarity.name.c_str());
if (item.flavorID.has_value()) ImGui::Text("Flavor: %s", schema.flavors[*item.flavorID].name.c_str());
if (calories.has_value()) ImGui::Text("%0.0f kcal", *calories);
if (digestionBonus.has_value())
{
if (*digestionBonus > 0)
ImGui::Text("Digestion Rate Bonus: +%0.2f%% / sec", *digestionBonus * 60.0f);
else if (digestionBonus < 0)
ImGui::Text("Digestion Rate Penalty: %0.2f%% / sec", *digestionBonus * 60.0f);
}
if (eatSpeedBonus.has_value())
{
if (*eatSpeedBonus > 0)
ImGui::Text("Eat Speed Bonus: +%0.2f%% / sec", *eatSpeedBonus);
else if (eatSpeedBonus < 0)
ImGui::Text("Eat Speed Penalty: %0.2f%% / sec", *eatSpeedBonus);
}
if (isPossibleToUpgrade)
{
ImGui::Text("Upgrade: %ix -> %s", *item.upgradeCount, schema.idToStringMap.at(*item.upgradeID).c_str());
if (isAbleToUpgrade)
ImGui::TextUnformatted("(Shift + Click -> Upgrade)\n(Shift + Ctrl + Click -> Upgrade All)");
}
ImGui::PopStyleColor();
ImGui::Separator();
ImGui::TextUnformatted(item.description.c_str());
}
else
{
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::TextUnformatted("???");
ImGui::PopFont();
}
ImGui::EndTooltip();
}
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
auto text = std::format("x{}", quantity);
auto textPos = ImVec2(cursorScreenPos.x + size.x - ImGui::CalcTextSize(text.c_str()).x,
cursorScreenPos.y + size.y - ImGui::GetTextLineHeightWithSpacing());
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
text.c_str());
ImGui::PopFont();
auto increment = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
cursorPos.x += increment;
if (cursorPos.x + increment > ImGui::GetContentRegionAvail().x)
{
cursorPos.x = cursorStartX;
cursorPos.y += increment;
}
ImGui::PopID();
}
if (count() == 0) ImGui::Text("Check the \"Play\" tab to earn rewards!");
}
ImGui::EndChild();
}
int Inventory::count()
{
int count{};
for (auto& [type, quantity] : values)
count += quantity;
return count;
}
}

View File

@@ -1,4 +1,4 @@
#include "main.hpp" #include "play.hpp"
#include <array> #include <array>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@@ -12,12 +12,12 @@
using namespace game::resource; using namespace game::resource;
using namespace game::util; using namespace game::util;
using namespace game::state::main; using namespace game::state::play;
using namespace glm; using namespace glm;
namespace game::state namespace game::state
{ {
World::Focus Main::focus_get() World::Focus Play::focus_get()
{ {
if (!isWindows) return World::CENTER; if (!isWindows) return World::CENTER;
@@ -27,7 +27,7 @@ namespace game::state
: World::CENTER; : World::CENTER;
} }
void Main::set(Resources& resources, int selectedCharacterIndex, enum Game game) void Play::set(Resources& resources, int selectedCharacterIndex, enum Game game)
{ {
auto& data = resources.character_get(selectedCharacterIndex); auto& data = resources.character_get(selectedCharacterIndex);
auto& saveData = data.save; auto& saveData = data.save;
@@ -35,8 +35,8 @@ namespace game::state
auto& dialogue = data.dialogue; auto& dialogue = data.dialogue;
auto& menuSchema = data.menuSchema; auto& menuSchema = data.menuSchema;
this->characterIndex = selectedCharacterIndex; this->characterIndex = selectedCharacterIndex;
konamiCodeIndex = 0; cheatCodeIndex = 0;
konamiCodeStartTime = 0.0; cheatCodeStartTime = 0.0;
character = character =
entity::Character(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y)); entity::Character(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y));
@@ -58,6 +58,7 @@ namespace game::state
characterManager = CharacterManager{}; characterManager = CharacterManager{};
cursor = entity::Cursor(character.data.cursorSchema.anm2); cursor = entity::Cursor(character.data.cursorSchema.anm2);
cursor.interactTypeID = character.data.interactTypeNames.empty() ? -1 : 0;
menu.inventory = Inventory{}; menu.inventory = Inventory{};
for (auto& [id, quantity] : saveData.inventory) for (auto& [id, quantity] : saveData.inventory)
@@ -79,14 +80,14 @@ namespace game::state
imgui::style::rounding_set(menuSchema.rounding); imgui::style::rounding_set(menuSchema.rounding);
imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select); imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select);
menu.color_set_check(resources, character);
menu.play = Play(character); menu.skillCheck = SkillCheck(character);
menu.play.totalPlays = saveData.totalPlays; menu.skillCheck.totalPlays = saveData.totalPlays;
menu.play.highScore = saveData.highScore; menu.skillCheck.highScore = saveData.highScore;
menu.play.bestCombo = saveData.bestCombo; menu.skillCheck.bestCombo = saveData.bestCombo;
menu.play.gradeCounts = saveData.gradeCounts; menu.skillCheck.gradeCounts = saveData.gradeCounts;
menu.play.isHighScoreAchieved = saveData.highScore > 0 ? true : false; menu.skillCheck.isHighScoreAchieved = saveData.highScore > 0 ? true : false;
menu.isChat = character.data.dialogue.help.is_valid() || character.data.dialogue.random.is_valid(); menu.isChat = character.data.dialogue.help.is_valid() || character.data.dialogue.random.is_valid();
text.entry = nullptr; text.entry = nullptr;
@@ -114,17 +115,31 @@ namespace game::state
isStartBegin = false; isStartBegin = false;
isStartEnd = false; isStartEnd = false;
} }
if (isPostgame)
{
isEnd = true;
isEndBegin = true;
isEndEnd = true;
}
else
{
isEnd = false;
isEndBegin = false;
isEndEnd = false;
}
} }
void Main::exit(Resources& resources) void Play::exit(Resources& resources)
{ {
imgui::style::color_set(resources.settings.color);
imgui::style::rounding_set(); imgui::style::rounding_set();
imgui::widget::sounds_set(nullptr, nullptr); imgui::widget::sounds_set(nullptr, nullptr);
ImGui::GetIO().FontDefault = resources.font.get(); ImGui::GetIO().FontDefault = resources.font.get();
save(resources); save(resources);
} }
void Main::tick(Resources&) void Play::tick(Resources&)
{ {
character.tick(); character.tick();
cursor.tick(); cursor.tick();
@@ -136,54 +151,54 @@ namespace game::state
item.tick(); item.tick();
} }
void Main::update(Resources& resources) void Play::update(Resources& resources)
{ {
static constexpr std::array<ImGuiKey, 10> KONAMI_CODE = { static constexpr std::array<ImGuiKey, 10> CHEAT_CODE = {
ImGuiKey_UpArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGuiKey_DownArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGuiKey_DownArrow, ImGuiKey_LeftArrow,
ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A}; ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A};
static constexpr std::array<ImGuiKey, 6> KONAMI_INPUT_KEYS = { static constexpr std::array<ImGuiKey, 6> CHEAT_INPUT_KEYS = {
ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A}; ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A};
static constexpr auto KONAMI_CODE_INPUT_TIME_SECONDS = 5.0; static constexpr auto CHEAT_CODE_INPUT_TIME_SECONDS = 5.0;
auto focus = focus_get(); auto focus = focus_get();
auto& dialogue = character.data.dialogue; auto& dialogue = character.data.dialogue;
if (!menu.isCheats) if (!menu.isCheats)
{ {
for (auto key : KONAMI_INPUT_KEYS) for (auto key : CHEAT_INPUT_KEYS)
{ {
if (!ImGui::IsKeyPressed(key, false)) continue; if (!ImGui::IsKeyPressed(key, false)) continue;
if (key == KONAMI_CODE[konamiCodeIndex]) if (key == CHEAT_CODE[cheatCodeIndex])
{ {
konamiCodeIndex++; cheatCodeIndex++;
konamiCodeStartTime = ImGui::GetTime(); cheatCodeStartTime = ImGui::GetTime();
} }
else if (key == KONAMI_CODE[0]) else if (key == CHEAT_CODE[0])
{ {
konamiCodeIndex = 1; cheatCodeIndex = 1;
konamiCodeStartTime = ImGui::GetTime(); cheatCodeStartTime = ImGui::GetTime();
} }
else else
{ {
konamiCodeIndex = 0; cheatCodeIndex = 0;
konamiCodeStartTime = 0.0; cheatCodeStartTime = 0.0;
} }
if (konamiCodeIndex >= (int)KONAMI_CODE.size()) if (cheatCodeIndex >= (int)CHEAT_CODE.size())
{ {
menu.isCheats = true; menu.isCheats = true;
konamiCodeIndex = 0; cheatCodeIndex = 0;
konamiCodeStartTime = 0.0; cheatCodeStartTime = 0.0;
toasts.push("Cheats unlocked!"); toasts.push("Cheats unlocked!");
character.data.menuSchema.sounds.cheatsActivated.play(); character.data.menuSchema.sounds.cheatsActivated.play();
} }
} }
if (konamiCodeIndex > 0 && (ImGui::GetTime() - konamiCodeStartTime > KONAMI_CODE_INPUT_TIME_SECONDS)) if (cheatCodeIndex > 0 && (ImGui::GetTime() - cheatCodeStartTime > CHEAT_CODE_INPUT_TIME_SECONDS))
{ {
konamiCodeIndex = 0; cheatCodeIndex = 0;
konamiCodeStartTime = 0.0; cheatCodeStartTime = 0.0;
} }
} }
@@ -264,15 +279,15 @@ namespace game::state
cursor.update(); cursor.update();
world.update(character, cursor, worldCanvas, focus); world.update(character, cursor, worldCanvas, focus);
if (autosaveTime += ImGui::GetIO().DeltaTime; autosaveTime > AUTOSAVE_TIME || menu.configuration.isSave) if (autosaveTime += ImGui::GetIO().DeltaTime; autosaveTime > AUTOSAVE_TIME || menu.settingsMenu.isSave)
{ {
save(resources); save(resources);
autosaveTime = 0; autosaveTime = 0;
menu.configuration.isSave = false; menu.settingsMenu.isSave = false;
} }
} }
void Main::render(Resources& resources, Canvas& canvas) void Play::render(Resources& resources, Canvas& canvas)
{ {
auto& textureShader = resources.shaders[shader::TEXTURE]; auto& textureShader = resources.shaders[shader::TEXTURE];
auto& rectShader = resources.shaders[shader::RECT]; auto& rectShader = resources.shaders[shader::RECT];
@@ -309,7 +324,7 @@ namespace game::state
canvas.unbind(); canvas.unbind();
} }
void Main::save(Resources& resources) void Play::save(Resources& resources)
{ {
resource::xml::Save save; resource::xml::Save save;
@@ -323,10 +338,10 @@ namespace game::state
save.digestionTimer = character.digestionTimer; save.digestionTimer = character.digestionTimer;
save.totalCaloriesConsumed = character.totalCaloriesConsumed; save.totalCaloriesConsumed = character.totalCaloriesConsumed;
save.totalFoodItemsEaten = character.totalFoodItemsEaten; save.totalFoodItemsEaten = character.totalFoodItemsEaten;
save.totalPlays = menu.play.totalPlays; save.totalPlays = menu.skillCheck.totalPlays;
save.highScore = menu.play.highScore; save.highScore = menu.skillCheck.highScore;
save.bestCombo = menu.play.bestCombo; save.bestCombo = menu.skillCheck.bestCombo;
save.gradeCounts = menu.play.gradeCounts; save.gradeCounts = menu.skillCheck.gradeCounts;
save.isPostgame = isPostgame; save.isPostgame = isPostgame;
save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE; save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE;

View File

@@ -2,19 +2,19 @@
#include "../resources.hpp" #include "../resources.hpp"
#include "main/area_manager.hpp" #include "play/area_manager.hpp"
#include "main/character_manager.hpp" #include "play/character_manager.hpp"
#include "main/info.hpp" #include "play/info.hpp"
#include "main/item_manager.hpp" #include "play/item_manager.hpp"
#include "main/menu.hpp" #include "play/menu.hpp"
#include "main/text.hpp" #include "play/text.hpp"
#include "main/toasts.hpp" #include "play/toasts.hpp"
#include "main/tools.hpp" #include "play/tools.hpp"
#include "main/world.hpp" #include "play/world.hpp"
namespace game::state namespace game::state
{ {
class Main class Play
{ {
public: public:
static constexpr auto AUTOSAVE_TIME = 30.0f; static constexpr auto AUTOSAVE_TIME = 30.0f;
@@ -28,22 +28,22 @@ namespace game::state
entity::Character character; entity::Character character;
entity::Cursor cursor; entity::Cursor cursor;
main::Info info; play::Info info;
main::Menu menu; play::Menu menu;
main::Tools tools; play::Tools tools;
main::Text text; play::Text text;
main::World world; play::World world;
main::Toasts toasts; play::Toasts toasts;
main::ItemManager itemManager{}; play::ItemManager itemManager{};
main::CharacterManager characterManager{}; play::CharacterManager characterManager{};
main::AreaManager areaManager{}; play::AreaManager areaManager{};
int characterIndex{}; int characterIndex{};
int areaIndex{}; int areaIndex{};
float autosaveTime{}; float autosaveTime{};
int konamiCodeIndex{}; int cheatCodeIndex{};
double konamiCodeStartTime{}; double cheatCodeStartTime{};
bool isWindows{true}; bool isWindows{true};
@@ -57,15 +57,15 @@ namespace game::state
bool isPostgame{}; bool isPostgame{};
Canvas worldCanvas{main::World::SIZE}; Canvas worldCanvas{play::World::SIZE};
Main() = default; Play() = default;
void set(Resources&, int characterIndex, Game = CONTINUE); void set(Resources&, int characterIndex, Game = CONTINUE);
void exit(Resources& resources); void exit(Resources& resources);
void update(Resources&); void update(Resources&);
void tick(Resources&); void tick(Resources&);
void render(Resources&, Canvas&); void render(Resources&, Canvas&);
void save(Resources&); void save(Resources&);
main::World::Focus focus_get(); play::World::Focus focus_get();
}; };
}; };

View File

@@ -1,10 +1,10 @@
#include "play.hpp" #include "skill_check.hpp"
#include <imgui_internal.h> #include <imgui_internal.h>
#include "../../util/imgui.hpp" #include "../../../util/imgui.hpp"
#include "../../util/imgui/widget.hpp" #include "../../../util/imgui/widget.hpp"
#include "../../util/math.hpp" #include "../../../util/math.hpp"
#include <cmath> #include <cmath>
#include <format> #include <format>
@@ -15,13 +15,13 @@ using namespace game::entity;
using namespace game::resource; using namespace game::resource;
using namespace glm; using namespace glm;
namespace game::state::main namespace game::state::play
{ {
float Play::accuracy_score_get(entity::Character& character) float SkillCheck::accuracy_score_get(entity::Character& character)
{ {
if (totalPlays == 0) return 0.0f; if (totalPlays == 0) return 0.0f;
auto& schema = character.data.playSchema; auto& schema = character.data.skillCheckSchema;
float combinedWeight{}; float combinedWeight{};
@@ -34,9 +34,9 @@ namespace game::state::main
return glm::clamp(0.0f, math::to_percent(combinedWeight / totalPlays), 100.0f); return glm::clamp(0.0f, math::to_percent(combinedWeight / totalPlays), 100.0f);
} }
Play::Challenge Play::challenge_generate(entity::Character& character) SkillCheck::Challenge SkillCheck::challenge_generate(entity::Character& character)
{ {
auto& schema = character.data.playSchema; auto& schema = character.data.skillCheckSchema;
Challenge newChallenge; Challenge newChallenge;
@@ -61,15 +61,15 @@ namespace game::state::main
return newChallenge; return newChallenge;
} }
Play::Play(entity::Character& character) { challenge = challenge_generate(character); } SkillCheck::SkillCheck(entity::Character& character) { challenge = challenge_generate(character); }
void Play::tick() void SkillCheck::tick()
{ {
for (auto& [i, actor] : itemActors) for (auto& [i, actor] : itemActors)
actor.tick(); actor.tick();
} }
void Play::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text) void SkillCheck::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text)
{ {
static constexpr auto BG_COLOR_MULTIPLIER = 0.5f; static constexpr auto BG_COLOR_MULTIPLIER = 0.5f;
static constexpr ImVec4 LINE_COLOR = ImVec4(1, 1, 1, 1); static constexpr ImVec4 LINE_COLOR = ImVec4(1, 1, 1, 1);
@@ -80,7 +80,7 @@ namespace game::state::main
static constexpr auto ITEM_FALL_GRAVITY = 2400.0f; static constexpr auto ITEM_FALL_GRAVITY = 2400.0f;
auto& dialogue = character.data.dialogue; auto& dialogue = character.data.dialogue;
auto& schema = character.data.playSchema; auto& schema = character.data.skillCheckSchema;
auto& itemSchema = character.data.itemSchema; auto& itemSchema = character.data.itemSchema;
auto& style = ImGui::GetStyle(); auto& style = ImGui::GetStyle();
auto drawList = ImGui::GetWindowDrawList(); auto drawList = ImGui::GetWindowDrawList();
@@ -202,7 +202,7 @@ namespace game::state::main
auto barButtonSize = ImVec2(barMax.x - barMin.x, barMax.y - barMin.y); auto barButtonSize = ImVec2(barMax.x - barMin.x, barMax.y - barMin.y);
if (ImGui::IsKeyPressed(ImGuiKey_Space) || if (ImGui::IsKeyPressed(ImGuiKey_Space) ||
WIDGET_FX(ImGui::InvisibleButton("##PlayBar", barButtonSize, ImGuiButtonFlags_PressedOnClick))) WIDGET_FX(ImGui::InvisibleButton("##SkillCheckBar", barButtonSize, ImGuiButtonFlags_PressedOnClick)))
{ {
int gradeID{}; int gradeID{};
@@ -234,7 +234,7 @@ namespace game::state::main
schema.sounds.rewardScore.play(); schema.sounds.rewardScore.play();
isRewardScoreAchieved = true; isRewardScoreAchieved = true;
for (auto& itemID : itemSchema.rewardItemPool) for (auto& itemID : itemSchema.skillCheckRewardItemPool)
{ {
inventory.values[itemID]++; inventory.values[itemID]++;
if (!itemActors.contains(itemID)) if (!itemActors.contains(itemID))

View File

@@ -1,21 +1,21 @@
#pragma once #pragma once
#include "../../canvas.hpp" #include "../../../render/canvas.hpp"
#include "../../entity/actor.hpp" #include "../../../entity/actor.hpp"
#include "../../entity/character.hpp" #include "../../../entity/character.hpp"
#include "../../resources.hpp" #include "../../../resources.hpp"
#include "inventory.hpp" #include "../inventory.hpp"
#include "text.hpp" #include "../text.hpp"
#include <imgui.h> #include <imgui.h>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace game::state::main namespace game::state::play
{ {
class Play class SkillCheck
{ {
public: public:
@@ -77,8 +77,8 @@ namespace game::state::main
std::unordered_map<int, glm::vec4> itemRects{}; std::unordered_map<int, glm::vec4> itemRects{};
std::unordered_map<int, Canvas> itemCanvases{}; std::unordered_map<int, Canvas> itemCanvases{};
Play() = default; SkillCheck() = default;
Play(entity::Character&); SkillCheck(entity::Character&);
Challenge challenge_generate(entity::Character&); Challenge challenge_generate(entity::Character&);
void tick(); void tick();
void update(Resources&, entity::Character&, Inventory&, Text&); void update(Resources&, entity::Character&, Inventory&, Text&);

View File

@@ -5,7 +5,7 @@
using namespace game::resource; using namespace game::resource;
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
int AreaManager::get(entity::Character& character) int AreaManager::get(entity::Character& character)
{ {

View File

@@ -2,7 +2,7 @@
#include "../../entity/character.hpp" #include "../../entity/character.hpp"
namespace game::state::main namespace game::state::play
{ {
class AreaManager class AreaManager
{ {

View File

@@ -8,7 +8,7 @@
using namespace game::resource::xml; using namespace game::resource::xml;
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
void CharacterManager::update(entity::Character& character, entity::Cursor& cursor, Text& text, Canvas& canvas) void CharacterManager::update(entity::Character& character, entity::Cursor& cursor, Text& text, Canvas& canvas)
{ {
@@ -36,12 +36,19 @@ namespace game::state::main
isInteractingPrevious = isInteracting; isInteractingPrevious = isInteracting;
isHoveringPrevious = isHovering; isHoveringPrevious = isHovering;
isHoldInteractingPrevious = isHoldInteracting;
isHovering = false; isHovering = false;
if (!isInteracting) isHoldInteracting = false;
if (isJustStoppedInteracting) if (isJustStoppedHoldInteracting)
{
cursor.queue_play({cursor.defaultAnimation});
if (character.queuedPlay.empty()) character.queue_idle_animation();
isJustStoppedHoldInteracting = false;
}
else if (isJustStoppedInteracting)
{ {
cursor.queue_play({cursor.defaultAnimation}); cursor.queue_play({cursor.defaultAnimation});
if (cursor.mode == RUB && character.queuedPlay.empty()) character.queue_idle_animation();
isJustStoppedInteracting = false; isJustStoppedInteracting = false;
} }
@@ -58,7 +65,7 @@ namespace game::state::main
auto rect = character.null_frame_rect(interactArea.nullID); auto rect = character.null_frame_rect(interactArea.nullID);
if (cursor.state == entity::Cursor::DEFAULT && math::is_point_in_rectf(rect, cursorWorldPosition) && if (cursor.state == entity::Cursor::DEFAULT && math::is_point_in_rectf(rect, cursorWorldPosition) &&
!isImguiCaptureMouse && interactArea.type == cursor.mode) !isImguiCaptureMouse && interactArea.typeID == cursor.interactTypeID)
{ {
cursor.state = entity::Cursor::HOVER; cursor.state = entity::Cursor::HOVER;
cursor.queue_play({interactArea.animationCursorHover}); cursor.queue_play({interactArea.animationCursorHover});
@@ -68,8 +75,8 @@ namespace game::state::main
if (isMouseLeftClick) if (isMouseLeftClick)
{ {
isInteracting = true; isInteracting = true;
isHoldInteracting = interactArea.isHold;
interactArea.sound.play(); interactArea.sound.play();
lastInteractType = cursor.mode;
if (interactArea.digestionBonusClick > 0 && character.calories > 0 && !character.isDigesting) if (interactArea.digestionBonusClick > 0 && character.calories > 0 && !character.isDigesting)
character.digestionProgress += interactArea.digestionBonusClick; character.digestionProgress += interactArea.digestionBonusClick;
@@ -105,11 +112,13 @@ namespace game::state::main
isImguiCaptureMouse) isImguiCaptureMouse)
{ {
isInteracting = false; isInteracting = false;
isHoldInteracting = false;
interactAreaID = -1; interactAreaID = -1;
} }
} }
if (isInteracting != isInteractingPrevious && !isInteracting) isJustStoppedInteracting = true; if (isInteracting != isInteractingPrevious && !isInteracting) isJustStoppedInteracting = true;
if (isHoldInteracting != isHoldInteractingPrevious && !isHoldInteracting) isJustStoppedHoldInteracting = true;
if (isHovering != isHoveringPrevious && !isHovering) isJustStoppedHovering = true; if (isHovering != isHoveringPrevious && !isHovering) isJustStoppedHovering = true;
cursorWorldPositionPrevious = cursorWorldPosition; cursorWorldPositionPrevious = cursorWorldPosition;

View File

@@ -4,7 +4,7 @@
#include "../../entity/cursor.hpp" #include "../../entity/cursor.hpp"
#include "text.hpp" #include "text.hpp"
namespace game::state::main namespace game::state::play
{ {
class CharacterManager class CharacterManager
{ {
@@ -15,8 +15,10 @@ namespace game::state::main
bool isHoveringPrevious{}; bool isHoveringPrevious{};
bool isJustStoppedInteracting{}; bool isJustStoppedInteracting{};
bool isJustStoppedHovering{}; bool isJustStoppedHovering{};
bool isHoldInteracting{};
bool isHoldInteractingPrevious{};
bool isJustStoppedHoldInteracting{};
int interactAreaID{-1}; int interactAreaID{-1};
InteractType lastInteractType{(InteractType)-1};
glm::vec2 cursorWorldPositionPrevious{}; glm::vec2 cursorWorldPositionPrevious{};
std::string queuedAnimation{}; std::string queuedAnimation{};

View File

@@ -5,7 +5,7 @@
using namespace game::resource; using namespace game::resource;
using namespace game::util::imgui; using namespace game::util::imgui;
namespace game::state::main namespace game::state::play
{ {
void Chat::update(Resources&, Text& text, entity::Character& character) void Chat::update(Resources&, Text& text, entity::Character& character)
{ {

View File

@@ -4,7 +4,7 @@
#include <imgui.h> #include <imgui.h>
namespace game::state::main namespace game::state::play
{ {
class Chat class Chat
{ {

View File

@@ -9,7 +9,7 @@
using namespace game::util::imgui; using namespace game::util::imgui;
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
void Cheats::update(Resources&, entity::Character& character, Inventory& inventory, Text& text) void Cheats::update(Resources&, entity::Character& character, Inventory& inventory, Text& text)
{ {

View File

@@ -5,7 +5,7 @@
#include <imgui.h> #include <imgui.h>
namespace game::state::main namespace game::state::play
{ {
class Cheats class Cheats
{ {

View File

@@ -6,7 +6,7 @@
using namespace game::util::imgui; using namespace game::util::imgui;
namespace game::state::main namespace game::state::play
{ {
void Debug::update(entity::Character& character, entity::Cursor& cursor, ItemManager& itemManager, Canvas& canvas) void Debug::update(entity::Character& character, entity::Cursor& cursor, ItemManager& itemManager, Canvas& canvas)
{ {

View File

@@ -7,7 +7,7 @@
#include <imgui.h> #include <imgui.h>
namespace game::state::main namespace game::state::play
{ {
class Debug class Debug
{ {

View File

@@ -11,7 +11,7 @@
using namespace game::resource; using namespace game::resource;
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
void Info::update(Resources& resources, entity::Character& character) void Info::update(Resources& resources, entity::Character& character)
{ {

View File

@@ -5,7 +5,7 @@
#include <imgui.h> #include <imgui.h>
namespace game::state::main namespace game::state::play
{ {
class Info class Info
{ {

View File

@@ -0,0 +1,348 @@
#include "inventory.hpp"
#include <cmath>
#include <format>
#include <ranges>
#include <tuple>
#include "../../util/color.hpp"
#include "../../util/imgui.hpp"
#include "../../util/imgui/widget.hpp"
#include "../../util/math.hpp"
using namespace game::util;
using namespace game::util::imgui;
using namespace game::entity;
using namespace game::resource;
using namespace glm;
namespace game::state::play
{
void Inventory::tick()
{
for (auto& [i, actor] : actors)
actor.tick();
}
void Inventory::update(Resources& resources, ItemManager& itemManager, entity::Character& character)
{
static constexpr auto INFO_CHILD_HEIGHT_MULTIPLIER = 1.0f / 3.0f;
auto& schema = character.data.itemSchema;
auto quantity_get = [&](int itemID) -> int&
{
auto& quantity = values[itemID];
quantity = glm::clamp(0, quantity, schema.quantityMax);
return quantity;
};
auto is_possible_to_upgrade_get = [&](const resource::xml::Item::Entry& item)
{
return item.upgradeID.has_value() && item.upgradeCount.has_value() &&
schema.idToStringMap.contains(*item.upgradeID);
};
auto is_able_to_upgrade_get = [&](const resource::xml::Item::Entry& item, int quantity)
{ return is_possible_to_upgrade_get(item) && quantity >= *item.upgradeCount; };
auto item_use = [&](int itemID)
{
auto& item = schema.items[itemID];
auto& category = schema.categories[item.categoryID];
auto& quantity = quantity_get(itemID);
if (quantity <= 0) return;
if (category.isEdible)
{
if (itemManager.items.size() + 1 >= ItemManager::LIMIT)
character.data.itemSchema.sounds.dispose.play();
else
{
character.data.itemSchema.sounds.summon.play();
itemManager.queuedItemIDs.emplace_back(itemID);
quantity--;
if (quantity <= 0) selectedItemID = -1;
}
}
else if (item.isToggleSpritesheet)
{
character.spritesheet_set(character.spritesheetType == Character::NORMAL ? Character::ALTERNATE
: Character::NORMAL);
character.data.alternateSpritesheet.sound.play();
quantity--;
}
};
auto item_upgrade = [&](int itemID, bool isAll)
{
auto& item = schema.items[itemID];
auto& quantity = quantity_get(itemID);
if (!is_possible_to_upgrade_get(item))
{
schema.sounds.upgradeFail.play();
return;
}
if (!is_able_to_upgrade_get(item, quantity))
{
schema.sounds.upgradeFail.play();
return;
}
if (isAll)
{
while (quantity >= *item.upgradeCount)
{
values.at(*item.upgradeID)++;
quantity -= *item.upgradeCount;
}
}
else
{
values.at(*item.upgradeID)++;
quantity -= *item.upgradeCount;
}
schema.sounds.upgrade.play();
if (quantity < *item.upgradeCount && selectedItemID == itemID) selectedItemID = *item.upgradeID;
};
auto item_canvas_get = [&](int itemID, ImVec2 size)
{
if (!actors.contains(itemID))
{
actors[itemID] = Actor(schema.anm2s[itemID], {}, Actor::SET);
rects[itemID] = actors[itemID].rect();
}
auto& rect = rects[itemID];
auto rectSize = vec2(rect.z, rect.w);
auto previewScale = (size.x <= 0.0f || size.y <= 0.0f || rectSize.x <= 0.0f || rectSize.y <= 0.0f ||
!std::isfinite(rectSize.x) || !std::isfinite(rectSize.y))
? 0.0f
: std::min(size.x / rectSize.x, size.y / rectSize.y);
auto previewSize = rectSize * previewScale;
auto canvasSize = ivec2(std::max(1.0f, previewSize.x), std::max(1.0f, previewSize.y));
if (!canvases.contains(itemID)) canvases.emplace(itemID, Canvas(canvasSize, Canvas::FLIP));
auto& canvas = canvases[itemID];
canvas.zoom = math::to_percent(previewScale);
canvas.pan = vec2(rect.x, rect.y);
canvas.bind();
canvas.size_set(canvasSize);
canvas.clear();
actors[itemID].render(resources.shaders[shader::TEXTURE], resources.shaders[shader::RECT], canvas);
canvas.unbind();
return std::tuple<Canvas&, glm::vec4&>(canvas, rect);
};
if (!itemManager.returnItemIDs.empty())
{
for (auto& id : itemManager.returnItemIDs)
values[id]++;
itemManager.returnItemIDs.clear();
}
if (ImGui::BeginChild("##Inventory Child", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None,
ImGuiWindowFlags_NoScrollbar))
{
auto inventoryCount = count();
auto available = ImGui::GetContentRegionAvail();
auto isItemSelected = selectedItemID >= 0 && selectedItemID < (int)schema.items.size();
auto isInfoVisible = isItemSelected || inventoryCount == 0;
auto infoChildHeight =
isInfoVisible ? available.y * INFO_CHILD_HEIGHT_MULTIPLIER + ImGui::GetStyle().ItemSpacing.y * 2.0f : 0.0f;
auto inventoryChildHeight =
isInfoVisible ? available.y - infoChildHeight - ImGui::GetStyle().ItemSpacing.y : available.y;
auto childSize = ImVec2(available.x, inventoryChildHeight);
auto infoChildSize = ImVec2(available.x, infoChildHeight);
if (ImGui::BeginChild("##Inventory List Child", childSize))
{
auto cursorPos = ImGui::GetCursorPos();
auto cursorStartX = ImGui::GetCursorPosX();
bool isAnyInventoryItemHovered{};
auto size = ImVec2(SIZE, SIZE);
for (int i = 0; i < (int)schema.items.size(); i++)
{
auto& item = schema.items[i];
auto& quantity = quantity_get(i);
auto& rarity = schema.rarities[item.rarityID];
if (rarity.isHidden && quantity <= 0) continue;
ImGui::PushID(i);
ImGui::SetCursorPos(cursorPos);
auto cursorScreenPos = ImGui::GetCursorScreenPos();
auto [canvas, rect] = item_canvas_get(i, size);
auto isSelected = selectedItemID == i;
if (isSelected)
{
auto selectedColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
ImGui::PushStyleColor(ImGuiCol_Button, selectedColor);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, selectedColor);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, selectedColor);
}
auto isPressed =
WIDGET_FX(ImGui::ImageButton("##Image Button", canvas.texture, size, ImVec2(), ImVec2(1, 1), ImVec4(),
quantity <= 0 ? ImVec4(0, 0, 0, 0.5f) : ImVec4(1, 1, 1, 1)));
if (isSelected) ImGui::PopStyleColor(3);
isAnyInventoryItemHovered = isAnyInventoryItemHovered || ImGui::IsItemHovered();
if (isPressed) selectedItemID = i;
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && quantity > 0) item_use(i);
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
auto text = std::format("x{}", quantity);
auto textPos = ImVec2(cursorScreenPos.x + size.x - ImGui::CalcTextSize(text.c_str()).x,
cursorScreenPos.y + size.y - ImGui::GetTextLineHeightWithSpacing());
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
text.c_str());
ImGui::PopFont();
auto increment = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
cursorPos.x += increment;
if (cursorPos.x + increment > ImGui::GetContentRegionAvail().x)
{
cursorPos.x = cursorStartX;
cursorPos.y += increment;
}
ImGui::PopID();
}
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !isAnyInventoryItemHovered)
selectedItemID = -1;
}
ImGui::EndChild();
isItemSelected = selectedItemID >= 0 && selectedItemID < (int)schema.items.size();
auto selectedQuantity = isItemSelected ? quantity_get(selectedItemID) : 0;
auto isSelectedItemKnown = isItemSelected && selectedQuantity > 0;
if (isInfoVisible &&
ImGui::BeginChild("##Info Child", infoChildSize, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar))
{
ImGui::Separator();
auto isButtonChildVisible = selectedQuantity > 0;
ImGui::PushFont(resources.font.get(), Font::BIG);
auto buttonRowHeight = ImGui::GetFrameHeight();
auto buttonChildHeight =
isButtonChildVisible ? buttonRowHeight * 2.0f + ImGui::GetStyle().ItemSpacing.y * 5.0f : 0.0f;
auto buttonChildSize = ImVec2(ImGui::GetContentRegionAvail().x, buttonChildHeight);
auto infoBodySize =
ImVec2(ImGui::GetContentRegionAvail().x,
ImGui::GetContentRegionAvail().y - buttonChildSize.y -
(isButtonChildVisible ? ImGui::GetStyle().ItemSpacing.y : 0.0f));
ImGui::PopFont();
if (ImGui::BeginChild("##Info Content Child", infoBodySize))
{
if (!isItemSelected)
{
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::TextWrapped("%s", "Check the \"Arcade\" tab to earn rewards!");
ImGui::PopFont();
}
else
{
auto& item = schema.items[selectedItemID];
auto& category = schema.categories[item.categoryID];
auto& rarity = schema.rarities[item.rarityID];
if (isSelectedItemKnown)
{
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::TextWrapped("%s (x%i)", item.name.c_str(), selectedQuantity);
ImGui::PopFont();
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
ImGui::TextWrapped("-- %s (%s) --", category.name.c_str(), rarity.name.c_str());
if (item.flavorID.has_value())
ImGui::TextWrapped("Flavor: %s", schema.flavors[*item.flavorID].name.c_str());
if (item.calories.has_value()) ImGui::TextWrapped("%0.0f kcal", *item.calories);
if (item.digestionBonus.has_value())
{
if (*item.digestionBonus > 0)
ImGui::TextWrapped("Digestion Rate Bonus: +%0.2f%% / sec", *item.digestionBonus * 60.0f);
else if (*item.digestionBonus < 0)
ImGui::TextWrapped("Digestion Rate Penalty: %0.2f%% / sec", *item.digestionBonus * 60.0f);
}
if (item.eatSpeedBonus.has_value())
{
if (*item.eatSpeedBonus > 0)
ImGui::TextWrapped("Eat Speed Bonus: +%0.2f%% / sec", *item.eatSpeedBonus);
else if (*item.eatSpeedBonus < 0)
ImGui::TextWrapped("Eat Speed Penalty: %0.2f%% / sec", *item.eatSpeedBonus);
}
if (is_possible_to_upgrade_get(item))
ImGui::TextWrapped("Upgrade: %ix -> %s", *item.upgradeCount,
schema.idToStringMap.at(*item.upgradeID).c_str());
ImGui::PopStyleColor();
ImGui::Separator();
ImGui::TextWrapped("%s", item.description.c_str());
}
else
{
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::TextWrapped("%s", "???");
ImGui::PopFont();
}
}
}
ImGui::EndChild();
if (isButtonChildVisible &&
ImGui::BeginChild("##Info Actions Child", buttonChildSize, ImGuiChildFlags_None,
ImGuiWindowFlags_NoScrollbar))
{
auto canUseSelectedItem = true;
auto canUpgradeSelectedItem = is_able_to_upgrade_get(schema.items[selectedItemID], selectedQuantity);
auto rowTwoButtonSize = row_widget_size_get(2);
ImGui::Separator();
ImGui::Dummy(ImVec2(0, ImGui::GetStyle().ItemSpacing.y));
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::BeginDisabled(!canUseSelectedItem);
if (WIDGET_FX(ImGui::Button("Spawn", {ImGui::GetContentRegionAvail().x, 0}))) item_use(selectedItemID);
ImGui::EndDisabled();
ImGui::BeginDisabled(!canUpgradeSelectedItem);
if (WIDGET_FX(ImGui::Button("Upgrade", rowTwoButtonSize))) item_upgrade(selectedItemID, false);
ImGui::SameLine();
if (WIDGET_FX(ImGui::Button("Upgrade All", rowTwoButtonSize))) item_upgrade(selectedItemID, true);
ImGui::EndDisabled();
ImGui::PopFont();
}
if (isButtonChildVisible) ImGui::EndChild();
}
if (isInfoVisible) ImGui::EndChild();
}
ImGui::EndChild();
}
int Inventory::count()
{
int count{};
for (auto& [type, quantity] : values)
count += quantity;
return count;
}
}

View File

@@ -8,7 +8,7 @@
#include <imgui.h> #include <imgui.h>
namespace game::state::main namespace game::state::play
{ {
class Inventory class Inventory
{ {
@@ -19,6 +19,7 @@ namespace game::state::main
std::unordered_map<int, entity::Actor> actors{}; std::unordered_map<int, entity::Actor> actors{};
std::unordered_map<int, glm::vec4> rects{}; std::unordered_map<int, glm::vec4> rects{};
std::unordered_map<int, Canvas> canvases{}; std::unordered_map<int, Canvas> canvases{};
int selectedItemID{-1};
void tick(); void tick();
void update(Resources&, ItemManager&, entity::Character&); void update(Resources&, ItemManager&, entity::Character&);

View File

@@ -13,7 +13,7 @@ using namespace game::resource;
using namespace game::util; using namespace game::util;
using namespace glm; using namespace glm;
namespace game::state::main namespace game::state::play
{ {
void ItemManager::update(entity::Character& character, entity::Cursor& cursor, AreaManager& areaManager, Text& text, void ItemManager::update(entity::Character& character, entity::Cursor& cursor, AreaManager& areaManager, Text& text,
const glm::vec4& bounds, Canvas& canvas) const glm::vec4& bounds, Canvas& canvas)

View File

@@ -7,7 +7,7 @@
#include "area_manager.hpp" #include "area_manager.hpp"
#include "text.hpp" #include "text.hpp"
namespace game::state::main namespace game::state::play
{ {
class ItemManager class ItemManager
{ {

View File

@@ -1,6 +1,7 @@
#include "menu.hpp" #include "menu.hpp"
#include "../../util/imgui.hpp" #include "../../util/imgui.hpp"
#include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp" #include "../../util/imgui/widget.hpp"
#include <algorithm> #include <algorithm>
@@ -8,12 +9,17 @@
using namespace game::util; using namespace game::util;
using namespace game::util::imgui; using namespace game::util::imgui;
namespace game::state::main namespace game::state::play
{ {
void Menu::tick() void Menu::tick()
{ {
inventory.tick(); inventory.tick();
play.tick(); skillCheck.tick();
}
void Menu::color_set_check(Resources& resources, entity::Character& character)
{
imgui::style::color_set(resources.settings.isUseCharacterColor ? character.data.color : resources.settings.color);
} }
void Menu::update(Resources& resources, ItemManager& itemManager, entity::Character& character, void Menu::update(Resources& resources, ItemManager& itemManager, entity::Character& character,
@@ -61,13 +67,13 @@ namespace game::state::main
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (WIDGET_FX(ImGui::BeginTabItem("Play"))) if (WIDGET_FX(ImGui::BeginTabItem("Arcade")))
{ {
play.update(resources, character, inventory, text); skillCheck.update(resources, character, inventory, text);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (WIDGET_FX(ImGui::BeginTabItem("Items"))) if (WIDGET_FX(ImGui::BeginTabItem("Inventory")))
{ {
inventory.update(resources, itemManager, character); inventory.update(resources, itemManager, character);
ImGui::EndTabItem(); ImGui::EndTabItem();
@@ -75,13 +81,14 @@ namespace game::state::main
if (WIDGET_FX(ImGui::BeginTabItem("Stats"))) if (WIDGET_FX(ImGui::BeginTabItem("Stats")))
{ {
stats.update(resources, play, character); stats.update(resources, skillCheck, character);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (WIDGET_FX(ImGui::BeginTabItem("Settings"))) if (WIDGET_FX(ImGui::BeginTabItem("Settings")))
{ {
configuration.update(resources, Configuration::MAIN); settingsMenu.update(resources, SettingsMenu::PLAY);
if (settingsMenu.isJustColorSet) color_set_check(resources, character);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
@@ -91,11 +98,13 @@ namespace game::state::main
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (isDebug && WIDGET_FX(ImGui::BeginTabItem("Debug"))) #if DEBUG
if (WIDGET_FX(ImGui::BeginTabItem("Debug")))
{ {
debug.update(character, cursor, itemManager, canvas); debug.update(character, cursor, itemManager, canvas);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
#endif
} }
ImGui::EndTabBar(); ImGui::EndTabBar();
} }

View File

@@ -2,37 +2,35 @@
#include <imgui.h> #include <imgui.h>
#include "../configuration.hpp" #include "../settings_menu.hpp"
#include "arcade/skill_check.hpp"
#include "chat.hpp" #include "chat.hpp"
#include "cheats.hpp" #include "cheats.hpp"
#include "debug.hpp" #include "debug.hpp"
#include "play.hpp"
#include "stats.hpp" #include "stats.hpp"
#include "text.hpp" #include "text.hpp"
#include "../../util/imgui/window_slide.hpp" #include "../../util/imgui/window_slide.hpp"
namespace game::state::main namespace game::state::play
{ {
class Menu class Menu
{ {
public: public:
Play play; SkillCheck skillCheck;
Chat chat; Chat chat;
Cheats cheats; Cheats cheats;
Debug debug; Debug debug;
Stats stats; Stats stats;
Inventory inventory; Inventory inventory;
state::Configuration configuration; state::SettingsMenu settingsMenu;
#if DEBUG #if DEBUG
bool isCheats{true};
#elif
bool isCheats{}; bool isCheats{};
bool isDebug{true};
#else
bool isCheats{};
bool isDebug{};
#endif #endif
bool isOpen{true}; bool isOpen{true};
@@ -41,5 +39,6 @@ namespace game::state::main
void tick(); void tick();
void update(Resources&, ItemManager&, entity::Character&, entity::Cursor&, Text&, Canvas&); void update(Resources&, ItemManager&, entity::Character&, entity::Cursor&, Text&, Canvas&);
void color_set_check(Resources&, entity::Character&);
}; };
} }

View File

@@ -7,9 +7,9 @@
using namespace game::resource; using namespace game::resource;
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
void Stats::update(Resources& resources, Play& play, entity::Character& character) void Stats::update(Resources& resources, SkillCheck& skillCheck, entity::Character& character)
{ {
ImGui::PushFont(ImGui::GetFont(), Font::BIG); ImGui::PushFont(ImGui::GetFont(), Font::BIG);
ImGui::TextUnformatted(character.data.name.c_str()); ImGui::TextUnformatted(character.data.name.c_str());
@@ -17,7 +17,7 @@ namespace game::state::main
ImGui::Separator(); ImGui::Separator();
auto& playSchema = character.data.playSchema; auto& skillCheckSchema = character.data.skillCheckSchema;
auto& system = resources.settings.measurementSystem; auto& system = resources.settings.measurementSystem;
auto weight = character.weight_get(system); auto weight = character.weight_get(system);
auto weightUnit = system == measurement::IMPERIAL ? "lbs" : "kg"; auto weightUnit = system == measurement::IMPERIAL ? "lbs" : "kg";
@@ -32,17 +32,17 @@ namespace game::state::main
ImGui::Text("Total Calories Consumed: %0.0f kcal", character.totalCaloriesConsumed); ImGui::Text("Total Calories Consumed: %0.0f kcal", character.totalCaloriesConsumed);
ImGui::Text("Total Food Items Eaten: %i", character.totalFoodItemsEaten); ImGui::Text("Total Food Items Eaten: %i", character.totalFoodItemsEaten);
ImGui::SeparatorText("Play"); ImGui::SeparatorText("Skill Check");
ImGui::Text("Best: %i pts (%ix)", play.highScore, play.bestCombo); ImGui::Text("Best: %i pts (%ix)", skillCheck.highScore, skillCheck.bestCombo);
ImGui::Text("Total Plays: %i", play.totalPlays); ImGui::Text("Total Skill Checks: %i", skillCheck.totalPlays);
for (int i = 0; i < (int)playSchema.grades.size(); i++) for (int i = 0; i < (int)skillCheckSchema.grades.size(); i++)
{ {
auto& grade = playSchema.grades[i]; auto& grade = skillCheckSchema.grades[i];
ImGui::Text("%s: %i", grade.namePlural.c_str(), play.gradeCounts[i]); ImGui::Text("%s: %i", grade.namePlural.c_str(), skillCheck.gradeCounts[i]);
} }
ImGui::Text("Accuracy: %0.2f%%", play.accuracy_score_get(character)); ImGui::Text("Accuracy: %0.2f%%", skillCheck.accuracy_score_get(character));
} }
} }

View File

@@ -3,15 +3,15 @@
#include "../../entity/character.hpp" #include "../../entity/character.hpp"
#include "../../resources.hpp" #include "../../resources.hpp"
#include "play.hpp" #include "arcade/skill_check.hpp"
#include <imgui.h> #include <imgui.h>
namespace game::state::main namespace game::state::play
{ {
class Stats class Stats
{ {
public: public:
void update(Resources&, Play&, entity::Character&); void update(Resources&, SkillCheck&, entity::Character&);
}; };
} }

View File

@@ -12,7 +12,7 @@
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
const char* utf8_advance_chars(const char* text, const char* end, int count) const char* utf8_advance_chars(const char* text, const char* end, int count)
{ {

View File

@@ -6,7 +6,7 @@
#include "../../resources.hpp" #include "../../resources.hpp"
namespace game::state::main namespace game::state::play
{ {
class Text class Text
{ {

View File

@@ -3,7 +3,7 @@
#include <imgui.h> #include <imgui.h>
#include <ranges> #include <ranges>
namespace game::state::main namespace game::state::play
{ {
void Toasts::tick() void Toasts::tick()
{ {

View File

@@ -3,7 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace game::state::main namespace game::state::play
{ {
class Toasts class Toasts
{ {

View File

@@ -8,7 +8,7 @@
using namespace game::util; using namespace game::util;
using namespace game::util::imgui; using namespace game::util::imgui;
namespace game::state::main namespace game::state::play
{ {
void Tools::update(entity::Character& character, entity::Cursor& cursor, World& world, World::Focus focus, void Tools::update(entity::Character& character, entity::Cursor& cursor, World& world, World::Focus focus,
Canvas& canvas) Canvas& canvas)
@@ -47,21 +47,20 @@ namespace game::state::main
{ {
auto buttonSize = imgui::to_imvec2(vec2(ImGui::GetContentRegionAvail().x)); auto buttonSize = imgui::to_imvec2(vec2(ImGui::GetContentRegionAvail().x));
auto cursor_mode_button = [&](const char* name, InteractType mode) auto cursor_mode_button = [&](const std::string& name, int interactTypeID)
{ {
auto isMode = cursor.mode == mode; auto isMode = cursor.interactTypeID == interactTypeID;
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::PushStyleColor(ImGuiCol_Button,
ImGui::GetStyleColorVec4(isMode ? ImGuiCol_ButtonHovered : ImGuiCol_Button)); ImGui::GetStyleColorVec4(isMode ? ImGuiCol_ButtonHovered : ImGuiCol_Button));
if (WIDGET_FX(ImGui::Button(name, buttonSize))) cursor.mode = mode; if (WIDGET_FX(ImGui::Button(name.c_str(), buttonSize))) cursor.interactTypeID = interactTypeID;
ImGui::PopStyleColor(); ImGui::PopStyleColor();
}; };
if (WIDGET_FX(ImGui::Button("Home", buttonSize))) world.character_focus(character, canvas, focus); if (WIDGET_FX(ImGui::Button("Home", buttonSize))) world.character_focus(character, canvas, focus);
ImGui::SetItemTooltip("%s", "Reset camera view.\n(Shortcut: Home)"); ImGui::SetItemTooltip("%s", "Reset camera view.\n(Shortcut: Home)");
cursor_mode_button("Rub", InteractType::RUB); for (int i = 0; i < (int)character.data.interactTypeNames.size(); i++)
cursor_mode_button("Kiss", InteractType::KISS); cursor_mode_button(character.data.interactTypeNames[i], i);
cursor_mode_button("Smack", InteractType::SMACK);
} }
ImGui::End(); ImGui::End();
} }

View File

@@ -5,7 +5,7 @@
#include "../../util/imgui/window_slide.hpp" #include "../../util/imgui/window_slide.hpp"
#include "world.hpp" #include "world.hpp"
namespace game::state::main namespace game::state::play
{ {
class Tools class Tools
{ {

View File

@@ -8,7 +8,7 @@
using namespace game::util; using namespace game::util;
namespace game::state::main namespace game::state::play
{ {
void World::set(entity::Character& character, Canvas& canvas, Focus focus) void World::set(entity::Character& character, Canvas& canvas, Focus focus)
{ {

View File

@@ -1,12 +1,12 @@
#pragma once #pragma once
#include "../../canvas.hpp" #include "../../render/canvas.hpp"
#include "../../entity/character.hpp" #include "../../entity/character.hpp"
#include "character_manager.hpp" #include "character_manager.hpp"
#include "item_manager.hpp" #include "item_manager.hpp"
namespace game::state::main namespace game::state::play
{ {
class World class World
{ {

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "../canvas.hpp" #include "../render/canvas.hpp"
#include "select/characters.hpp" #include "select/characters.hpp"
#include "select/info.hpp" #include "select/info.hpp"
@@ -21,4 +21,4 @@ namespace game::state
void update(Resources&); void update(Resources&);
void render(Resources&, Canvas&); void render(Resources&, Canvas&);
}; };
}; };

View File

@@ -1,10 +1,9 @@
#include "characters.hpp" #include "characters.hpp"
#include <ranges> #include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp" #include "../../util/imgui/widget.hpp"
using namespace game::util::imgui; using namespace game::util;
namespace game::state::select namespace game::state::select
{ {
@@ -39,6 +38,7 @@ namespace game::state::select
ImGui::PushID(i); ImGui::PushID(i);
ImGui::SetCursorPos(cursorPos); ImGui::SetCursorPos(cursorPos);
imgui::style::color_set(character.color);
auto isSelected = i == characterIndex; auto isSelected = i == characterIndex;
@@ -64,13 +64,14 @@ namespace game::state::select
} }
ImGui::PopID(); ImGui::PopID();
imgui::style::color_set(resources.settings.color);
} }
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (WIDGET_FX(ImGui::BeginTabItem("Configuration"))) if (WIDGET_FX(ImGui::BeginTabItem("Settings")))
{ {
configuration.update(resources); settingsMenu.update(resources);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
ImGui::EndTabBar(); ImGui::EndTabBar();

View File

@@ -1,15 +1,15 @@
#pragma once #pragma once
#include "../../resources.hpp" #include "../../resources.hpp"
#include "../configuration.hpp" #include "../settings_menu.hpp"
namespace game::state::select namespace game::state::select
{ {
class Characters class Characters
{ {
public: public:
Configuration configuration; SettingsMenu settingsMenu;
void update(Resources&, int& characterIndex); void update(Resources&, int& characterIndex);
}; };
} }

View File

@@ -2,6 +2,7 @@
#include "../../util/color.hpp" #include "../../util/color.hpp"
#include "../../util/imgui.hpp" #include "../../util/imgui.hpp"
#include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp" #include "../../util/imgui/widget.hpp"
#include "../../util/vector.hpp" #include "../../util/vector.hpp"
@@ -18,6 +19,7 @@ namespace game::state::select
auto& style = ImGui::GetStyle(); auto& style = ImGui::GetStyle();
auto viewport = ImGui::GetMainViewport(); auto viewport = ImGui::GetMainViewport();
auto& character = resources.characterPreviews[characterIndex];
auto size = ImVec2(viewport->Size.x / 2.0f - (style.WindowPadding.x * 2.0f), auto size = ImVec2(viewport->Size.x / 2.0f - (style.WindowPadding.x * 2.0f),
(viewport->Size.y / 2.0f) - (style.WindowPadding.y * 2.0f)); (viewport->Size.y / 2.0f) - (style.WindowPadding.y * 2.0f));
@@ -25,12 +27,12 @@ namespace game::state::select
ImGui::SetNextWindowSize(size); ImGui::SetNextWindowSize(size);
ImGui::SetNextWindowPos(pos); ImGui::SetNextWindowPos(pos);
imgui::style::color_set(character.color);
if (ImGui::Begin("##Info", nullptr, if (ImGui::Begin("##Info", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar)) ImGuiWindowFlags_NoTitleBar))
{ {
auto& character = resources.characterPreviews[characterIndex];
auto& save = character.save; auto& save = character.save;
auto& system = resources.settings.measurementSystem; auto& system = resources.settings.measurementSystem;
auto& weight = save.is_valid() ? save.weight : character.weight; auto& weight = save.is_valid() ? save.weight : character.weight;
@@ -123,5 +125,6 @@ namespace game::state::select
} }
} }
ImGui::End(); ImGui::End();
imgui::style::color_set(resources.settings.color);
} }
} }

View File

@@ -4,6 +4,7 @@
#include <cmath> #include <cmath>
#include "../../util/imgui.hpp" #include "../../util/imgui.hpp"
#include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp" #include "../../util/imgui/widget.hpp"
#include "../../util/vector.hpp" #include "../../util/vector.hpp"
@@ -25,13 +26,14 @@ namespace game::state::select
auto& style = ImGui::GetStyle(); auto& style = ImGui::GetStyle();
auto viewport = ImGui::GetMainViewport(); auto viewport = ImGui::GetMainViewport();
auto& character = resources.characterPreviews[characterIndex];
auto size = ImVec2(viewport->Size.x / 2.0f - (style.WindowPadding.x * 2.0f), auto size = ImVec2(viewport->Size.x / 2.0f - (style.WindowPadding.x * 2.0f),
(viewport->Size.y / 2.0f) - (style.WindowPadding.y * 2.0f)); (viewport->Size.y / 2.0f) - (style.WindowPadding.y * 2.0f));
auto pos = ImVec2(style.WindowPadding.x, style.WindowPadding.y); auto pos = ImVec2(style.WindowPadding.x, style.WindowPadding.y);
ImGui::SetNextWindowSize(size); ImGui::SetNextWindowSize(size);
ImGui::SetNextWindowPos(pos); ImGui::SetNextWindowPos(pos);
imgui::style::color_set(character.color);
if (ImGui::Begin("##Preview", nullptr, if (ImGui::Begin("##Preview", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
@@ -39,8 +41,6 @@ namespace game::state::select
{ {
if (ImGui::BeginTabBar("##Preview Tab Bar")) if (ImGui::BeginTabBar("##Preview Tab Bar"))
{ {
auto& character = resources.characterPreviews[characterIndex];
auto available = ImGui::GetContentRegionAvail(); auto available = ImGui::GetContentRegionAvail();
auto availableSize = imgui::to_vec2(available); auto availableSize = imgui::to_vec2(available);
auto textureSize = vec2(character.render.size); auto textureSize = vec2(character.render.size);
@@ -106,5 +106,6 @@ namespace game::state::select
} }
} }
ImGui::End(); ImGui::End();
imgui::style::color_set(resources.settings.color);
} }
} }

View File

@@ -1,4 +1,4 @@
#include "configuration.hpp" #include "settings_menu.hpp"
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <imgui.h> #include <imgui.h>
@@ -13,13 +13,15 @@ using namespace game::util::imgui;
namespace game::state namespace game::state
{ {
void Configuration::update(Resources& resources, Mode mode) void SettingsMenu::update(Resources& resources, Mode mode)
{ {
auto& settings = resources.settings; auto& settings = resources.settings;
auto& measurementSystem = settings.measurementSystem; auto& measurementSystem = settings.measurementSystem;
auto& volume = settings.volume; auto& volume = settings.volume;
auto& color = settings.color; auto& color = settings.color;
isJustColorSet = false;
ImGui::SeparatorText("Measurement System"); ImGui::SeparatorText("Measurement System");
WIDGET_FX(ImGui::RadioButton("Metric", (int*)&measurementSystem, measurement::METRIC)); WIDGET_FX(ImGui::RadioButton("Metric", (int*)&measurementSystem, measurement::METRIC));
ImGui::SetItemTooltip("%s", "Use kilograms (kg)."); ImGui::SetItemTooltip("%s", "Use kilograms (kg).");
@@ -34,10 +36,18 @@ namespace game::state
ImGui::SeparatorText("Appearance"); ImGui::SeparatorText("Appearance");
if (WIDGET_FX(ImGui::Checkbox("Use Character Color", &settings.isUseCharacterColor))) isJustColorSet = true;
ImGui::SetItemTooltip("When playing, the UI will use the character's preset UI color.");
ImGui::SameLine();
ImGui::BeginDisabled(settings.isUseCharacterColor);
if (WIDGET_FX( if (WIDGET_FX(
ImGui::ColorEdit3("Color", value_ptr(color), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip))) ImGui::ColorEdit3("Color", value_ptr(color), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip)))
{
style::color_set(color); style::color_set(color);
isJustColorSet = true;
}
ImGui::SetItemTooltip("%s", "Change the UI color."); ImGui::SetItemTooltip("%s", "Change the UI color.");
ImGui::EndDisabled();
ImGui::Separator(); ImGui::Separator();
if (WIDGET_FX(ImGui::Button("Reset to Default", ImVec2(-FLT_MIN, 0)))) if (WIDGET_FX(ImGui::Button("Reset to Default", ImVec2(-FLT_MIN, 0))))
@@ -46,7 +56,7 @@ namespace game::state
style::color_set(settings.color); style::color_set(settings.color);
} }
if (mode == MAIN) if (mode == PLAY)
{ {
ImGui::Separator(); ImGui::Separator();

View File

@@ -4,18 +4,19 @@
namespace game::state namespace game::state
{ {
class Configuration class SettingsMenu
{ {
public: public:
enum Mode enum Mode
{ {
SELECT, SELECT,
MAIN PLAY
}; };
bool isGoToSelect{}; bool isGoToSelect{};
bool isSave{}; bool isSave{};
bool isJustColorSet{};
void update(Resources&, Mode = SELECT); void update(Resources&, Mode = SELECT);
}; };
} }

View File

@@ -1,24 +0,0 @@
#pragma once
namespace game
{
#define LIST \
X(RUB, "Rub") \
X(KISS, "Kiss") \
X(SMACK, "Smack")
enum InteractType
{
#define X(symbol, string) symbol,
LIST
#undef X
};
static constexpr const char* INTERACT_TYPE_STRINGS[] = {
#define X(symbol, string) string,
LIST
#undef X
};
#undef LIST
}