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
include/*.cpp
src/*.cpp
src/render/*.cpp
src/resource/*.cpp
src/resource/xml/*.cpp
src/state/*.cpp
src/state/main/*.cpp
src/state/play/*.cpp
src/state/play/arcade/*.cpp
src/state/select/*.cpp
src/entity/*.cpp
src/window/*.cpp

View File

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

View File

@@ -335,12 +335,14 @@ namespace game::entity
void Character::queue_play(QueuedPlay play)
{
if (isStageUp) return;
queuedPlay = play;
queuedPlay.animation = animation_name_convert(queuedPlay.animation);
}
void Character::queue_idle_animation()
{
if (isStageUp) return;
if (data.animations.idle.empty()) return;
queue_play(
{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)
{
if (isStageUp) return;
if (interactArea.animation.empty()) return;
queue_play({is_over_capacity() && !interactArea.animationFull.empty() ? interactArea.animationFull
: interactArea.animation});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -142,7 +142,7 @@ namespace game::resource::xml
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);
std::string categoryString{};
@@ -173,7 +173,7 @@ namespace game::resource::xml
{
auto& item = items[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++)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
#include "play.hpp"
#include "skill_check.hpp"
#include "../../log.hpp"
#include "util.hpp"
@@ -10,7 +10,7 @@ using namespace game::util;
namespace game::resource::xml
{
Play::Play(const physfs::Path& path, Dialogue& dialogue)
SkillCheck::SkillCheck(const physfs::Path& path, Dialogue& dialogue)
{
XMLDocument document;
@@ -62,8 +62,8 @@ namespace game::resource::xml
}
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
{
class Play
class SkillCheck
{
public:
struct Grade
@@ -48,8 +48,8 @@ namespace game::resource::xml
bool isValid{};
Play() = default;
Play(const util::physfs::Path&, Dialogue&);
SkillCheck() = default;
SkillCheck(const util::physfs::Path&, Dialogue&);
bool is_valid() const;
};

View File

@@ -12,6 +12,8 @@ using namespace game::util;
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)
{
const char* temp = nullptr;
@@ -45,6 +47,60 @@ namespace game::resource::xml
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)
{
value.emplace();
@@ -84,102 +140,139 @@ namespace game::resource::xml
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{};
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))
{
eventID = anm2.eventMap.at(string);
return XML_SUCCESS;
}
else
{
logger.error(std::format("Could not query anm2 event ID: {} ({})", string, anm2.path));
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{};
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))
{
layerID = anm2.layerMap.at(string);
return XML_SUCCESS;
}
else
{
logger.error(std::format("Could not query anm2 layer ID: {} ({})", string, anm2.path));
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{};
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))
{
nullID = anm2.nullMap.at(string);
return XML_SUCCESS;
}
else
{
logger.error(std::format("Could not query anm2 null ID: {} ({})", string, anm2.path));
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)
{
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);
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)
{
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));
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)
{
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));
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)
{
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));
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);
element->QueryFloatAttribute("Weight", &animationEntry.weight);
auto result = query_string_attribute(element, "Animation", &animationEntry.animation);
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)
{
auto result = XML_SUCCESS;
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)
{
query_sound(element, attributeName.c_str(), archive, rootPath, soundEntry.sound);
element->QueryFloatAttribute("Weight", &soundEntry.weight);
auto result = query_sound(element, attributeName.c_str(), archive, rootPath, soundEntry.sound);
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& attributeName)
{
auto result = XML_SUCCESS;
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 <string>
#include <glm/glm.hpp>
#include <tinyxml2.h>
#include "animation_entry.hpp"
@@ -19,32 +20,42 @@ namespace game::resource::xml
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_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,
std::optional<float>& value);
tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
std::optional<int>& value);
void 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);
void query_null_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& nullID);
tinyxml2::XMLError query_event_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& eventID);
tinyxml2::XMLError query_layer_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& layerID);
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 = {});
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);
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);
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);
void query_animation_entry(tinyxml2::XMLElement* element, AnimationEntry& animationEntry);
void query_animation_entry_collection(tinyxml2::XMLElement* element, const char* name,
tinyxml2::XMLError query_animation_entry(tinyxml2::XMLElement* element, AnimationEntry& animationEntry);
tinyxml2::XMLError query_animation_entry_collection(tinyxml2::XMLElement* element, const char* name,
AnimationEntryCollection& animationEntryCollection);
void query_sound_entry(tinyxml2::XMLElement* element, const std::string& archive, const std::string& rootPath,
SoundEntry& soundEntry, const std::string& attributeName = "Sound");
void query_sound_entry_collection(tinyxml2::XMLElement* element, const char* name, const std::string& archive,
const std::string& rootPath, SoundEntryCollection& soundEntryCollection,
tinyxml2::XMLError query_sound_entry(tinyxml2::XMLElement* element, const std::string& archive,
const std::string& rootPath, SoundEntry& soundEntry,
const std::string& attributeName = "Sound");
tinyxml2::XMLError query_sound_entry_collection(tinyxml2::XMLElement* element, const char* name,
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&);

View File

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

View File

@@ -2,10 +2,10 @@
#include <SDL3/SDL.h>
#include "canvas.hpp"
#include "render/canvas.hpp"
#include "resources.hpp"
#include "state/main.hpp"
#include "state/play.hpp"
#include "state/select.hpp"
#include "entity/cursor.hpp"
@@ -22,7 +22,7 @@ namespace game
enum Type
{
MAIN,
PLAY,
SELECT
};
@@ -30,7 +30,7 @@ namespace game
Resources resources;
state::Main main;
state::Play play;
state::Select select;
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 <glm/glm.hpp>
@@ -12,12 +12,12 @@
using namespace game::resource;
using namespace game::util;
using namespace game::state::main;
using namespace game::state::play;
using namespace glm;
namespace game::state
{
World::Focus Main::focus_get()
World::Focus Play::focus_get()
{
if (!isWindows) return World::CENTER;
@@ -27,7 +27,7 @@ namespace game::state
: 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& saveData = data.save;
@@ -35,8 +35,8 @@ namespace game::state
auto& dialogue = data.dialogue;
auto& menuSchema = data.menuSchema;
this->characterIndex = selectedCharacterIndex;
konamiCodeIndex = 0;
konamiCodeStartTime = 0.0;
cheatCodeIndex = 0;
cheatCodeStartTime = 0.0;
character =
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{};
cursor = entity::Cursor(character.data.cursorSchema.anm2);
cursor.interactTypeID = character.data.interactTypeNames.empty() ? -1 : 0;
menu.inventory = Inventory{};
for (auto& [id, quantity] : saveData.inventory)
@@ -79,14 +80,14 @@ namespace game::state
imgui::style::rounding_set(menuSchema.rounding);
imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select);
menu.color_set_check(resources, character);
menu.play = Play(character);
menu.play.totalPlays = saveData.totalPlays;
menu.play.highScore = saveData.highScore;
menu.play.bestCombo = saveData.bestCombo;
menu.play.gradeCounts = saveData.gradeCounts;
menu.play.isHighScoreAchieved = saveData.highScore > 0 ? true : false;
menu.skillCheck = SkillCheck(character);
menu.skillCheck.totalPlays = saveData.totalPlays;
menu.skillCheck.highScore = saveData.highScore;
menu.skillCheck.bestCombo = saveData.bestCombo;
menu.skillCheck.gradeCounts = saveData.gradeCounts;
menu.skillCheck.isHighScoreAchieved = saveData.highScore > 0 ? true : false;
menu.isChat = character.data.dialogue.help.is_valid() || character.data.dialogue.random.is_valid();
text.entry = nullptr;
@@ -114,17 +115,31 @@ namespace game::state
isStartBegin = 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::widget::sounds_set(nullptr, nullptr);
ImGui::GetIO().FontDefault = resources.font.get();
save(resources);
}
void Main::tick(Resources&)
void Play::tick(Resources&)
{
character.tick();
cursor.tick();
@@ -136,54 +151,54 @@ namespace game::state
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_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};
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& dialogue = character.data.dialogue;
if (!menu.isCheats)
{
for (auto key : KONAMI_INPUT_KEYS)
for (auto key : CHEAT_INPUT_KEYS)
{
if (!ImGui::IsKeyPressed(key, false)) continue;
if (key == KONAMI_CODE[konamiCodeIndex])
if (key == CHEAT_CODE[cheatCodeIndex])
{
konamiCodeIndex++;
konamiCodeStartTime = ImGui::GetTime();
cheatCodeIndex++;
cheatCodeStartTime = ImGui::GetTime();
}
else if (key == KONAMI_CODE[0])
else if (key == CHEAT_CODE[0])
{
konamiCodeIndex = 1;
konamiCodeStartTime = ImGui::GetTime();
cheatCodeIndex = 1;
cheatCodeStartTime = ImGui::GetTime();
}
else
{
konamiCodeIndex = 0;
konamiCodeStartTime = 0.0;
cheatCodeIndex = 0;
cheatCodeStartTime = 0.0;
}
if (konamiCodeIndex >= (int)KONAMI_CODE.size())
if (cheatCodeIndex >= (int)CHEAT_CODE.size())
{
menu.isCheats = true;
konamiCodeIndex = 0;
konamiCodeStartTime = 0.0;
cheatCodeIndex = 0;
cheatCodeStartTime = 0.0;
toasts.push("Cheats unlocked!");
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;
konamiCodeStartTime = 0.0;
cheatCodeIndex = 0;
cheatCodeStartTime = 0.0;
}
}
@@ -264,15 +279,15 @@ namespace game::state
cursor.update();
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);
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& rectShader = resources.shaders[shader::RECT];
@@ -309,7 +324,7 @@ namespace game::state
canvas.unbind();
}
void Main::save(Resources& resources)
void Play::save(Resources& resources)
{
resource::xml::Save save;
@@ -323,10 +338,10 @@ namespace game::state
save.digestionTimer = character.digestionTimer;
save.totalCaloriesConsumed = character.totalCaloriesConsumed;
save.totalFoodItemsEaten = character.totalFoodItemsEaten;
save.totalPlays = menu.play.totalPlays;
save.highScore = menu.play.highScore;
save.bestCombo = menu.play.bestCombo;
save.gradeCounts = menu.play.gradeCounts;
save.totalPlays = menu.skillCheck.totalPlays;
save.highScore = menu.skillCheck.highScore;
save.bestCombo = menu.skillCheck.bestCombo;
save.gradeCounts = menu.skillCheck.gradeCounts;
save.isPostgame = isPostgame;
save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE;

View File

@@ -2,19 +2,19 @@
#include "../resources.hpp"
#include "main/area_manager.hpp"
#include "main/character_manager.hpp"
#include "main/info.hpp"
#include "main/item_manager.hpp"
#include "main/menu.hpp"
#include "main/text.hpp"
#include "main/toasts.hpp"
#include "main/tools.hpp"
#include "main/world.hpp"
#include "play/area_manager.hpp"
#include "play/character_manager.hpp"
#include "play/info.hpp"
#include "play/item_manager.hpp"
#include "play/menu.hpp"
#include "play/text.hpp"
#include "play/toasts.hpp"
#include "play/tools.hpp"
#include "play/world.hpp"
namespace game::state
{
class Main
class Play
{
public:
static constexpr auto AUTOSAVE_TIME = 30.0f;
@@ -28,22 +28,22 @@ namespace game::state
entity::Character character;
entity::Cursor cursor;
main::Info info;
main::Menu menu;
main::Tools tools;
main::Text text;
main::World world;
main::Toasts toasts;
main::ItemManager itemManager{};
main::CharacterManager characterManager{};
main::AreaManager areaManager{};
play::Info info;
play::Menu menu;
play::Tools tools;
play::Text text;
play::World world;
play::Toasts toasts;
play::ItemManager itemManager{};
play::CharacterManager characterManager{};
play::AreaManager areaManager{};
int characterIndex{};
int areaIndex{};
float autosaveTime{};
int konamiCodeIndex{};
double konamiCodeStartTime{};
int cheatCodeIndex{};
double cheatCodeStartTime{};
bool isWindows{true};
@@ -57,15 +57,15 @@ namespace game::state
bool isPostgame{};
Canvas worldCanvas{main::World::SIZE};
Canvas worldCanvas{play::World::SIZE};
Main() = default;
Play() = default;
void set(Resources&, int characterIndex, Game = CONTINUE);
void exit(Resources& resources);
void update(Resources&);
void tick(Resources&);
void render(Resources&, Canvas&);
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 "../../util/imgui.hpp"
#include "../../util/imgui/widget.hpp"
#include "../../util/math.hpp"
#include "../../../util/imgui.hpp"
#include "../../../util/imgui/widget.hpp"
#include "../../../util/math.hpp"
#include <cmath>
#include <format>
@@ -15,13 +15,13 @@ using namespace game::entity;
using namespace game::resource;
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;
auto& schema = character.data.playSchema;
auto& schema = character.data.skillCheckSchema;
float combinedWeight{};
@@ -34,9 +34,9 @@ namespace game::state::main
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;
@@ -61,15 +61,15 @@ namespace game::state::main
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)
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 ImVec4 LINE_COLOR = ImVec4(1, 1, 1, 1);
@@ -80,7 +80,7 @@ namespace game::state::main
static constexpr auto ITEM_FALL_GRAVITY = 2400.0f;
auto& dialogue = character.data.dialogue;
auto& schema = character.data.playSchema;
auto& schema = character.data.skillCheckSchema;
auto& itemSchema = character.data.itemSchema;
auto& style = ImGui::GetStyle();
auto drawList = ImGui::GetWindowDrawList();
@@ -202,7 +202,7 @@ namespace game::state::main
auto barButtonSize = ImVec2(barMax.x - barMin.x, barMax.y - barMin.y);
if (ImGui::IsKeyPressed(ImGuiKey_Space) ||
WIDGET_FX(ImGui::InvisibleButton("##PlayBar", barButtonSize, ImGuiButtonFlags_PressedOnClick)))
WIDGET_FX(ImGui::InvisibleButton("##SkillCheckBar", barButtonSize, ImGuiButtonFlags_PressedOnClick)))
{
int gradeID{};
@@ -234,7 +234,7 @@ namespace game::state::main
schema.sounds.rewardScore.play();
isRewardScoreAchieved = true;
for (auto& itemID : itemSchema.rewardItemPool)
for (auto& itemID : itemSchema.skillCheckRewardItemPool)
{
inventory.values[itemID]++;
if (!itemActors.contains(itemID))

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@
using namespace game::resource::xml;
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)
{
@@ -36,12 +36,19 @@ namespace game::state::main
isInteractingPrevious = isInteracting;
isHoveringPrevious = isHovering;
isHoldInteractingPrevious = isHoldInteracting;
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});
if (cursor.mode == RUB && character.queuedPlay.empty()) character.queue_idle_animation();
isJustStoppedInteracting = false;
}
@@ -58,7 +65,7 @@ namespace game::state::main
auto rect = character.null_frame_rect(interactArea.nullID);
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.queue_play({interactArea.animationCursorHover});
@@ -68,8 +75,8 @@ namespace game::state::main
if (isMouseLeftClick)
{
isInteracting = true;
isHoldInteracting = interactArea.isHold;
interactArea.sound.play();
lastInteractType = cursor.mode;
if (interactArea.digestionBonusClick > 0 && character.calories > 0 && !character.isDigesting)
character.digestionProgress += interactArea.digestionBonusClick;
@@ -105,11 +112,13 @@ namespace game::state::main
isImguiCaptureMouse)
{
isInteracting = false;
isHoldInteracting = false;
interactAreaID = -1;
}
}
if (isInteracting != isInteractingPrevious && !isInteracting) isJustStoppedInteracting = true;
if (isHoldInteracting != isHoldInteractingPrevious && !isHoldInteracting) isJustStoppedHoldInteracting = true;
if (isHovering != isHoveringPrevious && !isHovering) isJustStoppedHovering = true;
cursorWorldPositionPrevious = cursorWorldPosition;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
#include "menu.hpp"
#include "../../util/imgui.hpp"
#include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp"
#include <algorithm>
@@ -8,12 +9,17 @@
using namespace game::util;
using namespace game::util::imgui;
namespace game::state::main
namespace game::state::play
{
void Menu::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,
@@ -61,13 +67,13 @@ namespace game::state::main
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();
}
if (WIDGET_FX(ImGui::BeginTabItem("Items")))
if (WIDGET_FX(ImGui::BeginTabItem("Inventory")))
{
inventory.update(resources, itemManager, character);
ImGui::EndTabItem();
@@ -75,13 +81,14 @@ namespace game::state::main
if (WIDGET_FX(ImGui::BeginTabItem("Stats")))
{
stats.update(resources, play, character);
stats.update(resources, skillCheck, character);
ImGui::EndTabItem();
}
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();
}
@@ -91,11 +98,13 @@ namespace game::state::main
ImGui::EndTabItem();
}
if (isDebug && WIDGET_FX(ImGui::BeginTabItem("Debug")))
#if DEBUG
if (WIDGET_FX(ImGui::BeginTabItem("Debug")))
{
debug.update(character, cursor, itemManager, canvas);
ImGui::EndTabItem();
}
#endif
}
ImGui::EndTabBar();
}

View File

@@ -2,37 +2,35 @@
#include <imgui.h>
#include "../configuration.hpp"
#include "../settings_menu.hpp"
#include "arcade/skill_check.hpp"
#include "chat.hpp"
#include "cheats.hpp"
#include "debug.hpp"
#include "play.hpp"
#include "stats.hpp"
#include "text.hpp"
#include "../../util/imgui/window_slide.hpp"
namespace game::state::main
namespace game::state::play
{
class Menu
{
public:
Play play;
SkillCheck skillCheck;
Chat chat;
Cheats cheats;
Debug debug;
Stats stats;
Inventory inventory;
state::Configuration configuration;
state::SettingsMenu settingsMenu;
#if DEBUG
bool isCheats{true};
#elif
bool isCheats{};
bool isDebug{true};
#else
bool isCheats{};
bool isDebug{};
#endif
bool isOpen{true};
@@ -41,5 +39,6 @@ namespace game::state::main
void tick();
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::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::TextUnformatted(character.data.name.c_str());
@@ -17,7 +17,7 @@ namespace game::state::main
ImGui::Separator();
auto& playSchema = character.data.playSchema;
auto& skillCheckSchema = character.data.skillCheckSchema;
auto& system = resources.settings.measurementSystem;
auto weight = character.weight_get(system);
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 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("Total Plays: %i", play.totalPlays);
ImGui::Text("Best: %i pts (%ix)", skillCheck.highScore, skillCheck.bestCombo);
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];
ImGui::Text("%s: %i", grade.namePlural.c_str(), play.gradeCounts[i]);
auto& grade = skillCheckSchema.grades[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 "../../resources.hpp"
#include "play.hpp"
#include "arcade/skill_check.hpp"
#include <imgui.h>
namespace game::state::main
namespace game::state::play
{
class Stats
{
public:
void update(Resources&, Play&, entity::Character&);
void update(Resources&, SkillCheck&, entity::Character&);
};
}

View File

@@ -12,7 +12,7 @@
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)
{

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@
using namespace game::util;
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,
Canvas& canvas)
@@ -47,21 +47,20 @@ namespace game::state::main
{
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::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();
};
if (WIDGET_FX(ImGui::Button("Home", buttonSize))) world.character_focus(character, canvas, focus);
ImGui::SetItemTooltip("%s", "Reset camera view.\n(Shortcut: Home)");
cursor_mode_button("Rub", InteractType::RUB);
cursor_mode_button("Kiss", InteractType::KISS);
cursor_mode_button("Smack", InteractType::SMACK);
for (int i = 0; i < (int)character.data.interactTypeNames.size(); i++)
cursor_mode_button(character.data.interactTypeNames[i], i);
}
ImGui::End();
}

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#pragma once
#include "../canvas.hpp"
#include "../render/canvas.hpp"
#include "select/characters.hpp"
#include "select/info.hpp"

View File

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

View File

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

View File

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

View File

@@ -4,6 +4,7 @@
#include <cmath>
#include "../../util/imgui.hpp"
#include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp"
#include "../../util/vector.hpp"
@@ -25,13 +26,14 @@ namespace game::state::select
auto& style = ImGui::GetStyle();
auto viewport = ImGui::GetMainViewport();
auto& character = resources.characterPreviews[characterIndex];
auto size = ImVec2(viewport->Size.x / 2.0f - (style.WindowPadding.x * 2.0f),
(viewport->Size.y / 2.0f) - (style.WindowPadding.y * 2.0f));
auto pos = ImVec2(style.WindowPadding.x, style.WindowPadding.y);
ImGui::SetNextWindowSize(size);
ImGui::SetNextWindowPos(pos);
imgui::style::color_set(character.color);
if (ImGui::Begin("##Preview", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
@@ -39,8 +41,6 @@ namespace game::state::select
{
if (ImGui::BeginTabBar("##Preview Tab Bar"))
{
auto& character = resources.characterPreviews[characterIndex];
auto available = ImGui::GetContentRegionAvail();
auto availableSize = imgui::to_vec2(available);
auto textureSize = vec2(character.render.size);
@@ -106,5 +106,6 @@ namespace game::state::select
}
}
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 <imgui.h>
@@ -13,13 +13,15 @@ using namespace game::util::imgui;
namespace game::state
{
void Configuration::update(Resources& resources, Mode mode)
void SettingsMenu::update(Resources& resources, Mode mode)
{
auto& settings = resources.settings;
auto& measurementSystem = settings.measurementSystem;
auto& volume = settings.volume;
auto& color = settings.color;
isJustColorSet = false;
ImGui::SeparatorText("Measurement System");
WIDGET_FX(ImGui::RadioButton("Metric", (int*)&measurementSystem, measurement::METRIC));
ImGui::SetItemTooltip("%s", "Use kilograms (kg).");
@@ -34,10 +36,18 @@ namespace game::state
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(
ImGui::ColorEdit3("Color", value_ptr(color), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip)))
{
style::color_set(color);
isJustColorSet = true;
}
ImGui::SetItemTooltip("%s", "Change the UI color.");
ImGui::EndDisabled();
ImGui::Separator();
if (WIDGET_FX(ImGui::Button("Reset to Default", ImVec2(-FLT_MIN, 0))))
@@ -46,7 +56,7 @@ namespace game::state
style::color_set(settings.color);
}
if (mode == MAIN)
if (mode == PLAY)
{
ImGui::Separator();

View File

@@ -4,17 +4,18 @@
namespace game::state
{
class Configuration
class SettingsMenu
{
public:
enum Mode
{
SELECT,
MAIN
PLAY
};
bool isGoToSelect{};
bool isSave{};
bool isJustColorSet{};
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
}