a (not so) mini update
This commit is contained in:
@@ -237,15 +237,23 @@ namespace game::entity
|
||||
|
||||
if (!queuedPlay.empty())
|
||||
{
|
||||
auto& index = animationMap.at(queuedPlay.animation);
|
||||
if (queuedPlay.isPlayAfterAnimation)
|
||||
nextQueuedPlay = queuedPlay;
|
||||
else if ((state == STOPPED || index != animationIndex) && currentQueuedPlay.isInterruptible)
|
||||
if (!animationMap.contains(queuedPlay.animation))
|
||||
{
|
||||
play(queuedPlay.animation, queuedPlay.mode, queuedPlay.time, queuedPlay.speedMultiplier);
|
||||
currentQueuedPlay = queuedPlay;
|
||||
logger.error(std::string("Animation \"" + queuedPlay.animation + "\" does not exist! Unable to play!"));
|
||||
if (!defaultAnimation.empty()) queue_default_animation();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& index = animationMap.at(queuedPlay.animation);
|
||||
if (queuedPlay.isPlayAfterAnimation)
|
||||
nextQueuedPlay = queuedPlay;
|
||||
else if ((state == STOPPED || index != animationIndex) && currentQueuedPlay.isInterruptible)
|
||||
{
|
||||
play(queuedPlay.animation, queuedPlay.mode, queuedPlay.time, queuedPlay.speedMultiplier);
|
||||
currentQueuedPlay = queuedPlay;
|
||||
}
|
||||
queuedPlay = QueuedPlay{};
|
||||
}
|
||||
queuedPlay = QueuedPlay{};
|
||||
}
|
||||
|
||||
auto animation = animation_get();
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace game::entity
|
||||
auto& save = data.save;
|
||||
auto saveIsValid = save.is_valid();
|
||||
|
||||
capacity = saveIsValid ? save.capacity : data.capacity;
|
||||
weight = saveIsValid ? save.weight : data.weight;
|
||||
digestionRate = saveIsValid ? save.digestionRate : data.digestionRate;
|
||||
eatSpeed = saveIsValid ? save.eatSpeed : data.eatSpeed;
|
||||
capacity = saveIsValid ? save.capacity : (double)data.capacity;
|
||||
weight = saveIsValid ? save.weight : (double)data.weight;
|
||||
digestionRate = saveIsValid ? save.digestionRate : (double)data.digestionRate;
|
||||
eatSpeed = saveIsValid ? save.eatSpeed : (double)data.eatSpeed;
|
||||
|
||||
calories = saveIsValid ? save.calories : 0;
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace game::entity
|
||||
|
||||
float Character::weight_get(measurement::System system)
|
||||
{
|
||||
return system == measurement::IMPERIAL ? weight * (float)measurement::KG_TO_LB : weight;
|
||||
return system == measurement::IMPERIAL ? weight * (double)measurement::KG_TO_LB : weight;
|
||||
}
|
||||
|
||||
int Character::stage_from_weight_get(float checkWeight) const
|
||||
@@ -99,7 +99,7 @@ namespace game::entity
|
||||
{
|
||||
if (stageIndex == -1) stageIndex = this->stage;
|
||||
|
||||
float threshold = data.weight;
|
||||
double threshold = data.weight;
|
||||
|
||||
if (!data.stages.empty())
|
||||
{
|
||||
@@ -111,7 +111,7 @@ namespace game::entity
|
||||
threshold = data.stages[stageIndex - 1].threshold;
|
||||
}
|
||||
|
||||
return system == measurement::IMPERIAL ? threshold * (float)measurement::KG_TO_LB : threshold;
|
||||
return system == measurement::IMPERIAL ? threshold * (double)measurement::KG_TO_LB : threshold;
|
||||
}
|
||||
|
||||
float Character::stage_threshold_next_get(measurement::System system) const
|
||||
@@ -122,23 +122,35 @@ namespace game::entity
|
||||
float Character::stage_progress_get()
|
||||
{
|
||||
auto currentStage = stage_get();
|
||||
if (currentStage >= stage_max_get()) return 1.0f;
|
||||
if (currentStage >= stage_max_get()) return 1.0;
|
||||
|
||||
auto currentThreshold = stage_threshold_get(currentStage);
|
||||
auto nextThreshold = stage_threshold_get(currentStage + 1);
|
||||
if (nextThreshold <= currentThreshold) return 1.0f;
|
||||
if (nextThreshold <= currentThreshold) return 1.0;
|
||||
|
||||
return (weight - currentThreshold) / (nextThreshold - currentThreshold);
|
||||
}
|
||||
|
||||
float Character::digestion_rate_get() { return digestionRate * 60; }
|
||||
float Character::digestion_rate_get() { return digestionRate * 60.0; }
|
||||
|
||||
float Character::max_capacity() const { return capacity * data.capacityMaxMultiplier; }
|
||||
bool Character::is_over_capacity() const { return calories > capacity; }
|
||||
bool Character::is_max_capacity() const { return calories >= max_capacity(); }
|
||||
float Character::capacity_percent_get() const { return calories / max_capacity(); }
|
||||
|
||||
std::string Character::animation_name_convert(const std::string& name) { return std::format("{}{}", name, stage); }
|
||||
std::string Character::animation_append_id_get() const
|
||||
{
|
||||
if (stage <= 0) return {};
|
||||
auto stageIndex = stage - 1;
|
||||
if (stageIndex < 0 || stageIndex >= (int)data.stages.size()) return {};
|
||||
return data.stages[stageIndex].animationAppendID;
|
||||
}
|
||||
|
||||
std::string Character::animation_name_convert(const std::string& name)
|
||||
{
|
||||
auto appendID = animation_append_id_get();
|
||||
return appendID.empty() ? name : name + appendID;
|
||||
}
|
||||
void Character::play_convert(const std::string& animation, Mode playMode, float startAtTime,
|
||||
float speedMultiplierValue)
|
||||
{
|
||||
@@ -148,9 +160,8 @@ namespace game::entity
|
||||
void Character::expand_areas_apply()
|
||||
{
|
||||
auto stageProgress = stage_progress_get();
|
||||
auto capacityProgress = isDigesting
|
||||
? (float)calories / max_capacity() * (float)digestionTimer / data.digestionTimerMax
|
||||
: calories / max_capacity();
|
||||
auto capacityProgress = isDigesting ? (double)calories / max_capacity() * (double)digestionTimer / data.digestionTimerMax
|
||||
: calories / max_capacity();
|
||||
|
||||
for (int i = 0; i < (int)data.expandAreas.size(); i++)
|
||||
{
|
||||
@@ -158,8 +169,8 @@ namespace game::entity
|
||||
auto& overrideLayer = overrides[expandAreaOverrideLayerIDs[i]];
|
||||
auto& overrideNull = overrides[expandAreaOverrideNullIDs[i]];
|
||||
|
||||
auto stageScaleAdd = ((expandArea.scaleAdd * stageProgress) * 0.5f);
|
||||
auto capacityScaleAdd = ((expandArea.scaleAdd * capacityProgress) * 0.5f);
|
||||
auto stageScaleAdd = ((double)expandArea.scaleAdd * stageProgress) * 0.5;
|
||||
auto capacityScaleAdd = ((double)expandArea.scaleAdd * capacityProgress) * 0.5;
|
||||
|
||||
auto scaleAdd =
|
||||
glm::clamp(glm::vec2(), glm::vec2(stageScaleAdd + capacityScaleAdd), glm::vec2(expandArea.scaleAdd));
|
||||
@@ -201,20 +212,21 @@ namespace game::entity
|
||||
if (digestionTimer <= 0)
|
||||
{
|
||||
auto increment = calories * data.caloriesToKilogram;
|
||||
auto nextWeight = glm::clamp(weight + increment, data.weight, data.weightMax);
|
||||
|
||||
if (is_over_capacity())
|
||||
{
|
||||
auto capacityMaxCalorieDifference = (calories - capacity);
|
||||
auto overCapacityPercent = capacityMaxCalorieDifference / (max_capacity() - capacity);
|
||||
auto capacityIncrement =
|
||||
(overCapacityPercent * data.capacityIfOverStuffedOnDigestBonus) * capacityMaxCalorieDifference;
|
||||
capacity = glm::clamp(data.capacityMin, capacity + capacityIncrement, data.capacityMax);
|
||||
(double)((overCapacityPercent * data.capacityIfOverStuffedOnDigestBonus) * capacityMaxCalorieDifference);
|
||||
capacity = glm::clamp(capacity + capacityIncrement, (double)data.capacityMin, (double)data.capacityMax);
|
||||
}
|
||||
|
||||
totalCaloriesConsumed += calories;
|
||||
calories = 0;
|
||||
|
||||
if (auto nextStage = stage_from_weight_get(weight + increment); nextStage > stage_from_weight_get(weight))
|
||||
if (auto nextStage = stage_from_weight_get((double)nextWeight); nextStage > stage_from_weight_get(weight))
|
||||
{
|
||||
queuedPlay = QueuedPlay{};
|
||||
nextQueuedPlay = QueuedPlay{};
|
||||
@@ -226,7 +238,7 @@ namespace game::entity
|
||||
else
|
||||
isJustDigested = true;
|
||||
|
||||
weight += increment;
|
||||
weight = (double)nextWeight;
|
||||
|
||||
isDigesting = false;
|
||||
digestionTimer = data.digestionTimerMax;
|
||||
@@ -258,7 +270,7 @@ namespace game::entity
|
||||
auto talk_reset = [&]()
|
||||
{
|
||||
isTalking = false;
|
||||
talkTimer = 0.0f;
|
||||
talkTimer = 0.0;
|
||||
talkOverride.frame = FrameOptional();
|
||||
};
|
||||
|
||||
@@ -277,9 +289,9 @@ namespace game::entity
|
||||
talkOverride.frame.size = frame.size;
|
||||
talkOverride.frame.pivot = frame.pivot;
|
||||
|
||||
talkTimer += 1.0f;
|
||||
talkTimer += 1.0;
|
||||
|
||||
if (talkTimer > animationTalkDurations.at(animationIndex)) talkTimer = 0.0f;
|
||||
if (talkTimer > animationTalkDurations.at(animationIndex)) talkTimer = 0.0;
|
||||
}
|
||||
else
|
||||
talk_reset();
|
||||
@@ -301,7 +313,7 @@ namespace game::entity
|
||||
auto blink_reset = [&]()
|
||||
{
|
||||
isBlinking = false;
|
||||
blinkTimer = 0.0f;
|
||||
blinkTimer = 0.0;
|
||||
blinkOverride.frame = FrameOptional();
|
||||
};
|
||||
|
||||
@@ -320,7 +332,7 @@ namespace game::entity
|
||||
blinkOverride.frame.size = frame.size;
|
||||
blinkOverride.frame.pivot = frame.pivot;
|
||||
|
||||
blinkTimer += 1.0f;
|
||||
blinkTimer += 1.0;
|
||||
|
||||
if (blinkTimer >= blinkDuration) blink_reset();
|
||||
}
|
||||
@@ -348,14 +360,6 @@ namespace game::entity
|
||||
{is_over_capacity() && !data.animations.idleFull.empty() ? data.animations.idleFull : data.animations.idle});
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
void Character::spritesheet_set(SpritesheetType type)
|
||||
{
|
||||
switch (type)
|
||||
|
||||
@@ -85,9 +85,9 @@ namespace game::entity
|
||||
void tick();
|
||||
void play_convert(const std::string&, Mode = PLAY, float time = 0.0f, float speedMultiplier = 1.0f);
|
||||
void queue_idle_animation();
|
||||
void queue_interact_area_animation(resource::xml::Character::InteractArea&);
|
||||
void queue_play(QueuedPlay);
|
||||
|
||||
std::string animation_append_id_get() const;
|
||||
std::string animation_name_convert(const std::string& name);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ using namespace glm;
|
||||
|
||||
namespace game::entity
|
||||
{
|
||||
Item::Item(Anm2 _anm2, glm::ivec2 _position, int _schemaID, int _chewCount, int _animationIndex, glm::vec2 _velocity,
|
||||
Item::Item(Anm2 _anm2, glm::ivec2 _position, int _schemaID, int _durability, int _animationIndex, glm::vec2 _velocity,
|
||||
float _rotation)
|
||||
: Actor(_anm2, _position, SET, 0.0f, _animationIndex), schemaID(_schemaID), chewCount(_chewCount),
|
||||
: Actor(_anm2, _position, SET, 0.0f, _animationIndex), schemaID(_schemaID), durability(_durability),
|
||||
velocity(_velocity)
|
||||
{
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace game::entity
|
||||
|
||||
int schemaID{};
|
||||
int rotationOverrideID{};
|
||||
int chewCount{};
|
||||
int durability{};
|
||||
|
||||
glm::vec2 velocity{};
|
||||
float angularVelocity{};
|
||||
|
||||
Item(resource::xml::Anm2, glm::ivec2 position, int id, int chewCount = 0, int animationIndex = -1,
|
||||
Item(resource::xml::Anm2, glm::ivec2 position, int id, int durability = 0, int animationIndex = -1,
|
||||
glm::vec2 velocity = {}, float rotation = 0.0f);
|
||||
void update();
|
||||
};
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace game
|
||||
|
||||
logger.info("Initializing...");
|
||||
|
||||
if (!PHYSFS_init((argc > 0 && argv && argv[0]) ? argv[0] : "snivy"))
|
||||
if (!PHYSFS_init((argc > 0 && argv && argv[0]) ? argv[0] : "shweets-sim"))
|
||||
{
|
||||
logger.fatal(std::format("Failed to initialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
|
||||
isError = true;
|
||||
@@ -85,12 +85,12 @@ namespace game
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
static constexpr glm::vec2 SIZE = {1600, 900};
|
||||
window = SDL_CreateWindow("Feed Snivy", SIZE.x, SIZE.y, SDL_WINDOW_OPENGL);
|
||||
window = SDL_CreateWindow("Shweet's Sim", SIZE.x, SIZE.y, SDL_WINDOW_OPENGL);
|
||||
#else
|
||||
|
||||
SDL_PropertiesID windowProperties = SDL_CreateProperties();
|
||||
|
||||
SDL_SetStringProperty(windowProperties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Feed Snivy");
|
||||
SDL_SetStringProperty(windowProperties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Shweet's Sim");
|
||||
SDL_SetNumberProperty(windowProperties, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, (long)settings.windowSize.x);
|
||||
SDL_SetNumberProperty(windowProperties, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, (long)settings.windowSize.y);
|
||||
|
||||
|
||||
@@ -14,11 +14,9 @@ namespace game::resource
|
||||
|
||||
public:
|
||||
static constexpr auto NORMAL = 20;
|
||||
static constexpr auto ABOVE_AVERAGE = 24;
|
||||
static constexpr auto BIG = 30;
|
||||
static constexpr auto HEADER_1 = 24;
|
||||
static constexpr auto HEADER_2 = 30;
|
||||
static constexpr auto HEADER_3 = 40;
|
||||
static constexpr auto HEADER_2 = 50;
|
||||
static constexpr auto HEADER_1 = 60;
|
||||
|
||||
Font() = default;
|
||||
Font(const std::filesystem::path&, float = NORMAL);
|
||||
|
||||
@@ -43,28 +43,31 @@ namespace game::resource::xml
|
||||
|
||||
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
|
||||
|
||||
root->QueryFloatAttribute("Weight", &weight);
|
||||
root->QueryDoubleAttribute("Weight", &weight);
|
||||
root->QueryDoubleAttribute("WeightMax", &weightMax);
|
||||
|
||||
root->QueryFloatAttribute("Capacity", &capacity);
|
||||
root->QueryFloatAttribute("CapacityMin", &capacityMin);
|
||||
root->QueryFloatAttribute("CapacityMax", &capacityMax);
|
||||
root->QueryFloatAttribute("CapacityMaxMultiplier", &capacityMaxMultiplier);
|
||||
root->QueryFloatAttribute("CapacityIfOverStuffedOnDigestBonus", &capacityIfOverStuffedOnDigestBonus);
|
||||
root->QueryDoubleAttribute("Capacity", &capacity);
|
||||
root->QueryDoubleAttribute("CapacityMin", &capacityMin);
|
||||
root->QueryDoubleAttribute("CapacityMax", &capacityMax);
|
||||
root->QueryDoubleAttribute("CapacityMaxMultiplier", &capacityMaxMultiplier);
|
||||
root->QueryDoubleAttribute("CapacityIfOverStuffedOnDigestBonus", &capacityIfOverStuffedOnDigestBonus);
|
||||
|
||||
root->QueryFloatAttribute("CaloriesToKilogram", &caloriesToKilogram);
|
||||
root->QueryDoubleAttribute("CaloriesToKilogram", &caloriesToKilogram);
|
||||
|
||||
root->QueryFloatAttribute("DigestionRate", &digestionRate);
|
||||
root->QueryFloatAttribute("DigestionRateMin", &digestionRateMin);
|
||||
root->QueryFloatAttribute("DigestionRateMax", &digestionRateMax);
|
||||
root->QueryDoubleAttribute("DigestionRate", &digestionRate);
|
||||
root->QueryDoubleAttribute("DigestionRateMin", &digestionRateMin);
|
||||
root->QueryDoubleAttribute("DigestionRateMax", &digestionRateMax);
|
||||
root->QueryIntAttribute("DigestionTimerMax", &digestionTimerMax);
|
||||
|
||||
root->QueryFloatAttribute("EatSpeed", &eatSpeed);
|
||||
root->QueryFloatAttribute("EatSpeedMin", &eatSpeedMin);
|
||||
root->QueryFloatAttribute("EatSpeedMax", &eatSpeedMax);
|
||||
root->QueryDoubleAttribute("EatSpeed", &eatSpeed);
|
||||
root->QueryDoubleAttribute("EatSpeedMin", &eatSpeedMin);
|
||||
root->QueryDoubleAttribute("EatSpeedMax", &eatSpeedMax);
|
||||
|
||||
root->QueryFloatAttribute("BlinkChance", &blinkChance);
|
||||
root->QueryFloatAttribute("GurgleChance", &gurgleChance);
|
||||
root->QueryFloatAttribute("GurgleCapacityMultiplier", &gurgleCapacityMultiplier);
|
||||
root->QueryDoubleAttribute("BlinkChance", &blinkChance);
|
||||
root->QueryDoubleAttribute("GurgleChance", &gurgleChance);
|
||||
root->QueryDoubleAttribute("GurgleCapacityMultiplier", &gurgleCapacityMultiplier);
|
||||
|
||||
root->QueryIntAttribute("TextBlipPeriodBase", &textBlipPeriodBase);
|
||||
|
||||
auto dialoguePath = physfs::Path(archive + "/" + "dialogue.xml");
|
||||
|
||||
@@ -100,6 +103,7 @@ namespace game::resource::xml
|
||||
|
||||
if (auto element = root->FirstChildElement("Sounds"))
|
||||
{
|
||||
query_sound_entry_collection(element, "Blip", archive, soundRootPath, sounds.blip);
|
||||
query_sound_entry_collection(element, "Digest", archive, soundRootPath, sounds.digest);
|
||||
query_sound_entry_collection(element, "Gurgle", archive, soundRootPath, sounds.gurgle);
|
||||
}
|
||||
@@ -127,6 +131,7 @@ namespace game::resource::xml
|
||||
child->QueryFloatAttribute("Threshold", &stage.threshold);
|
||||
child->QueryIntAttribute("AreaID", &stage.areaID);
|
||||
dialogue.query_pool_id(child, "DialoguePoolID", stage.pool.id);
|
||||
query_string_attribute(child, "AnimationAppendID", &stage.animationAppendID);
|
||||
stages.emplace_back(std::move(stage));
|
||||
}
|
||||
}
|
||||
@@ -178,12 +183,11 @@ namespace game::resource::xml
|
||||
if (child->FindAttribute("Layer")) query_layer_id(child, "Layer", anm2, interactArea.layerID);
|
||||
|
||||
query_null_id(child, "Null", anm2, interactArea.nullID);
|
||||
query_string_attribute(child, "Animation", &interactArea.animation);
|
||||
query_string_attribute(child, "AnimationFull", &interactArea.animationFull);
|
||||
query_string_attribute(child, "AnimationCursorHover", &interactArea.animationCursorHover);
|
||||
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);
|
||||
dialogue.query_pool_id(child, "DialoguePoolIDFull", interactArea.poolFull.id);
|
||||
query_bool_attribute(child, "IsHold", &interactArea.isHold);
|
||||
child->QueryFloatAttribute("DigestionBonusRub", &interactArea.digestionBonusRub);
|
||||
child->QueryFloatAttribute("DigestionBonusClick", &interactArea.digestionBonusClick);
|
||||
@@ -225,6 +229,9 @@ namespace game::resource::xml
|
||||
else
|
||||
logger.warning(std::format("No character skill_check.xml file found: {}", path.string()));
|
||||
|
||||
if (auto stringsPath = physfs::Path(archive + "/" + "strings.xml"); stringsPath.is_valid())
|
||||
strings = Strings(stringsPath);
|
||||
|
||||
logger.info(std::format("Initialized character: {}", name));
|
||||
|
||||
this->path = path;
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
#include "dialogue.hpp"
|
||||
#include "item.hpp"
|
||||
#include "menu.hpp"
|
||||
#include "skill_check.hpp"
|
||||
#include "save.hpp"
|
||||
#include "skill_check.hpp"
|
||||
#include "strings.hpp"
|
||||
|
||||
namespace game::resource::xml
|
||||
{
|
||||
@@ -24,6 +25,7 @@ namespace game::resource::xml
|
||||
float threshold{};
|
||||
int areaID{};
|
||||
Dialogue::PoolReference pool{-1};
|
||||
std::string animationAppendID{};
|
||||
};
|
||||
|
||||
struct EatArea
|
||||
@@ -42,8 +44,6 @@ namespace game::resource::xml
|
||||
|
||||
struct InteractArea
|
||||
{
|
||||
std::string animation{};
|
||||
std::string animationFull{};
|
||||
std::string animationCursorActive{};
|
||||
std::string animationCursorHover{};
|
||||
SoundEntryCollection sound{};
|
||||
@@ -53,6 +53,7 @@ namespace game::resource::xml
|
||||
int typeID{-1};
|
||||
bool isHold{};
|
||||
Dialogue::PoolReference pool{-1};
|
||||
Dialogue::PoolReference poolFull{-1};
|
||||
|
||||
float digestionBonusRub{};
|
||||
float digestionBonusClick{};
|
||||
@@ -73,6 +74,7 @@ namespace game::resource::xml
|
||||
|
||||
struct Sounds
|
||||
{
|
||||
SoundEntryCollection blip{};
|
||||
SoundEntryCollection gurgle{};
|
||||
SoundEntryCollection digest{};
|
||||
};
|
||||
@@ -98,6 +100,7 @@ namespace game::resource::xml
|
||||
Menu menuSchema{};
|
||||
Cursor cursorSchema{};
|
||||
SkillCheck skillCheckSchema{};
|
||||
Strings strings{};
|
||||
|
||||
Save save{};
|
||||
|
||||
@@ -119,23 +122,25 @@ namespace game::resource::xml
|
||||
|
||||
std::string name{};
|
||||
std::filesystem::path path{};
|
||||
float weight{50};
|
||||
float capacity{2000.0f};
|
||||
float capacityMin{2000.0f};
|
||||
float capacityMax{99999.0f};
|
||||
float capacityMaxMultiplier{1.5f};
|
||||
float capacityIfOverStuffedOnDigestBonus{0.25f};
|
||||
float caloriesToKilogram{1000.0f};
|
||||
float digestionRate{0.05f};
|
||||
float digestionRateMin{0.0f};
|
||||
float digestionRateMax{0.25f};
|
||||
double weight{50};
|
||||
double weightMax{1000};
|
||||
double capacity{2000.0};
|
||||
double capacityMin{2000.0};
|
||||
double capacityMax{99999.0};
|
||||
double capacityMaxMultiplier{1.5};
|
||||
double capacityIfOverStuffedOnDigestBonus{0.25};
|
||||
double caloriesToKilogram{1000.0};
|
||||
double digestionRate{0.05};
|
||||
double digestionRateMin{0.0};
|
||||
double digestionRateMax{0.25};
|
||||
int digestionTimerMax{60};
|
||||
float eatSpeed{1.0f};
|
||||
float eatSpeedMin{1.0f};
|
||||
float eatSpeedMax{3.0f};
|
||||
float blinkChance{1.0f};
|
||||
float gurgleChance{1.0f};
|
||||
float gurgleCapacityMultiplier{1.0f};
|
||||
int textBlipPeriodBase{3};
|
||||
double eatSpeed{1.0};
|
||||
double eatSpeedMin{1.0};
|
||||
double eatSpeedMax{3.0};
|
||||
double blinkChance{1.0};
|
||||
double gurgleChance{1.0};
|
||||
double gurgleCapacityMultiplier{1.0};
|
||||
Dialogue::PoolReference pool{-1};
|
||||
|
||||
Character() = default;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace game::resource::xml
|
||||
|
||||
query_string_attribute(root, "Name", &name);
|
||||
query_string_attribute(root, "Description", &description);
|
||||
query_string_attribute(root, "Author", &author);
|
||||
query_string_attribute(root, "Credits", &credits);
|
||||
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
|
||||
root->QueryFloatAttribute("Weight", &weight);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace game::resource::xml
|
||||
int stages{1};
|
||||
|
||||
std::string name{};
|
||||
std::string author{};
|
||||
std::string credits{};
|
||||
std::string description{};
|
||||
std::filesystem::path path{};
|
||||
float weight{50};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../../log.hpp"
|
||||
#include "../../util/math.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
|
||||
using namespace tinyxml2;
|
||||
@@ -98,6 +99,8 @@ namespace game::resource::xml
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
entrySelectionOrder.assign(entries.size(), -1);
|
||||
}
|
||||
|
||||
if (auto element = root->FirstChildElement("Pools"))
|
||||
@@ -158,15 +161,32 @@ namespace game::resource::xml
|
||||
}
|
||||
}
|
||||
|
||||
int Dialogue::Pool::get() const
|
||||
int Dialogue::entry_pick(Pool& pool)
|
||||
{
|
||||
if (this->empty()) return -1;
|
||||
auto index = rand() % this->size();
|
||||
return this->at(index);
|
||||
if (pool.empty()) return -1;
|
||||
|
||||
std::vector<int> unselected{};
|
||||
for (auto id : pool)
|
||||
if (id >= 0 && id < (int)entrySelectionOrder.size() && entrySelectionOrder[id] < 0) unselected.emplace_back(id);
|
||||
|
||||
std::vector<int> candidates = unselected.empty() ? std::vector<int>(pool.begin(), pool.end()) : unselected;
|
||||
if (candidates.empty()) return -1;
|
||||
|
||||
auto oldestOrder = entrySelectionOrder[candidates.front()];
|
||||
for (auto id : candidates)
|
||||
oldestOrder = std::min(oldestOrder, entrySelectionOrder[id]);
|
||||
|
||||
std::vector<int> oldestCandidates{};
|
||||
for (auto id : candidates)
|
||||
if (entrySelectionOrder[id] == oldestOrder) oldestCandidates.emplace_back(id);
|
||||
|
||||
auto pickedID = oldestCandidates.at(rand() % oldestCandidates.size());
|
||||
entrySelectionOrder[pickedID] = selectionCounter++;
|
||||
return pickedID;
|
||||
}
|
||||
Dialogue::Entry* Dialogue::get(int id) { return &entries.at(id); }
|
||||
Dialogue::Entry* Dialogue::get(Dialogue::EntryReference& entry) { return &entries.at(entry.id); }
|
||||
Dialogue::Entry* Dialogue::get(const std::string& string) { return &entries.at(entryIDMap.at(string)); }
|
||||
Dialogue::Entry* Dialogue::get(Dialogue::PoolReference& pool) { return &entries.at(pools.at(pool.id).get()); }
|
||||
Dialogue::Entry* Dialogue::get(Dialogue::Pool& pool) { return &entries.at(pool.get()); }
|
||||
Dialogue::Entry* Dialogue::get(Dialogue::PoolReference& pool) { return &entries.at(entry_pick(pools.at(pool.id))); }
|
||||
Dialogue::Entry* Dialogue::get(Dialogue::Pool& pool) { return &entries.at(entry_pick(pool)); }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../../util/physfs.hpp"
|
||||
|
||||
@@ -45,11 +46,7 @@ namespace game::resource::xml
|
||||
inline bool is_valid() const { return id != -1; };
|
||||
};
|
||||
|
||||
class Pool : public std::vector<int>
|
||||
{
|
||||
public:
|
||||
int get() const;
|
||||
};
|
||||
class Pool : public std::vector<int> {};
|
||||
|
||||
std::map<std::string, int> entryIDMap;
|
||||
std::map<int, std::string> entryIDMapReverse;
|
||||
@@ -57,6 +54,8 @@ namespace game::resource::xml
|
||||
|
||||
std::vector<Pool> pools{};
|
||||
std::map<std::string, int> poolMap{};
|
||||
std::vector<long long> entrySelectionOrder{};
|
||||
long long selectionCounter{};
|
||||
|
||||
EntryReference start{-1};
|
||||
EntryReference end{-1};
|
||||
@@ -83,8 +82,9 @@ namespace game::resource::xml
|
||||
Entry* get(Dialogue::EntryReference&);
|
||||
Entry* get(Dialogue::Pool&);
|
||||
Entry* get(Dialogue::PoolReference&);
|
||||
int entry_pick(Pool&);
|
||||
void query_entry_id(tinyxml2::XMLElement* element, const char* name, int& id);
|
||||
void query_pool_id(tinyxml2::XMLElement* element, const char* name, int& id);
|
||||
inline bool is_valid() const { return isValid; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,8 @@ namespace game::resource::xml
|
||||
std::string itemTextureRootPath{};
|
||||
query_string_attribute(element, "TextureRootPath", &itemTextureRootPath);
|
||||
|
||||
element->QueryIntAttribute("ChewCount", &chewCount);
|
||||
element->QueryIntAttribute("Durability", &durability);
|
||||
if (element->FindAttribute("ChewCount")) element->QueryIntAttribute("ChewCount", &durability);
|
||||
element->QueryIntAttribute("QuantityMax", &quantityMax);
|
||||
|
||||
for (auto child = element->FirstChildElement("Item"); child; child = child->NextSiblingElement("Item"))
|
||||
@@ -121,11 +122,14 @@ namespace game::resource::xml
|
||||
query_string_attribute(child, "Description", &item.description);
|
||||
|
||||
query_float_optional_attribute(child, "Calories", item.calories);
|
||||
query_float_optional_attribute(child, "CapacityBonus", item.capacityBonus);
|
||||
query_optional_vec3(child, "ColorR", "ColorG", "ColorB", item.color);
|
||||
query_float_optional_attribute(child, "DigestionBonus", item.digestionBonus);
|
||||
query_float_optional_attribute(child, "EatSpeedBonus", item.eatSpeedBonus);
|
||||
query_float_optional_attribute(child, "Gravity", item.gravity);
|
||||
|
||||
query_int_optional_attribute(child, "ChewCount", item.chewCount);
|
||||
query_int_optional_attribute(child, "Durability", item.durability);
|
||||
if (!item.durability.has_value()) query_int_optional_attribute(child, "ChewCount", item.durability);
|
||||
|
||||
if (child->FindAttribute("UpgradeID"))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../audio.hpp"
|
||||
#include "anm2.hpp"
|
||||
#include "sound_entry.hpp"
|
||||
@@ -46,10 +48,12 @@ namespace game::resource::xml
|
||||
std::optional<int> upgradeID{};
|
||||
std::optional<int> flavorID;
|
||||
std::optional<float> calories{};
|
||||
std::optional<float> capacityBonus{};
|
||||
std::optional<glm::vec3> color{};
|
||||
std::optional<float> eatSpeedBonus{};
|
||||
std::optional<float> digestionBonus{};
|
||||
std::optional<float> gravity{};
|
||||
std::optional<int> chewCount{};
|
||||
std::optional<int> durability{};
|
||||
bool isSkillCheckReward{};
|
||||
bool isToggleSpritesheet{};
|
||||
};
|
||||
@@ -90,7 +94,7 @@ namespace game::resource::xml
|
||||
Animations animations{};
|
||||
Sounds sounds{};
|
||||
Anm2 baseAnm2{};
|
||||
int chewCount{2};
|
||||
int durability{2};
|
||||
int quantityMax{99};
|
||||
|
||||
bool isValid{};
|
||||
|
||||
@@ -91,7 +91,8 @@ namespace game::resource::xml
|
||||
{
|
||||
Item item{};
|
||||
child->QueryIntAttribute("ID", &item.id);
|
||||
child->QueryIntAttribute("ChewCount", &item.chewCount);
|
||||
child->QueryIntAttribute("Durability", &item.durability);
|
||||
if (child->FindAttribute("ChewCount")) child->QueryIntAttribute("ChewCount", &item.durability);
|
||||
child->QueryFloatAttribute("PositionX", &item.position.x);
|
||||
child->QueryFloatAttribute("PositionY", &item.position.y);
|
||||
child->QueryFloatAttribute("VelocityX", &item.velocity.x);
|
||||
@@ -162,7 +163,7 @@ namespace game::resource::xml
|
||||
auto itemElement = itemsElement->InsertNewChildElement("Item");
|
||||
|
||||
itemElement->SetAttribute("ID", item.id);
|
||||
itemElement->SetAttribute("ChewCount", item.chewCount);
|
||||
itemElement->SetAttribute("Durability", item.durability);
|
||||
itemElement->SetAttribute("PositionX", item.position.x);
|
||||
itemElement->SetAttribute("PositionY", item.position.y);
|
||||
itemElement->SetAttribute("VelocityX", item.velocity.x);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace game::resource::xml
|
||||
struct Item
|
||||
{
|
||||
int id{};
|
||||
int chewCount{};
|
||||
int durability{};
|
||||
glm::vec2 position{};
|
||||
glm::vec2 velocity{};
|
||||
float rotation{};
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace game::resource::xml
|
||||
int volume{50};
|
||||
bool isUseCharacterColor{true};
|
||||
|
||||
glm::vec3 color{0.120f, 0.515f, 0.115f};
|
||||
glm::vec3 color{0.120f, 0.120f, 0.120f};
|
||||
glm::ivec2 windowSize{1600, 900};
|
||||
glm::vec2 windowPosition{};
|
||||
|
||||
|
||||
48
src/resource/xml/strings.cpp
Normal file
48
src/resource/xml/strings.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "strings.hpp"
|
||||
|
||||
#include "../../log.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <format>
|
||||
|
||||
using namespace tinyxml2;
|
||||
|
||||
namespace game::resource::xml
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string definition_element_name_get(const Strings::Definition& definition)
|
||||
{
|
||||
std::string name = definition.attribute;
|
||||
if (name.rfind("Text", 0) == 0) name.replace(0, 4, "String");
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
Strings::Strings()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
values[i] = definitions[i].fallback;
|
||||
}
|
||||
|
||||
Strings::Strings(const util::physfs::Path& path)
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
values[i] = definitions[i].fallback;
|
||||
|
||||
XMLDocument document;
|
||||
if (document_load(path, document) != XML_SUCCESS) return;
|
||||
|
||||
auto root = document.RootElement();
|
||||
if (!root) return;
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
if (auto element = root->FirstChildElement(definition_element_name_get(definitions[i]).c_str()))
|
||||
query_string_attribute(element, "Text", &values[i]);
|
||||
|
||||
isValid = true;
|
||||
logger.info(std::format("Initialized strings: {}", path.c_str()));
|
||||
}
|
||||
|
||||
const std::string& Strings::get(Type type) const { return values[type]; }
|
||||
}
|
||||
159
src/resource/xml/strings.hpp
Normal file
159
src/resource/xml/strings.hpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../util/physfs.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
namespace game::resource::xml
|
||||
{
|
||||
#define GAME_XML_STRING_LIST(X) \
|
||||
X(MenuTabInteract, "TextMenuTabInteract", "Interact") \
|
||||
X(MenuTabArcade, "TextMenuTabArcade", "Arcade") \
|
||||
X(MenuTabInventory, "TextMenuTabInventory", "Inventory") \
|
||||
X(MenuTabSettings, "TextMenuTabSettings", "Settings") \
|
||||
X(MenuTabCheats, "TextMenuTabCheats", "Cheats") \
|
||||
X(MenuTabDebug, "TextMenuTabDebug", "Debug") \
|
||||
X(MenuOpenTooltip, "TextMenuOpenTooltip", "Open Main Menu") \
|
||||
X(MenuCloseTooltip, "TextMenuCloseTooltip", "Close Main Menu") \
|
||||
X(InteractChatButton, "TextInteractChatButton", "Let's chat!") \
|
||||
X(InteractHelpButton, "TextInteractHelpButton", "Help") \
|
||||
X(InteractFeelingButton, "TextInteractFeelingButton", "How are you feeling?") \
|
||||
X(InteractWeightFormat, "TextInteractWeightFormat", "Weight: %0.2f %s (Stage: %i)") \
|
||||
X(InteractCapacityFormat, "TextInteractCapacityFormat", "Capacity: %0.0f kcal (Max: %0.0f kcal)") \
|
||||
X(InteractDigestionRateFormat, "TextInteractDigestionRateFormat", "Digestion Rate: %0.2f%%/sec") \
|
||||
X(InteractEatingSpeedFormat, "TextInteractEatingSpeedFormat", "Eating Speed: %0.2fx") \
|
||||
X(InteractTotalCaloriesFormat, "TextInteractTotalCaloriesFormat", "Total Calories Consumed: %0.0f kcal") \
|
||||
X(InteractTotalFoodItemsFormat, "TextInteractTotalFoodItemsFormat", "Total Food Items Eaten: %i") \
|
||||
X(SettingsMeasurementSystem, "TextSettingsMeasurementSystem", "Measurement System") \
|
||||
X(SettingsMetric, "TextSettingsMetric", "Metric") \
|
||||
X(SettingsMetricTooltip, "TextSettingsMetricTooltip", "Use kilograms (kg).") \
|
||||
X(SettingsImperial, "TextSettingsImperial", "Imperial") \
|
||||
X(SettingsImperialTooltip, "TextSettingsImperialTooltip", "Use pounds (lbs).") \
|
||||
X(SettingsSound, "TextSettingsSound", "Sound") \
|
||||
X(SettingsVolume, "TextSettingsVolume", "Volume") \
|
||||
X(SettingsVolumeTooltip, "TextSettingsVolumeTooltip", "Adjust master volume.") \
|
||||
X(SettingsAppearance, "TextSettingsAppearance", "Appearance") \
|
||||
X(SettingsUseCharacterColor, "TextSettingsUseCharacterColor", "Use Character Color") \
|
||||
X(SettingsUseCharacterColorTooltip, "TextSettingsUseCharacterColorTooltip", \
|
||||
"When playing, the UI will use the character's preset UI color.") \
|
||||
X(SettingsColor, "TextSettingsColor", "Color") \
|
||||
X(SettingsColorTooltip, "TextSettingsColorTooltip", "Change the UI color.") \
|
||||
X(SettingsResetButton, "TextSettingsResetButton", "Reset to Default") \
|
||||
X(SettingsSaveButton, "TextSettingsSaveButton", "Save") \
|
||||
X(SettingsSaveTooltip, "TextSettingsSaveTooltip", "Save the game.\n(Note: the game autosaves frequently.)") \
|
||||
X(SettingsReturnToCharactersButton, "TextSettingsReturnToCharactersButton", "Return to Characters") \
|
||||
X(SettingsReturnToCharactersTooltip, "TextSettingsReturnToCharactersTooltip", \
|
||||
"Go back to the character selection screen.\nProgress will be saved.") \
|
||||
X(ToastCheatsUnlocked, "TextToastCheatsUnlocked", "Cheats unlocked!") \
|
||||
X(ToastSaving, "TextToastSaving", "Saving...") \
|
||||
X(ToolsHomeButton, "TextToolsHomeButton", "Home") \
|
||||
X(ToolsHomeTooltip, "TextToolsHomeTooltip", "Reset camera view.\n(Shortcut: Home)") \
|
||||
X(ToolsOpenTooltip, "TextToolsOpenTooltip", "Open Tools") \
|
||||
X(ToolsCloseTooltip, "TextToolsCloseTooltip", "Close Tools") \
|
||||
X(DebugCursorScreenFormat, "TextDebugCursorScreenFormat", "Cursor Pos (Screen): %0.0f, %0.0f") \
|
||||
X(DebugCursorWorldFormat, "TextDebugCursorWorldFormat", "Cursor Pos (World): %0.0f, %0.0f") \
|
||||
X(DebugAnimations, "TextDebugAnimations", "Animations") \
|
||||
X(DebugNowPlayingFormat, "TextDebugNowPlayingFormat", "Now Playing: %s") \
|
||||
X(DebugDialogue, "TextDebugDialogue", "Dialogue") \
|
||||
X(DebugShowNulls, "TextDebugShowNulls", "Show Nulls (Hitboxes)") \
|
||||
X(DebugShowWorldBounds, "TextDebugShowWorldBounds", "Show World Bounds") \
|
||||
X(DebugItem, "TextDebugItem", "Item") \
|
||||
X(DebugHeld, "TextDebugHeld", "Held") \
|
||||
X(DebugItemTypeFormat, "TextDebugItemTypeFormat", "Type: %i") \
|
||||
X(DebugItemPositionFormat, "TextDebugItemPositionFormat", "Position: %0.0f, %0.0f") \
|
||||
X(DebugItemVelocityFormat, "TextDebugItemVelocityFormat", "Velocity: %0.0f, %0.0f") \
|
||||
X(DebugItemDurabilityFormat, "TextDebugItemDurabilityFormat", "Durability: %i") \
|
||||
X(InventoryEmptyHint, "TextInventoryEmptyHint", "Check the \"Arcade\" tab to earn rewards!") \
|
||||
X(InventoryFlavorFormat, "TextInventoryFlavorFormat", "Flavor: %s") \
|
||||
X(InventoryCaloriesFormat, "TextInventoryCaloriesFormat", "%0.0f kcal") \
|
||||
X(InventoryDurabilityFormat, "TextInventoryDurabilityFormat", "Durability: %i") \
|
||||
X(InventoryCapacityBonusFormat, "TextInventoryCapacityBonusFormat", "Capacity Bonus: +%0.0f kcal") \
|
||||
X(InventoryDigestionRateBonusFormat, "TextInventoryDigestionRateBonusFormat", "Digestion Rate Bonus: +%0.2f%% / sec") \
|
||||
X(InventoryDigestionRatePenaltyFormat, "TextInventoryDigestionRatePenaltyFormat", "Digestion Rate Penalty: %0.2f%% / sec") \
|
||||
X(InventoryEatSpeedBonusFormat, "TextInventoryEatSpeedBonusFormat", "Eat Speed Bonus: +%0.2f%% / sec") \
|
||||
X(InventoryEatSpeedPenaltyFormat, "TextInventoryEatSpeedPenaltyFormat", "Eat Speed Penalty: %0.2f%% / sec") \
|
||||
X(InventoryUpgradePreviewFormat, "TextInventoryUpgradePreviewFormat", "Upgrade: %ix -> %s") \
|
||||
X(InventoryUnknown, "TextInventoryUnknown", "???") \
|
||||
X(InventorySpawnButton, "TextInventorySpawnButton", "Spawn") \
|
||||
X(InventoryUpgradeButton, "TextInventoryUpgradeButton", "Upgrade") \
|
||||
X(InventoryUpgradeAllButton, "TextInventoryUpgradeAllButton", "Upgrade All") \
|
||||
X(InventoryUpgradeNoPath, "TextInventoryUpgradeNoPath", "This item cannot be upgraded.") \
|
||||
X(InventoryUpgradeNeedsTemplate, "TextInventoryUpgradeNeedsTemplate", "Needs {}x to upgrade into {}!") \
|
||||
X(InventoryUpgradeOneTemplate, "TextInventoryUpgradeOneTemplate", "Use {}x to upgrade into 1x {}.") \
|
||||
X(InventoryUpgradeAllTemplate, "TextInventoryUpgradeAllTemplate", "Use {}x to upgrade into {}x {}.") \
|
||||
X(ArcadeSkillCheckName, "TextArcadeSkillCheckName", "Skill Check") \
|
||||
X(ArcadeSkillCheckDescription, "TextArcadeSkillCheckDescription", \
|
||||
"Test your timing to build score, chain combos, and earn rewards based on your performance.") \
|
||||
X(ArcadePlayButton, "TextArcadePlayButton", "Play") \
|
||||
X(ArcadeStatsButton, "TextArcadeStatsButton", "Stats") \
|
||||
X(ArcadeBackButton, "TextArcadeBackButton", "Back") \
|
||||
X(ArcadeBestFormat, "TextArcadeBestFormat", "Best: %i pts (%ix)") \
|
||||
X(ArcadeTotalSkillChecksFormat, "TextArcadeTotalSkillChecksFormat", "Total Skill Checks: %i") \
|
||||
X(ArcadeAccuracyFormat, "TextArcadeAccuracyFormat", "Accuracy: %0.2f%%") \
|
||||
X(InfoProgressMax, "TextInfoProgressMax", "MAX") \
|
||||
X(InfoProgressToNextStage, "TextInfoProgressToNextStage", "To Next Stage") \
|
||||
X(InfoStageProgressFormat, "TextInfoStageProgressFormat", "Stage: %i/%i (%0.1f%%)") \
|
||||
X(InfoMaxedOut, "TextInfoMaxedOut", "Maxed out!") \
|
||||
X(InfoStageStartFormat, "TextInfoStageStartFormat", "Start: %0.2f %s") \
|
||||
X(InfoStageCurrentFormat, "TextInfoStageCurrentFormat", "Current: %0.2f %s") \
|
||||
X(InfoStageNextFormat, "TextInfoStageNextFormat", "Next: %0.2f %s") \
|
||||
X(InfoDigestion, "TextInfoDigestion", "Digestion") \
|
||||
X(InfoDigesting, "TextInfoDigesting", "Digesting...") \
|
||||
X(InfoDigestionInProgress, "TextInfoDigestionInProgress", "Digestion in progress...") \
|
||||
X(InfoGiveFoodToStartDigesting, "TextInfoGiveFoodToStartDigesting", "Give food to start digesting!") \
|
||||
X(InfoDigestionRateFormat, "TextInfoDigestionRateFormat", "Rate: %0.2f%% / sec") \
|
||||
X(InfoEatingSpeedFormat, "TextInfoEatingSpeedFormat", "Eating Speed: %0.2fx") \
|
||||
X(SkillCheckScoreFormat, "TextSkillCheckScoreFormat", "Score: %i pts (%ix)") \
|
||||
X(SkillCheckBestFormat, "TextSkillCheckBestFormat", "Best: %i pts (%ix)") \
|
||||
X(SkillCheckInstructions, "TextSkillCheckInstructions", "Match the line to the colored areas with Space/click! Better performance, better rewards!") \
|
||||
X(SkillCheckScoreLoss, "TextSkillCheckScoreLoss", "-1") \
|
||||
X(SkillCheckRewardToast, "TextSkillCheckRewardToast", "Fantastic score! Congratulations!") \
|
||||
X(SkillCheckHighScoreToast, "TextSkillCheckHighScoreToast", "High Score!") \
|
||||
X(SkillCheckGradeSuccessTemplate, "TextSkillCheckGradeSuccessTemplate", "{} (+{})") \
|
||||
X(SkillCheckMenuButton, "TextSkillCheckMenuButton", "Menu") \
|
||||
X(CheatsCalories, "TextCheatsCalories", "Calories") \
|
||||
X(CheatsCapacity, "TextCheatsCapacity", "Capacity") \
|
||||
X(CheatsWeight, "TextCheatsWeight", "Weight") \
|
||||
X(CheatsWeightFormat, "TextCheatsWeightFormat", "%0.2f kg") \
|
||||
X(CheatsStage, "TextCheatsStage", "Stage") \
|
||||
X(CheatsDigestionRate, "TextCheatsDigestionRate", "Digestion Rate") \
|
||||
X(CheatsDigestionRateFormat, "TextCheatsDigestionRateFormat", "%0.2f% / tick") \
|
||||
X(CheatsEatSpeed, "TextCheatsEatSpeed", "Eat Speed") \
|
||||
X(CheatsEatSpeedFormat, "TextCheatsEatSpeedFormat", "%0.2fx") \
|
||||
X(CheatsDigestButton, "TextCheatsDigestButton", "Digest") \
|
||||
X(CheatsInventory, "TextCheatsInventory", "Inventory")
|
||||
|
||||
class Strings
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
#define X(type, attr, fallback) type,
|
||||
GAME_XML_STRING_LIST(X)
|
||||
#undef X
|
||||
Count
|
||||
};
|
||||
|
||||
struct Definition
|
||||
{
|
||||
const char* attribute;
|
||||
const char* fallback;
|
||||
};
|
||||
|
||||
inline static constexpr std::array<Definition, Count> definitions{{
|
||||
#define X(type, attr, fallback) {attr, fallback},
|
||||
GAME_XML_STRING_LIST(X)
|
||||
#undef X
|
||||
}};
|
||||
|
||||
std::array<std::string, Count> values{};
|
||||
|
||||
bool isValid{};
|
||||
|
||||
Strings();
|
||||
Strings(const util::physfs::Path&);
|
||||
|
||||
const std::string& get(Type) const;
|
||||
};
|
||||
}
|
||||
@@ -117,6 +117,29 @@ namespace game::resource::xml
|
||||
return result;
|
||||
}
|
||||
|
||||
XMLError query_optional_vec3(XMLElement* element, const char* attributeX, const char* attributeY,
|
||||
const char* attributeZ, std::optional<glm::vec3>& value)
|
||||
{
|
||||
auto hasX = element->FindAttribute(attributeX);
|
||||
auto hasY = element->FindAttribute(attributeY);
|
||||
auto hasZ = element->FindAttribute(attributeZ);
|
||||
|
||||
if (!hasX && !hasY && !hasZ)
|
||||
{
|
||||
value.reset();
|
||||
return XML_NO_ATTRIBUTE;
|
||||
}
|
||||
|
||||
value = glm::vec3();
|
||||
auto result = XML_SUCCESS;
|
||||
|
||||
if (hasX) result = query_result_merge(result, element->QueryFloatAttribute(attributeX, &value->x));
|
||||
if (hasY) result = query_result_merge(result, element->QueryFloatAttribute(attributeY, &value->y));
|
||||
if (hasZ) result = query_result_merge(result, element->QueryFloatAttribute(attributeZ, &value->z));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XMLError document_load(const physfs::Path& path, XMLDocument& document)
|
||||
{
|
||||
if (!path.is_valid())
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace game::resource::xml
|
||||
std::optional<float>& value);
|
||||
tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
|
||||
std::optional<int>& value);
|
||||
tinyxml2::XMLError query_optional_vec3(tinyxml2::XMLElement* element, const char* attributeX, const char* attributeY,
|
||||
const char* attributeZ, std::optional<glm::vec3>& value);
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "play.hpp"
|
||||
#include "play/style.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
@@ -11,12 +12,25 @@
|
||||
#include "../util/math.hpp"
|
||||
|
||||
using namespace game::resource;
|
||||
using namespace game::resource::xml;
|
||||
using namespace game::util;
|
||||
using namespace game::state::play;
|
||||
using namespace glm;
|
||||
|
||||
namespace game::state
|
||||
{
|
||||
namespace
|
||||
{
|
||||
int durability_animation_index_get(const resource::xml::Item& schema, const resource::xml::Anm2& anm2, int durability,
|
||||
int durabilityMax)
|
||||
{
|
||||
if (durability >= durabilityMax) return -1;
|
||||
|
||||
auto animationName = schema.animations.chew + std::to_string(std::max(0, durability));
|
||||
return anm2.animationMap.contains(animationName) ? anm2.animationMap.at(animationName) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
World::Focus Play::focus_get()
|
||||
{
|
||||
if (!isWindows) return World::CENTER;
|
||||
@@ -40,9 +54,10 @@ namespace game::state
|
||||
|
||||
character =
|
||||
entity::Character(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y));
|
||||
character.digestionRate = glm::clamp(data.digestionRateMin, character.digestionRate, data.digestionRateMax);
|
||||
character.eatSpeed = glm::clamp(data.eatSpeedMin, character.eatSpeed, data.eatSpeedMax);
|
||||
character.capacity = glm::clamp(data.capacityMin, character.capacity, data.capacityMax);
|
||||
character.digestionRate =
|
||||
glm::clamp(character.digestionRate, (float)data.digestionRateMin, (float)data.digestionRateMax);
|
||||
character.eatSpeed = glm::clamp(character.eatSpeed, (float)data.eatSpeedMin, (float)data.eatSpeedMax);
|
||||
character.capacity = glm::clamp(character.capacity, (float)data.capacityMin, (float)data.capacityMax);
|
||||
|
||||
auto isAlternateSpritesheet =
|
||||
(game == NEW_GAME && math::random_percent_roll(data.alternateSpritesheet.chanceOnNewGame));
|
||||
@@ -71,29 +86,34 @@ namespace game::state
|
||||
for (auto& item : saveData.items)
|
||||
{
|
||||
auto& anm2 = itemSchema.anm2s.at(item.id);
|
||||
auto chewAnimation = itemSchema.animations.chew + std::to_string(item.chewCount);
|
||||
auto animationIndex = item.chewCount > 0 ? anm2.animationMap[chewAnimation] : -1;
|
||||
auto& schemaItem = itemSchema.items.at(item.id);
|
||||
auto durabilityMax = schemaItem.durability.value_or(itemSchema.durability);
|
||||
auto animationIndex = durability_animation_index_get(itemSchema, anm2, item.durability, durabilityMax);
|
||||
auto& saveItem = itemSchema.anm2s.at(item.id);
|
||||
itemManager.items.emplace_back(saveItem, item.position, item.id, item.chewCount, animationIndex, item.velocity,
|
||||
itemManager.items.emplace_back(saveItem, item.position, item.id, item.durability, animationIndex, item.velocity,
|
||||
item.rotation);
|
||||
}
|
||||
|
||||
imgui::style::rounding_set(menuSchema.rounding);
|
||||
imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select);
|
||||
menu.color_set_check(resources, character);
|
||||
play::style::color_set(resources, character);
|
||||
|
||||
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();
|
||||
menu.arcade = Arcade(character);
|
||||
menu.arcade.skillCheck.totalPlays = saveData.totalPlays;
|
||||
menu.arcade.skillCheck.highScore = saveData.highScore;
|
||||
menu.arcade.skillCheck.bestCombo = saveData.bestCombo;
|
||||
menu.arcade.skillCheck.gradeCounts = saveData.gradeCounts;
|
||||
menu.arcade.skillCheck.isHighScoreAchieved = saveData.highScore > 0 ? true : false;
|
||||
|
||||
text.entry = nullptr;
|
||||
text.isEnabled = false;
|
||||
|
||||
#if DEBUG
|
||||
menu.isCheats = true;
|
||||
#else
|
||||
menu.isCheats = false;
|
||||
#endif
|
||||
|
||||
isPostgame = saveData.isPostgame;
|
||||
if (character.stage_get() >= character.stage_max_get()) isPostgame = true;
|
||||
if (isPostgame) menu.isCheats = true;
|
||||
@@ -190,7 +210,7 @@ namespace game::state
|
||||
menu.isCheats = true;
|
||||
cheatCodeIndex = 0;
|
||||
cheatCodeStartTime = 0.0;
|
||||
toasts.push("Cheats unlocked!");
|
||||
toasts.push(character.data.strings.get(Strings::ToastCheatsUnlocked));
|
||||
character.data.menuSchema.sounds.cheatsActivated.play();
|
||||
}
|
||||
}
|
||||
@@ -338,10 +358,10 @@ namespace game::state
|
||||
save.digestionTimer = character.digestionTimer;
|
||||
save.totalCaloriesConsumed = character.totalCaloriesConsumed;
|
||||
save.totalFoodItemsEaten = character.totalFoodItemsEaten;
|
||||
save.totalPlays = menu.skillCheck.totalPlays;
|
||||
save.highScore = menu.skillCheck.highScore;
|
||||
save.bestCombo = menu.skillCheck.bestCombo;
|
||||
save.gradeCounts = menu.skillCheck.gradeCounts;
|
||||
save.totalPlays = menu.arcade.skillCheck.totalPlays;
|
||||
save.highScore = menu.arcade.skillCheck.highScore;
|
||||
save.bestCombo = menu.arcade.skillCheck.bestCombo;
|
||||
save.gradeCounts = menu.arcade.skillCheck.gradeCounts;
|
||||
save.isPostgame = isPostgame;
|
||||
save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE;
|
||||
|
||||
@@ -352,7 +372,7 @@ namespace game::state
|
||||
}
|
||||
|
||||
for (auto& item : itemManager.items)
|
||||
save.items.emplace_back(item.schemaID, item.chewCount, item.position, item.velocity,
|
||||
save.items.emplace_back(item.schemaID, item.durability, item.position, item.velocity,
|
||||
*item.overrides[item.rotationOverrideID].frame.rotation);
|
||||
|
||||
save.isValid = true;
|
||||
@@ -360,6 +380,6 @@ namespace game::state
|
||||
resources.character_save_set(characterIndex, save);
|
||||
save.serialize(character.data.save_path_get());
|
||||
|
||||
toasts.push("Saving...");
|
||||
toasts.push(character.data.strings.get(Strings::ToastSaving));
|
||||
}
|
||||
};
|
||||
|
||||
76
src/state/play/arcade.cpp
Normal file
76
src/state/play/arcade.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "arcade.hpp"
|
||||
|
||||
#include "../../util/imgui/widget.hpp"
|
||||
|
||||
using namespace game::util::imgui;
|
||||
using namespace game::resource::xml;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
Arcade::Arcade(entity::Character& character) : skillCheck(character) {}
|
||||
|
||||
void Arcade::tick() { skillCheck.tick(); }
|
||||
|
||||
void Arcade::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text)
|
||||
{
|
||||
auto available = ImGui::GetContentRegionAvail();
|
||||
auto& strings = character.data.strings;
|
||||
|
||||
if (view == SKILL_CHECK)
|
||||
{
|
||||
if (skillCheck.update(resources, character, inventory, text)) view = MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
auto buttonHeight = ImGui::GetFrameHeightWithSpacing();
|
||||
auto childSize = ImVec2(available.x, std::max(0.0f, available.y - buttonHeight));
|
||||
|
||||
if (ImGui::BeginChild("##Arcade Child", childSize))
|
||||
{
|
||||
if (view == MENU)
|
||||
{
|
||||
auto buttonWidth = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_2);
|
||||
ImGui::TextUnformatted(strings.get(Strings::ArcadeSkillCheckName).c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("%s", strings.get(Strings::ArcadeSkillCheckDescription).c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ArcadePlayButton).c_str(), ImVec2(buttonWidth, 0))))
|
||||
view = SKILL_CHECK;
|
||||
ImGui::SameLine();
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ArcadeStatsButton).c_str(), ImVec2(buttonWidth, 0))))
|
||||
view = SKILL_CHECK_STATS;
|
||||
}
|
||||
else if (view == SKILL_CHECK_STATS)
|
||||
{
|
||||
auto& schema = character.data.skillCheckSchema;
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_2);
|
||||
ImGui::TextUnformatted(strings.get(Strings::ArcadeSkillCheckName).c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text(strings.get(Strings::ArcadeBestFormat).c_str(), skillCheck.highScore, skillCheck.bestCombo);
|
||||
ImGui::Text(strings.get(Strings::ArcadeTotalSkillChecksFormat).c_str(), skillCheck.totalPlays);
|
||||
|
||||
for (int i = 0; i < (int)schema.grades.size(); i++)
|
||||
{
|
||||
auto& grade = schema.grades[i];
|
||||
ImGui::Text("%s: %i", grade.namePlural.c_str(), skillCheck.gradeCounts[i]);
|
||||
}
|
||||
|
||||
ImGui::Text(strings.get(Strings::ArcadeAccuracyFormat).c_str(), skillCheck.accuracy_score_get(character));
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (view == SKILL_CHECK_STATS)
|
||||
{
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ArcadeBackButton).c_str()))) view = MENU;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/state/play/arcade.hpp
Normal file
26
src/state/play/arcade.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "arcade/skill_check.hpp"
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
class Arcade
|
||||
{
|
||||
public:
|
||||
enum View
|
||||
{
|
||||
MENU,
|
||||
SKILL_CHECK,
|
||||
SKILL_CHECK_STATS
|
||||
};
|
||||
|
||||
SkillCheck skillCheck{};
|
||||
View view{MENU};
|
||||
|
||||
Arcade() = default;
|
||||
Arcade(entity::Character&);
|
||||
|
||||
void tick();
|
||||
void update(Resources&, entity::Character&, Inventory&, Text&);
|
||||
};
|
||||
}
|
||||
@@ -6,13 +6,16 @@
|
||||
#include "../../../util/imgui/widget.hpp"
|
||||
#include "../../../util/math.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <format>
|
||||
#include <ranges>
|
||||
|
||||
using namespace game::util;
|
||||
using namespace game::entity;
|
||||
using namespace game::resource;
|
||||
using namespace game::resource::xml;
|
||||
using namespace glm;
|
||||
|
||||
namespace game::state::play
|
||||
@@ -69,7 +72,7 @@ namespace game::state::play
|
||||
actor.tick();
|
||||
}
|
||||
|
||||
void SkillCheck::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text)
|
||||
bool 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);
|
||||
@@ -82,25 +85,31 @@ namespace game::state::play
|
||||
auto& dialogue = character.data.dialogue;
|
||||
auto& schema = character.data.skillCheckSchema;
|
||||
auto& itemSchema = character.data.itemSchema;
|
||||
auto& strings = character.data.strings;
|
||||
auto& style = ImGui::GetStyle();
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
auto position = ImGui::GetCursorScreenPos();
|
||||
auto size = ImGui::GetContentRegionAvail();
|
||||
auto spacing = ImGui::GetTextLineHeightWithSpacing();
|
||||
auto& io = ImGui::GetIO();
|
||||
auto menuButtonHeight = ImGui::GetFrameHeightWithSpacing();
|
||||
size.y = std::max(0.0f, size.y - menuButtonHeight);
|
||||
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
|
||||
ImGui::Text("Score: %i pts (%ix)", score, combo);
|
||||
auto bestString = std::format("Best: {} pts({}x)", highScore, bestCombo);
|
||||
ImGui::Text(strings.get(Strings::SkillCheckScoreFormat).c_str(), score, combo);
|
||||
std::array<char, 128> bestBuffer{};
|
||||
std::snprintf(bestBuffer.data(), bestBuffer.size(), strings.get(Strings::SkillCheckBestFormat).c_str(), highScore,
|
||||
bestCombo);
|
||||
auto bestString = std::string(bestBuffer.data());
|
||||
ImGui::SetCursorPos(ImVec2(size.x - ImGui::CalcTextSize(bestString.c_str()).x, cursorPos.y));
|
||||
|
||||
ImGui::Text("Best: %i pts (%ix)", highScore, bestCombo);
|
||||
ImGui::Text(strings.get(Strings::SkillCheckBestFormat).c_str(), highScore, bestCombo);
|
||||
|
||||
if (score == 0 && isActive)
|
||||
{
|
||||
ImGui::SetCursorPos(ImVec2(style.WindowPadding.x, size.y - style.WindowPadding.y));
|
||||
ImGui::TextWrapped("Match the line to the colored areas with Space/click! Better performance, better rewards!");
|
||||
ImGui::TextWrapped("%s", strings.get(Strings::SkillCheckInstructions).c_str());
|
||||
}
|
||||
|
||||
auto barMin = ImVec2(position.x + (size.x * 0.5f) - (spacing * 0.5f), position.y + (spacing * 2.0f));
|
||||
@@ -193,8 +202,11 @@ namespace game::state::play
|
||||
score--;
|
||||
schema.sounds.scoreLoss.play();
|
||||
auto toastMessagePosition =
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize("-1").x - ImGui::GetTextLineHeightWithSpacing(), lineMin.y);
|
||||
toasts.emplace_back("-1", toastMessagePosition, schema.endTimerMax, schema.endTimerMax);
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize(strings.get(Strings::SkillCheckScoreLoss).c_str()).x -
|
||||
ImGui::GetTextLineHeightWithSpacing(),
|
||||
lineMin.y);
|
||||
toasts.emplace_back(strings.get(Strings::SkillCheckScoreLoss), toastMessagePosition, schema.endTimerMax,
|
||||
schema.endTimerMax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,10 +270,10 @@ namespace game::state::play
|
||||
}
|
||||
|
||||
auto toastMessagePosition =
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize("Fantastic score!\nCongratulations!").x -
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize(strings.get(Strings::SkillCheckRewardToast).c_str()).x -
|
||||
ImGui::GetTextLineHeightWithSpacing(),
|
||||
lineMin.y + (ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y));
|
||||
toasts.emplace_back("Fantastic score! Congratulations!", toastMessagePosition, schema.endTimerMax,
|
||||
toasts.emplace_back(strings.get(Strings::SkillCheckRewardToast), toastMessagePosition, schema.endTimerMax,
|
||||
schema.endTimerMax);
|
||||
}
|
||||
|
||||
@@ -274,9 +286,11 @@ namespace game::state::play
|
||||
isHighScoreAchievedThisRun = true;
|
||||
schema.sounds.highScore.play();
|
||||
auto toastMessagePosition =
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize("High Score!").x - ImGui::GetTextLineHeightWithSpacing(),
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize(strings.get(Strings::SkillCheckHighScoreToast).c_str()).x -
|
||||
ImGui::GetTextLineHeightWithSpacing(),
|
||||
lineMin.y + ImGui::GetTextLineHeightWithSpacing());
|
||||
toasts.emplace_back("High Score!", toastMessagePosition, schema.endTimerMax, schema.endTimerMax);
|
||||
toasts.emplace_back(strings.get(Strings::SkillCheckHighScoreToast), toastMessagePosition,
|
||||
schema.endTimerMax, schema.endTimerMax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,10 +351,10 @@ namespace game::state::play
|
||||
{
|
||||
score = 0;
|
||||
combo = 0;
|
||||
if (isHighScoreAchieved) schema.sounds.highScoreLoss.play();
|
||||
if (isHighScoreAchievedThisRun) schema.sounds.highScoreLoss.play();
|
||||
if (highScore > 0) isHighScoreAchieved = true;
|
||||
isRewardScoreAchieved = false;
|
||||
isHighScoreAchievedThisRun = true;
|
||||
isHighScoreAchievedThisRun = false;
|
||||
highScoreStart = highScore;
|
||||
isGameOver = true;
|
||||
}
|
||||
@@ -351,7 +365,9 @@ namespace game::state::play
|
||||
|
||||
queuedChallenge = challenge_generate(character);
|
||||
|
||||
auto string = grade.isFailure ? grade.name : std::format("{} (+{})", grade.name, grade.value);
|
||||
auto string = grade.isFailure ? grade.name
|
||||
: std::vformat(strings.get(Strings::SkillCheckGradeSuccessTemplate),
|
||||
std::make_format_args(grade.name, grade.value));
|
||||
auto toastMessagePosition =
|
||||
ImVec2(barMin.x - ImGui::CalcTextSize(string.c_str()).x - ImGui::GetTextLineHeightWithSpacing(), lineMin.y);
|
||||
toasts.emplace_back(string, toastMessagePosition, endTimerMax, endTimerMax);
|
||||
@@ -429,5 +445,8 @@ namespace game::state::play
|
||||
if (fallingItem.position.y > position.y + size.y) items.erase(items.begin() + i--);
|
||||
}
|
||||
ImGui::PopClipRect();
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(position.x, position.y + size.y + ImGui::GetStyle().ItemSpacing.y));
|
||||
return WIDGET_FX(ImGui::Button(strings.get(Strings::SkillCheckMenuButton).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace game::state::play
|
||||
SkillCheck(entity::Character&);
|
||||
Challenge challenge_generate(entity::Character&);
|
||||
void tick();
|
||||
void update(Resources&, entity::Character&, Inventory&, Text&);
|
||||
bool update(Resources&, entity::Character&, Inventory&, Text&);
|
||||
float accuracy_score_get(entity::Character&);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,14 +89,18 @@ namespace game::state::play
|
||||
interact_area_override_tick, interactArea.scaleEffectCycles));
|
||||
}
|
||||
|
||||
if (interactArea.pool.is_valid() && text.is_interruptible())
|
||||
text.set(dialogue.get(interactArea.pool), character);
|
||||
if (text.is_interruptible())
|
||||
{
|
||||
auto& pool = character.is_over_capacity() && interactArea.poolFull.is_valid() ? interactArea.poolFull
|
||||
: interactArea.pool;
|
||||
if (pool.is_valid())
|
||||
text.set(dialogue.get(pool), character);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInteracting)
|
||||
{
|
||||
cursor.state = entity::Cursor::ACTION;
|
||||
character.queue_interact_area_animation(interactArea);
|
||||
cursor.queue_play({interactArea.animationCursorActive});
|
||||
|
||||
if (interactArea.digestionBonusRub > 0 && character.calories > 0 && !character.isDigesting)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#include "chat.hpp"
|
||||
|
||||
#include "../../util/imgui/widget.hpp"
|
||||
|
||||
using namespace game::resource;
|
||||
using namespace game::util::imgui;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
void Chat::update(Resources&, Text& text, entity::Character& character)
|
||||
{
|
||||
auto& dialogue = character.data.dialogue;
|
||||
auto size = ImGui::GetContentRegionAvail();
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
|
||||
if (dialogue.random.is_valid())
|
||||
if (WIDGET_FX(ImGui::Button("Let's chat!", ImVec2(size.x, 0))))
|
||||
text.set(dialogue.get(dialogue.random), character);
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
if (dialogue.help.is_valid())
|
||||
if (WIDGET_FX(ImGui::Button("Help", ImVec2(size.x, 0)))) text.set(dialogue.get(dialogue.help), character);
|
||||
|
||||
auto stage = glm::clamp(0, character.stage_get(), character.stage_max_get());
|
||||
auto& pool = stage > 0 ? character.data.stages.at(stage - 1).pool : character.data.pool;
|
||||
|
||||
if (pool.is_valid())
|
||||
if (WIDGET_FX(ImGui::Button("How are you feeling?", ImVec2(size.x, 0)))) text.set(dialogue.get(pool), character);
|
||||
}
|
||||
}
|
||||
@@ -8,82 +8,50 @@
|
||||
|
||||
using namespace game::util::imgui;
|
||||
using namespace game::util;
|
||||
using namespace game::resource::xml;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
void Cheats::update(Resources&, entity::Character& character, Inventory& inventory, Text& text)
|
||||
void Cheats::update(Resources&, entity::Character& character, Inventory& inventory)
|
||||
{
|
||||
static constexpr auto FEED_INCREMENT = 100.0f;
|
||||
auto& strings = character.data.strings;
|
||||
|
||||
if (ImGui::BeginChild("##Cheats"))
|
||||
{
|
||||
|
||||
if (WIDGET_FX(ImGui::Button("Feed")))
|
||||
{
|
||||
character.calories = std::min(character.calories + FEED_INCREMENT, character.max_capacity());
|
||||
character.queue_idle_animation();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (WIDGET_FX(ImGui::Button("Starve")))
|
||||
{
|
||||
character.calories = std::max(0.0f, character.calories - FEED_INCREMENT);
|
||||
character.queue_idle_animation();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (WIDGET_FX(ImGui::Button("Digest"))) character.digestionProgress = entity::Character::DIGESTION_MAX;
|
||||
|
||||
auto stage = character.stage + 1;
|
||||
if (WIDGET_FX(ImGui::SliderInt("Stage", &stage, 1, (int)character.data.stages.size() + 1)))
|
||||
|
||||
auto weight_update = [&]() { character.queue_idle_animation(); };
|
||||
|
||||
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsCalories).c_str(), &character.calories, 0,
|
||||
character.max_capacity(), "%0.0f kcal"));
|
||||
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsCapacity).c_str(), &character.capacity,
|
||||
character.data.capacityMin, character.data.capacityMax, "%0.0f kcal"));
|
||||
|
||||
if (WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsWeight).c_str(), &character.weight,
|
||||
character.data.weight, character.data.weightMax,
|
||||
strings.get(Strings::CheatsWeightFormat).c_str())))
|
||||
weight_update();
|
||||
|
||||
if (WIDGET_FX(ImGui::SliderInt(strings.get(Strings::CheatsStage).c_str(), &stage, 1,
|
||||
(int)character.data.stages.size() + 1)))
|
||||
{
|
||||
character.stage = glm::clamp(0, stage - 1, (int)character.data.stages.size());
|
||||
character.weight =
|
||||
character.stage == 0 ? character.data.weight : character.data.stages.at(character.stage - 1).threshold;
|
||||
character.queue_idle_animation();
|
||||
weight_update();
|
||||
}
|
||||
|
||||
WIDGET_FX(ImGui::SliderFloat("Capacity", &character.capacity, character.data.capacityMin,
|
||||
character.data.capacityMax, "%0.0f kcal"));
|
||||
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsDigestionRate).c_str(), &character.digestionRate,
|
||||
character.data.digestionRateMin, character.data.digestionRateMax,
|
||||
strings.get(Strings::CheatsDigestionRateFormat).c_str()));
|
||||
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsEatSpeed).c_str(), &character.eatSpeed,
|
||||
character.data.eatSpeedMin, character.data.eatSpeedMax,
|
||||
strings.get(Strings::CheatsEatSpeedFormat).c_str()));
|
||||
|
||||
WIDGET_FX(ImGui::SliderFloat("Digestion Rate", &character.digestionRate, character.data.digestionRateMin,
|
||||
character.data.digestionRateMax, "%0.2f% / tick"));
|
||||
WIDGET_FX(ImGui::SliderFloat("Eat Speed", &character.eatSpeed, character.data.eatSpeedMin,
|
||||
character.data.eatSpeedMax, "%0.2fx"));
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::CheatsDigestButton).c_str())))
|
||||
character.digestionProgress = entity::Character::DIGESTION_MAX;
|
||||
|
||||
ImGui::SeparatorText("Animations");
|
||||
ImGui::Text("Now Playing: %s", character.animationMapReverse.at(character.animationIndex).c_str());
|
||||
|
||||
auto childSize = ImVec2(0, ImGui::GetContentRegionAvail().y / 3);
|
||||
|
||||
if (ImGui::BeginChild("##Animations", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
for (int i = 0; i < (int)character.animations.size(); i++)
|
||||
{
|
||||
auto& animation = character.animations[i];
|
||||
ImGui::PushID(i);
|
||||
if (WIDGET_FX(ImGui::Selectable(animation.name.c_str())))
|
||||
character.play(animation.name.c_str(), entity::Actor::PLAY_FORCE);
|
||||
ImGui::SetItemTooltip("%s", animation.name.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SeparatorText("Dialogue");
|
||||
|
||||
if (ImGui::BeginChild("##Dialogue", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
for (int i = 0; i < (int)character.data.dialogue.entries.size(); i++)
|
||||
{
|
||||
auto& entry = character.data.dialogue.entries[i];
|
||||
ImGui::PushID(i);
|
||||
if (WIDGET_FX(ImGui::Selectable(entry.name.c_str()))) text.set(&entry, character);
|
||||
ImGui::SetItemTooltip("%s", entry.name.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SeparatorText("Inventory");
|
||||
ImGui::SeparatorText(strings.get(Strings::CheatsInventory).c_str());
|
||||
|
||||
if (ImGui::BeginChild("##Inventory", ImGui::GetContentRegionAvail(), ImGuiChildFlags_Borders))
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace game::state::play
|
||||
class Cheats
|
||||
{
|
||||
public:
|
||||
|
||||
void update(Resources&, entity::Character&, Inventory&, Text&);
|
||||
void update(Resources&, entity::Character&, Inventory&);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,31 +5,68 @@
|
||||
#include <ranges>
|
||||
|
||||
using namespace game::util::imgui;
|
||||
using namespace game::resource::xml;
|
||||
|
||||
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,
|
||||
Text& text)
|
||||
{
|
||||
auto& strings = character.data.strings;
|
||||
auto cursorPosition = canvas.screen_position_convert(cursor.position);
|
||||
|
||||
ImGui::Text("Cursor Pos (Screen): %0.0f, %0.0f", cursor.position.x, cursor.position.y);
|
||||
ImGui::Text("Cursor Pos (World): %0.0f, %0.0f", cursorPosition.x, cursorPosition.y);
|
||||
ImGui::Text(strings.get(Strings::DebugCursorScreenFormat).c_str(), cursor.position.x, cursor.position.y);
|
||||
ImGui::Text(strings.get(Strings::DebugCursorWorldFormat).c_str(), cursorPosition.x, cursorPosition.y);
|
||||
|
||||
WIDGET_FX(ImGui::Checkbox("Show Nulls (Hitboxes)", &character.isShowNulls));
|
||||
WIDGET_FX(ImGui::Checkbox("Show World Bounds", &isBoundsDisplay));
|
||||
ImGui::SeparatorText(strings.get(Strings::DebugAnimations).c_str());
|
||||
ImGui::Text(strings.get(Strings::DebugNowPlayingFormat).c_str(), character.animationMapReverse.at(character.animationIndex).c_str());
|
||||
|
||||
auto childSize = ImVec2(0, ImGui::GetContentRegionAvail().y / 3);
|
||||
|
||||
if (ImGui::BeginChild("##Animations", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
for (int i = 0; i < (int)character.animations.size(); i++)
|
||||
{
|
||||
auto& animation = character.animations[i];
|
||||
ImGui::PushID(i);
|
||||
if (WIDGET_FX(ImGui::Selectable(animation.name.c_str())))
|
||||
character.play(animation.name.c_str(), entity::Actor::PLAY_FORCE);
|
||||
ImGui::SetItemTooltip("%s", animation.name.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SeparatorText(strings.get(Strings::DebugDialogue).c_str());
|
||||
|
||||
if (ImGui::BeginChild("##Dialogue", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
for (int i = 0; i < (int)character.data.dialogue.entries.size(); i++)
|
||||
{
|
||||
auto& entry = character.data.dialogue.entries[i];
|
||||
ImGui::PushID(i);
|
||||
if (WIDGET_FX(ImGui::Selectable(entry.name.c_str()))) text.set(&entry, character);
|
||||
ImGui::SetItemTooltip("%s", entry.name.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
WIDGET_FX(ImGui::Checkbox(strings.get(Strings::DebugShowNulls).c_str(), &character.isShowNulls));
|
||||
WIDGET_FX(ImGui::Checkbox(strings.get(Strings::DebugShowWorldBounds).c_str(), &isBoundsDisplay));
|
||||
|
||||
if (!itemManager.items.empty())
|
||||
{
|
||||
ImGui::SeparatorText("Item");
|
||||
ImGui::SeparatorText(strings.get(Strings::DebugItem).c_str());
|
||||
|
||||
for (int i = 0; i < (int)itemManager.items.size(); i++)
|
||||
{
|
||||
auto& item = itemManager.items[i];
|
||||
if (itemManager.heldItemIndex == i) ImGui::Text("Held");
|
||||
ImGui::Text("Type: %i", item.schemaID);
|
||||
ImGui::Text("Position: %0.0f, %0.0f", item.position.x, item.position.y);
|
||||
ImGui::Text("Velocity: %0.0f, %0.0f", item.velocity.x, item.velocity.y);
|
||||
ImGui::Text("Chew Count: %i", item.chewCount);
|
||||
if (itemManager.heldItemIndex == i) ImGui::TextUnformatted(strings.get(Strings::DebugHeld).c_str());
|
||||
ImGui::Text(strings.get(Strings::DebugItemTypeFormat).c_str(), item.schemaID);
|
||||
ImGui::Text(strings.get(Strings::DebugItemPositionFormat).c_str(), item.position.x, item.position.y);
|
||||
ImGui::Text(strings.get(Strings::DebugItemVelocityFormat).c_str(), item.velocity.x, item.velocity.y);
|
||||
ImGui::Text(strings.get(Strings::DebugItemDurabilityFormat).c_str(), item.durability);
|
||||
ImGui::Separator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../../entity/cursor.hpp"
|
||||
|
||||
#include "item_manager.hpp"
|
||||
#include "text.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
@@ -14,6 +15,6 @@ namespace game::state::play
|
||||
public:
|
||||
bool isBoundsDisplay{};
|
||||
|
||||
void update(entity::Character&, entity::Cursor& cursor, ItemManager&, Canvas& canvas);
|
||||
void update(entity::Character&, entity::Cursor&, ItemManager&, Canvas&, Text&);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <format>
|
||||
|
||||
using namespace game::resource;
|
||||
using namespace game::resource::xml;
|
||||
using namespace game::util;
|
||||
|
||||
namespace game::state::play
|
||||
@@ -18,6 +19,7 @@ namespace game::state::play
|
||||
static constexpr auto WIDTH_MULTIPLIER = 0.30f;
|
||||
static constexpr auto HEIGHT_MULTIPLIER = 4.0f;
|
||||
|
||||
auto& strings = character.data.strings;
|
||||
auto& style = ImGui::GetStyle();
|
||||
auto windowSize = imgui::to_ivec2(ImGui::GetMainViewport()->Size);
|
||||
|
||||
@@ -45,26 +47,29 @@ namespace game::state::play
|
||||
auto unitString = (system == measurement::IMPERIAL ? "lbs" : "kg");
|
||||
|
||||
auto weightString = util::string::format_commas(weight, 2) + " " + unitString;
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::ABOVE_AVERAGE);
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_1);
|
||||
ImGui::TextUnformatted(weightString.c_str());
|
||||
ImGui::SetItemTooltip("%s", weightString.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
auto stageProgress = character.stage_progress_get();
|
||||
ImGui::ProgressBar(stageProgress, ImVec2(ImGui::GetContentRegionAvail().x, 0),
|
||||
stage >= stageMax ? "MAX" : "To Next Stage");
|
||||
strings.get(stage >= stageMax ? Strings::InfoProgressMax
|
||||
: Strings::InfoProgressToNextStage)
|
||||
.c_str());
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::Text("Stage: %i/%i (%0.1f%%)", stage + 1, stageMax + 1, math::to_percent(stageProgress));
|
||||
ImGui::Text(strings.get(Strings::InfoStageProgressFormat).c_str(), stage + 1, stageMax + 1,
|
||||
math::to_percent(stageProgress));
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imgui::to_imvec4(color::GRAY));
|
||||
if (stage >= stageMax)
|
||||
ImGui::Text("Maxed out!");
|
||||
ImGui::TextUnformatted(strings.get(Strings::InfoMaxedOut).c_str());
|
||||
else
|
||||
{
|
||||
ImGui::Text("Start: %0.2f %s", stageWeight, unitString);
|
||||
ImGui::Text("Current: %0.2f %s", weight, unitString);
|
||||
ImGui::Text("Next: %0.2f %s", stageNextWeight, unitString);
|
||||
ImGui::Text(strings.get(Strings::InfoStageStartFormat).c_str(), stageWeight, unitString);
|
||||
ImGui::Text(strings.get(Strings::InfoStageCurrentFormat).c_str(), weight, unitString);
|
||||
ImGui::Text(strings.get(Strings::InfoStageNextFormat).c_str(), stageNextWeight, unitString);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
@@ -82,7 +87,7 @@ namespace game::state::play
|
||||
auto overstuffedPercent = std::max(0.0f, (calories - capacity) / (character.max_capacity() - capacity));
|
||||
auto caloriesColor = ImVec4(1.0f, 1.0f - overstuffedPercent, 1.0f - overstuffedPercent, 1.0f);
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::ABOVE_AVERAGE);
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_1);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, caloriesColor);
|
||||
auto caloriesString = std::format("{:.0f} kcal / {:.0f} kcal", calories,
|
||||
character.is_over_capacity() ? character.max_capacity() : character.capacity);
|
||||
@@ -95,14 +100,16 @@ namespace game::state::play
|
||||
? (float)character.digestionTimer / character.data.digestionTimerMax
|
||||
: character.digestionProgress / entity::Character::DIGESTION_MAX;
|
||||
ImGui::ProgressBar(digestionProgress, ImVec2(ImGui::GetContentRegionAvail().x, 0),
|
||||
character.isDigesting ? "Digesting..." : "Digestion");
|
||||
strings.get(character.isDigesting ? Strings::InfoDigesting
|
||||
: Strings::InfoDigestion)
|
||||
.c_str());
|
||||
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
if (character.isDigesting)
|
||||
ImGui::TextUnformatted("Digestion in progress...");
|
||||
ImGui::TextUnformatted(strings.get(Strings::InfoDigestionInProgress).c_str());
|
||||
else if (digestionProgress <= 0.0f)
|
||||
ImGui::TextUnformatted("Give food to start digesting!");
|
||||
ImGui::TextUnformatted(strings.get(Strings::InfoGiveFoodToStartDigesting).c_str());
|
||||
else
|
||||
ImGui::Text("%0.2f%%", math::to_percent(digestionProgress));
|
||||
|
||||
@@ -110,8 +117,8 @@ namespace game::state::play
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||
|
||||
ImGui::Text("Rate: %0.2f%% / sec", character.digestion_rate_get());
|
||||
ImGui::Text("Eating Speed: %0.2fx", character.eatSpeed);
|
||||
ImGui::Text(strings.get(Strings::InfoDigestionRateFormat).c_str(), character.digestion_rate_get());
|
||||
ImGui::Text(strings.get(Strings::InfoEatingSpeedFormat).c_str(), character.eatSpeed);
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
|
||||
57
src/state/play/interact.cpp
Normal file
57
src/state/play/interact.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "interact.hpp"
|
||||
|
||||
#include "../../util/imgui/widget.hpp"
|
||||
#include "../../util/measurement.hpp"
|
||||
|
||||
using namespace game::resource;
|
||||
using namespace game::resource::xml;
|
||||
using namespace game::util;
|
||||
using namespace game::util::imgui;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
void Interact::update(Resources& resources, Text& text, entity::Character& character)
|
||||
{
|
||||
auto& dialogue = character.data.dialogue;
|
||||
auto& strings = character.data.strings;
|
||||
auto size = ImGui::GetContentRegionAvail();
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_2);
|
||||
|
||||
if (dialogue.random.is_valid())
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InteractChatButton).c_str(), ImVec2(size.x, 0))))
|
||||
text.set(dialogue.get(dialogue.random), character);
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
if (dialogue.help.is_valid())
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InteractHelpButton).c_str(), ImVec2(size.x, 0))))
|
||||
text.set(dialogue.get(dialogue.help), character);
|
||||
|
||||
auto stage = glm::clamp(0, character.stage_get(), character.stage_max_get());
|
||||
auto& pool = stage > 0 ? character.data.stages.at(stage - 1).pool : character.data.pool;
|
||||
|
||||
if (pool.is_valid())
|
||||
if (WIDGET_FX(
|
||||
ImGui::Button(strings.get(Strings::InteractFeelingButton).c_str(), ImVec2(size.x, 0))))
|
||||
text.set(dialogue.get(pool), character);
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_1);
|
||||
ImGui::SeparatorText(character.data.name.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
auto& system = resources.settings.measurementSystem;
|
||||
auto weight = character.weight_get(system);
|
||||
auto weightUnit = system == measurement::IMPERIAL ? "lbs" : "kg";
|
||||
|
||||
ImGui::Text(strings.get(Strings::InteractWeightFormat).c_str(), weight, weightUnit,
|
||||
character.stage_get() + 1);
|
||||
ImGui::Text(strings.get(Strings::InteractCapacityFormat).c_str(), character.capacity,
|
||||
character.max_capacity());
|
||||
ImGui::Text(strings.get(Strings::InteractDigestionRateFormat).c_str(), character.digestion_rate_get());
|
||||
ImGui::Text(strings.get(Strings::InteractEatingSpeedFormat).c_str(), character.eatSpeed);
|
||||
ImGui::Separator();
|
||||
ImGui::Text(strings.get(Strings::InteractTotalCaloriesFormat).c_str(), character.totalCaloriesConsumed);
|
||||
ImGui::Text(strings.get(Strings::InteractTotalFoodItemsFormat).c_str(), character.totalFoodItemsEaten);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
class Chat
|
||||
class Interact
|
||||
{
|
||||
public:
|
||||
void update(Resources&, Text&, entity::Character&);
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "inventory.hpp"
|
||||
#include "style.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
@@ -7,6 +8,7 @@
|
||||
|
||||
#include "../../util/color.hpp"
|
||||
#include "../../util/imgui.hpp"
|
||||
#include "../../util/imgui/style.hpp"
|
||||
#include "../../util/imgui/widget.hpp"
|
||||
#include "../../util/math.hpp"
|
||||
|
||||
@@ -18,6 +20,8 @@ using namespace glm;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
using Strings = resource::xml::Strings;
|
||||
|
||||
void Inventory::tick()
|
||||
{
|
||||
for (auto& [i, actor] : actors)
|
||||
@@ -29,6 +33,7 @@ namespace game::state::play
|
||||
static constexpr auto INFO_CHILD_HEIGHT_MULTIPLIER = 1.0f / 3.0f;
|
||||
|
||||
auto& schema = character.data.itemSchema;
|
||||
auto& strings = character.data.strings;
|
||||
|
||||
auto quantity_get = [&](int itemID) -> int&
|
||||
{
|
||||
@@ -46,6 +51,77 @@ namespace game::state::play
|
||||
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_summary_draw = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||
{
|
||||
auto& category = schema.categories[item.categoryID];
|
||||
auto& rarity = schema.rarities[item.rarityID];
|
||||
auto durability = item.durability.value_or(schema.durability);
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
ImGui::TextWrapped("%s (x%i)", item.name.c_str(), quantity);
|
||||
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(strings.get(Strings::InventoryFlavorFormat).c_str(),
|
||||
schema.flavors[*item.flavorID].name.c_str());
|
||||
if (item.calories.has_value())
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryCaloriesFormat).c_str(), *item.calories);
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryDurabilityFormat).c_str(), durability);
|
||||
if (item.capacityBonus.has_value())
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryCapacityBonusFormat).c_str(), *item.capacityBonus);
|
||||
if (item.digestionBonus.has_value())
|
||||
{
|
||||
if (*item.digestionBonus > 0)
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryDigestionRateBonusFormat).c_str(),
|
||||
*item.digestionBonus * 60.0f);
|
||||
else if (*item.digestionBonus < 0)
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryDigestionRatePenaltyFormat).c_str(),
|
||||
*item.digestionBonus * 60.0f);
|
||||
}
|
||||
if (item.eatSpeedBonus.has_value())
|
||||
{
|
||||
if (*item.eatSpeedBonus > 0)
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryEatSpeedBonusFormat).c_str(), *item.eatSpeedBonus);
|
||||
else if (*item.eatSpeedBonus < 0)
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryEatSpeedPenaltyFormat).c_str(), *item.eatSpeedBonus);
|
||||
}
|
||||
if (is_possible_to_upgrade_get(item))
|
||||
ImGui::TextWrapped(strings.get(Strings::InventoryUpgradePreviewFormat).c_str(), *item.upgradeCount,
|
||||
schema.idToStringMap.at(*item.upgradeID).c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Separator();
|
||||
};
|
||||
|
||||
auto item_details_draw = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||
{
|
||||
item_summary_draw(item, quantity);
|
||||
|
||||
if (ImGui::BeginChild("##Info Description Child", ImGui::GetContentRegionAvail()))
|
||||
ImGui::TextWrapped("%s", item.description.c_str());
|
||||
ImGui::EndChild();
|
||||
};
|
||||
|
||||
auto item_tooltip_draw = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||
{
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 24.0f);
|
||||
item_summary_draw(item, quantity);
|
||||
ImGui::TextWrapped("%s", item.description.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
};
|
||||
|
||||
auto item_unknown_draw = [&]()
|
||||
{
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 24.0f);
|
||||
ImGui::TextWrapped("%s", strings.get(Strings::InventoryUnknown).c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::PopFont();
|
||||
};
|
||||
|
||||
auto item_use = [&](int itemID)
|
||||
{
|
||||
auto& item = schema.items[itemID];
|
||||
@@ -176,6 +252,7 @@ namespace game::state::play
|
||||
auto& item = schema.items[i];
|
||||
auto& quantity = quantity_get(i);
|
||||
auto& rarity = schema.rarities[item.rarityID];
|
||||
auto hasItemColor = item.color.has_value();
|
||||
|
||||
if (rarity.isHidden && quantity <= 0) continue;
|
||||
|
||||
@@ -185,6 +262,7 @@ namespace game::state::play
|
||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||
auto [canvas, rect] = item_canvas_get(i, size);
|
||||
auto isSelected = selectedItemID == i;
|
||||
if (hasItemColor) imgui::style::color_set(*item.color);
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
@@ -201,8 +279,16 @@ namespace game::state::play
|
||||
isAnyInventoryItemHovered = isAnyInventoryItemHovered || ImGui::IsItemHovered();
|
||||
if (isPressed) selectedItemID = i;
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && quantity > 0) item_use(i);
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
if (quantity > 0)
|
||||
item_tooltip_draw(item, quantity);
|
||||
else
|
||||
item_unknown_draw();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
|
||||
auto text = std::format("x{}", quantity);
|
||||
auto textPos = ImVec2(cursorScreenPos.x + size.x - ImGui::CalcTextSize(text.c_str()).x,
|
||||
@@ -210,6 +296,7 @@ namespace game::state::play
|
||||
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
|
||||
text.c_str());
|
||||
ImGui::PopFont();
|
||||
if (hasItemColor) style::color_set(resources, character);
|
||||
|
||||
auto increment = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
|
||||
cursorPos.x += increment;
|
||||
@@ -231,13 +318,15 @@ namespace game::state::play
|
||||
isItemSelected = selectedItemID >= 0 && selectedItemID < (int)schema.items.size();
|
||||
auto selectedQuantity = isItemSelected ? quantity_get(selectedItemID) : 0;
|
||||
auto isSelectedItemKnown = isItemSelected && selectedQuantity > 0;
|
||||
auto selectedItemHasColor = isItemSelected && schema.items[selectedItemID].color.has_value();
|
||||
|
||||
if (isInfoVisible &&
|
||||
ImGui::BeginChild("##Info Child", infoChildSize, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
if (selectedItemHasColor) imgui::style::color_set(*schema.items[selectedItemID].color);
|
||||
ImGui::Separator();
|
||||
auto isButtonChildVisible = selectedQuantity > 0;
|
||||
ImGui::PushFont(resources.font.get(), Font::BIG);
|
||||
ImGui::PushFont(resources.font.get(), Font::HEADER_2);
|
||||
auto buttonRowHeight = ImGui::GetFrameHeight();
|
||||
auto buttonChildHeight =
|
||||
isButtonChildVisible ? buttonRowHeight * 2.0f + ImGui::GetStyle().ItemSpacing.y * 5.0f : 0.0f;
|
||||
@@ -252,56 +341,18 @@ namespace game::state::play
|
||||
{
|
||||
if (!isItemSelected)
|
||||
{
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
ImGui::TextWrapped("%s", "Check the \"Arcade\" tab to earn rewards!");
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
ImGui::TextWrapped("%s", strings.get(Strings::InventoryEmptyHint).c_str());
|
||||
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());
|
||||
}
|
||||
item_details_draw(item, selectedQuantity);
|
||||
else
|
||||
{
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
ImGui::TextWrapped("%s", "???");
|
||||
ImGui::PopFont();
|
||||
}
|
||||
item_unknown_draw();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@@ -310,28 +361,75 @@ namespace game::state::play
|
||||
ImGui::BeginChild("##Info Actions Child", buttonChildSize, ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
auto& selectedItem = schema.items[selectedItemID];
|
||||
auto canUseSelectedItem = true;
|
||||
auto canUpgradeSelectedItem = is_able_to_upgrade_get(schema.items[selectedItemID], selectedQuantity);
|
||||
auto canUpgradeSelectedItem = is_able_to_upgrade_get(selectedItem, selectedQuantity);
|
||||
auto rowTwoButtonSize = row_widget_size_get(2);
|
||||
|
||||
auto upgrade_item_name_get = [&]() -> std::string
|
||||
{
|
||||
if (!selectedItem.upgradeID.has_value()) return {};
|
||||
return schema.items.at(*selectedItem.upgradeID).name;
|
||||
};
|
||||
|
||||
auto upgrade_tooltip_get = [&](bool isAll)
|
||||
{
|
||||
if (!is_possible_to_upgrade_get(selectedItem))
|
||||
return strings.get(Strings::InventoryUpgradeNoPath);
|
||||
|
||||
auto upgradeItemName = upgrade_item_name_get();
|
||||
auto upgradeCount = *selectedItem.upgradeCount;
|
||||
|
||||
if (!canUpgradeSelectedItem)
|
||||
return std::vformat(strings.get(Strings::InventoryUpgradeNeedsTemplate),
|
||||
std::make_format_args(upgradeCount, upgradeItemName));
|
||||
|
||||
if (!isAll)
|
||||
return std::vformat(strings.get(Strings::InventoryUpgradeOneTemplate),
|
||||
std::make_format_args(upgradeCount, upgradeItemName));
|
||||
|
||||
auto upgradedCount = selectedQuantity / upgradeCount;
|
||||
return std::vformat(strings.get(Strings::InventoryUpgradeAllTemplate),
|
||||
std::make_format_args(upgradeCount, upgradedCount, upgradeItemName));
|
||||
};
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0, ImGui::GetStyle().ItemSpacing.y));
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
|
||||
ImGui::BeginDisabled(!canUseSelectedItem);
|
||||
if (WIDGET_FX(ImGui::Button("Spawn", {ImGui::GetContentRegionAvail().x, 0}))) item_use(selectedItemID);
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InventorySpawnButton).c_str(),
|
||||
{ImGui::GetContentRegionAvail().x, 0})))
|
||||
item_use(selectedItemID);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::BeginDisabled(!canUpgradeSelectedItem);
|
||||
if (WIDGET_FX(ImGui::Button("Upgrade", rowTwoButtonSize))) item_upgrade(selectedItemID, false);
|
||||
if (WIDGET_FX(
|
||||
ImGui::Button(strings.get(Strings::InventoryUpgradeButton).c_str(), rowTwoButtonSize)))
|
||||
item_upgrade(selectedItemID, false);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
{
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
||||
ImGui::SetItemTooltip("%s", upgrade_tooltip_get(false).c_str());
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (WIDGET_FX(ImGui::Button("Upgrade All", rowTwoButtonSize))) item_upgrade(selectedItemID, true);
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InventoryUpgradeAllButton).c_str(),
|
||||
rowTwoButtonSize)))
|
||||
item_upgrade(selectedItemID, true);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
{
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
||||
ImGui::SetItemTooltip("%s", upgrade_tooltip_get(true).c_str());
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::PopFont();
|
||||
}
|
||||
if (isButtonChildVisible) ImGui::EndChild();
|
||||
if (selectedItemHasColor) style::color_set(resources, character);
|
||||
}
|
||||
if (isInfoVisible) ImGui::EndChild();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,18 @@ using namespace glm;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
namespace
|
||||
{
|
||||
int durability_animation_index_get(const resource::xml::Item& schema, const resource::xml::Anm2& anm2, int durability,
|
||||
int durabilityMax)
|
||||
{
|
||||
if (durability >= durabilityMax) return -1;
|
||||
|
||||
auto animationName = schema.animations.chew + std::to_string(std::max(0, durability));
|
||||
return anm2.animationMap.contains(animationName) ? anm2.animationMap.at(animationName) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ItemManager::update(entity::Character& character, entity::Cursor& cursor, AreaManager& areaManager, Text& text,
|
||||
const glm::vec4& bounds, Canvas& canvas)
|
||||
{
|
||||
@@ -70,7 +82,11 @@ namespace game::state::play
|
||||
math::random_in_range(spawnBounds.y, spawnBounds.y + spawnBounds.w));
|
||||
|
||||
auto& itemSchema = character.data.itemSchema;
|
||||
items.emplace_back(itemSchema.anm2s.at(id), position, id);
|
||||
auto& item = itemSchema.items.at(id);
|
||||
auto& anm2 = itemSchema.anm2s.at(id);
|
||||
auto durabilityMax = item.durability.value_or(itemSchema.durability);
|
||||
auto animationIndex = durability_animation_index_get(itemSchema, anm2, 0, durabilityMax);
|
||||
items.emplace_back(anm2, position, id, 0, animationIndex);
|
||||
}
|
||||
queuedItemIDs.clear();
|
||||
|
||||
@@ -97,15 +113,15 @@ namespace game::state::play
|
||||
|
||||
if (schema.categories[item.categoryID].isEdible)
|
||||
{
|
||||
auto& chewCountMax = item.chewCount.has_value() ? *item.chewCount : schema.chewCount;
|
||||
auto caloriesChew = item.calories.has_value() ? *item.calories / (chewCountMax + 1) : 0;
|
||||
auto isCanEat = character.calories + caloriesChew <= character.max_capacity();
|
||||
auto& durabilityMax = item.durability.has_value() ? *item.durability : schema.durability;
|
||||
auto caloriesPerBite = item.calories.has_value() && durabilityMax > 0 ? *item.calories / durabilityMax : 0;
|
||||
auto isCanEat = character.calories + caloriesPerBite <= character.max_capacity();
|
||||
|
||||
if (isJustItemHeld)
|
||||
{
|
||||
if (isCanEat)
|
||||
text.set(dialogue.get(isOverCapacity ? dialogue.feedFull : dialogue.feed), character);
|
||||
else if (caloriesChew > character.capacity)
|
||||
else if (caloriesPerBite > character.capacity)
|
||||
text.set(dialogue.get(dialogue.lowCapacity), character);
|
||||
else
|
||||
text.set(dialogue.get(dialogue.full), character);
|
||||
@@ -125,37 +141,44 @@ namespace game::state::play
|
||||
|
||||
if (character.playedEventID == eatArea.eventID)
|
||||
{
|
||||
heldItem->chewCount++;
|
||||
heldItem->durability++;
|
||||
character.consume_played_event();
|
||||
|
||||
auto chewAnimation = schema.animations.chew + std::to_string(heldItem->chewCount);
|
||||
auto animationIndex = heldItem->chewCount > 0 ? heldItem->animationMap[chewAnimation] : -1;
|
||||
|
||||
heldItem->play(animationIndex, entity::Actor::SET);
|
||||
|
||||
character.calories += caloriesChew;
|
||||
character.totalCaloriesConsumed += caloriesChew;
|
||||
character.calories += caloriesPerBite;
|
||||
character.totalCaloriesConsumed += caloriesPerBite;
|
||||
|
||||
if (item.capacityBonus.has_value())
|
||||
{
|
||||
character.capacity += *item.capacityBonus / durabilityMax;
|
||||
character.capacity =
|
||||
glm::clamp(character.capacity, (float)character.data.capacityMin, (float)character.data.capacityMax);
|
||||
}
|
||||
if (item.eatSpeedBonus.has_value())
|
||||
{
|
||||
character.eatSpeed += *item.eatSpeedBonus / (chewCountMax + 1);
|
||||
character.eatSpeed =
|
||||
glm::clamp(character.data.eatSpeedMin, character.eatSpeed, character.data.eatSpeedMax);
|
||||
character.eatSpeed += *item.eatSpeedBonus / durabilityMax;
|
||||
character.eatSpeed = glm::clamp(character.eatSpeed, (float)character.data.eatSpeedMin,
|
||||
(float)character.data.eatSpeedMax);
|
||||
}
|
||||
if (item.digestionBonus.has_value())
|
||||
{
|
||||
character.digestionRate += *item.digestionBonus / (chewCountMax + 1);
|
||||
character.digestionRate = glm::clamp(character.data.digestionRateMin, character.digestionRate,
|
||||
character.data.digestionRateMax);
|
||||
character.digestionRate += *item.digestionBonus / durabilityMax;
|
||||
character.digestionRate = glm::clamp(character.digestionRate, (float)character.data.digestionRateMin,
|
||||
(float)character.data.digestionRateMax);
|
||||
}
|
||||
|
||||
if (heldItem->chewCount > chewCountMax)
|
||||
if (heldItem->durability >= durabilityMax)
|
||||
{
|
||||
isQueueFinishFood = true;
|
||||
character.totalFoodItemsEaten++;
|
||||
queuedRemoveItemIndex = heldItemIndex;
|
||||
heldItemIndex = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto animationIndex =
|
||||
durability_animation_index_get(schema, *heldItem, heldItem->durability, durabilityMax);
|
||||
heldItem->play(animationIndex, entity::Actor::SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +200,7 @@ namespace game::state::play
|
||||
|
||||
// Food stolen
|
||||
if (auto animation = character.animation_get(character.animation_name_convert(eatArea.animation));
|
||||
character.is_playing(animation->name))
|
||||
animation && character.is_playing(animation->name))
|
||||
{
|
||||
if (!math::is_point_in_rectf(rect, heldItem->position))
|
||||
text.set(dialogue.get(isOverCapacity ? dialogue.foodTakenFull : dialogue.foodTaken), character);
|
||||
@@ -232,7 +255,7 @@ namespace game::state::play
|
||||
|
||||
if (isMouseRightClicked)
|
||||
{
|
||||
if (item.chewCount > 0)
|
||||
if (item.durability > 0)
|
||||
schema.sounds.dispose.play();
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "menu.hpp"
|
||||
|
||||
#include "style.hpp"
|
||||
|
||||
#include "../../util/imgui.hpp"
|
||||
#include "../../util/imgui/style.hpp"
|
||||
#include "../../util/imgui/widget.hpp"
|
||||
@@ -8,18 +10,14 @@
|
||||
|
||||
using namespace game::util;
|
||||
using namespace game::util::imgui;
|
||||
using namespace game::resource::xml;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
void Menu::tick()
|
||||
{
|
||||
inventory.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);
|
||||
arcade.tick();
|
||||
}
|
||||
|
||||
void Menu::update(Resources& resources, ItemManager& itemManager, entity::Character& character,
|
||||
@@ -28,6 +26,7 @@ namespace game::state::play
|
||||
static constexpr auto WIDTH_MULTIPLIER = 0.30f;
|
||||
|
||||
auto& schema = character.data.menuSchema;
|
||||
auto& strings = character.data.strings;
|
||||
|
||||
auto style = ImGui::GetStyle();
|
||||
auto& io = ImGui::GetIO();
|
||||
@@ -61,47 +60,41 @@ namespace game::state::play
|
||||
if (ImGui::BeginTabBar("##Options"))
|
||||
{
|
||||
|
||||
if (isChat && WIDGET_FX(ImGui::BeginTabItem("Chat")))
|
||||
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabInteract).c_str())))
|
||||
{
|
||||
chat.update(resources, text, character);
|
||||
interact.update(resources, text, character);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (WIDGET_FX(ImGui::BeginTabItem("Arcade")))
|
||||
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabArcade).c_str())))
|
||||
{
|
||||
skillCheck.update(resources, character, inventory, text);
|
||||
arcade.update(resources, character, inventory, text);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (WIDGET_FX(ImGui::BeginTabItem("Inventory")))
|
||||
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabInventory).c_str())))
|
||||
{
|
||||
inventory.update(resources, itemManager, character);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (WIDGET_FX(ImGui::BeginTabItem("Stats")))
|
||||
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabSettings).c_str())))
|
||||
{
|
||||
stats.update(resources, skillCheck, character);
|
||||
settingsMenu.update(resources, SettingsMenu::PLAY, &strings);
|
||||
if (settingsMenu.isJustColorSet) style::color_set(resources, character);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (WIDGET_FX(ImGui::BeginTabItem("Settings")))
|
||||
if (isCheats && WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabCheats).c_str())))
|
||||
{
|
||||
settingsMenu.update(resources, SettingsMenu::PLAY);
|
||||
if (settingsMenu.isJustColorSet) color_set_check(resources, character);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (isCheats && WIDGET_FX(ImGui::BeginTabItem("Cheats")))
|
||||
{
|
||||
cheats.update(resources, character, inventory, text);
|
||||
cheats.update(resources, character, inventory);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (WIDGET_FX(ImGui::BeginTabItem("Debug")))
|
||||
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabDebug).c_str())))
|
||||
{
|
||||
debug.update(character, cursor, itemManager, canvas);
|
||||
debug.update(character, cursor, itemManager, canvas, text);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
#endif
|
||||
@@ -128,7 +121,9 @@ namespace game::state::play
|
||||
|
||||
if (t <= 0.0f || t >= 1.0f)
|
||||
{
|
||||
ImGui::SetItemTooltip(isOpen ? "Close Main Menu" : "Open Main Menu");
|
||||
ImGui::SetItemTooltip("%s", strings.get(isOpen ? Strings::MenuCloseTooltip
|
||||
: Strings::MenuOpenTooltip)
|
||||
.c_str());
|
||||
if (result)
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
#include "../settings_menu.hpp"
|
||||
|
||||
#include "arcade/skill_check.hpp"
|
||||
#include "chat.hpp"
|
||||
#include "arcade.hpp"
|
||||
#include "cheats.hpp"
|
||||
#include "debug.hpp"
|
||||
#include "stats.hpp"
|
||||
#include "interact.hpp"
|
||||
#include "inventory.hpp"
|
||||
#include "text.hpp"
|
||||
|
||||
#include "../../util/imgui/window_slide.hpp"
|
||||
@@ -18,27 +18,24 @@ namespace game::state::play
|
||||
class Menu
|
||||
{
|
||||
public:
|
||||
SkillCheck skillCheck;
|
||||
Chat chat;
|
||||
Arcade arcade;
|
||||
Interact interact;
|
||||
Cheats cheats;
|
||||
Debug debug;
|
||||
Stats stats;
|
||||
Inventory inventory;
|
||||
|
||||
state::SettingsMenu settingsMenu;
|
||||
|
||||
#if DEBUG
|
||||
bool isCheats{true};
|
||||
#elif
|
||||
#else
|
||||
bool isCheats{};
|
||||
#endif
|
||||
|
||||
bool isOpen{true};
|
||||
bool isChat{true};
|
||||
util::imgui::WindowSlide slide{};
|
||||
|
||||
void tick();
|
||||
void update(Resources&, ItemManager&, entity::Character&, entity::Cursor&, Text&, Canvas&);
|
||||
void color_set_check(Resources&, entity::Character&);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "stats.hpp"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "../../util/measurement.hpp"
|
||||
|
||||
using namespace game::resource;
|
||||
using namespace game::util;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
void Stats::update(Resources& resources, SkillCheck& skillCheck, entity::Character& character)
|
||||
{
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
ImGui::TextUnformatted(character.data.name.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
auto& skillCheckSchema = character.data.skillCheckSchema;
|
||||
auto& system = resources.settings.measurementSystem;
|
||||
auto weight = character.weight_get(system);
|
||||
auto weightUnit = system == measurement::IMPERIAL ? "lbs" : "kg";
|
||||
|
||||
ImGui::Text("Weight: %0.2f %s (Stage: %i)", weight, weightUnit, character.stage_get() + 1);
|
||||
ImGui::Text("Capacity: %0.0f kcal (Max: %0.0f kcal)", character.capacity, character.max_capacity());
|
||||
ImGui::Text("Digestion Rate: %0.2f%%/sec", character.digestion_rate_get());
|
||||
ImGui::Text("Eating Speed: %0.2fx", character.eatSpeed);
|
||||
|
||||
ImGui::SeparatorText("Totals");
|
||||
|
||||
ImGui::Text("Total Calories Consumed: %0.0f kcal", character.totalCaloriesConsumed);
|
||||
ImGui::Text("Total Food Items Eaten: %i", character.totalFoodItemsEaten);
|
||||
|
||||
ImGui::SeparatorText("Skill Check");
|
||||
|
||||
ImGui::Text("Best: %i pts (%ix)", skillCheck.highScore, skillCheck.bestCombo);
|
||||
ImGui::Text("Total Skill Checks: %i", skillCheck.totalPlays);
|
||||
|
||||
for (int i = 0; i < (int)skillCheckSchema.grades.size(); i++)
|
||||
{
|
||||
auto& grade = skillCheckSchema.grades[i];
|
||||
ImGui::Text("%s: %i", grade.namePlural.c_str(), skillCheck.gradeCounts[i]);
|
||||
}
|
||||
|
||||
ImGui::Text("Accuracy: %0.2f%%", skillCheck.accuracy_score_get(character));
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../entity/character.hpp"
|
||||
#include "../../resources.hpp"
|
||||
|
||||
#include "arcade/skill_check.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
class Stats
|
||||
{
|
||||
public:
|
||||
void update(Resources&, SkillCheck&, entity::Character&);
|
||||
};
|
||||
}
|
||||
14
src/state/play/style.hpp
Normal file
14
src/state/play/style.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../entity/character.hpp"
|
||||
#include "../../resources.hpp"
|
||||
#include "../../util/imgui/style.hpp"
|
||||
|
||||
namespace game::state::play::style
|
||||
{
|
||||
inline void color_set(Resources& resources, const entity::Character& character)
|
||||
{
|
||||
game::util::imgui::style::color_set(resources.settings.isUseCharacterColor ? character.data.color
|
||||
: resources.settings.color);
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,12 @@ namespace game::state::play
|
||||
index = 0;
|
||||
time = 0.0f;
|
||||
isEnabled = true;
|
||||
character.isTalking = true;
|
||||
if (!dialogueEntry->animation.empty())
|
||||
character.queue_play({.animation = dialogueEntry->animation, .isInterruptible = isInterruptible});
|
||||
if (dialogueEntry->text.empty()) isEnabled = false;
|
||||
if (dialogueEntry->text.empty())
|
||||
isEnabled = false;
|
||||
else
|
||||
character.isTalking = true;
|
||||
}
|
||||
|
||||
void Text::tick(entity::Character& character)
|
||||
@@ -48,6 +50,8 @@ namespace game::state::play
|
||||
if (!entry || isFinished) return;
|
||||
|
||||
index++;
|
||||
auto blipPeriod = character.data.textBlipPeriodBase;
|
||||
if (blipPeriod > 0 && index % blipPeriod == 0) character.data.sounds.blip.play();
|
||||
|
||||
if (index >= ImTextCountCharsFromUtf8(entry->text.c_str(), entry->text.c_str() + entry->text.size()))
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
using namespace game::util;
|
||||
using namespace game::util::imgui;
|
||||
using namespace game::resource::xml;
|
||||
|
||||
namespace game::state::play
|
||||
{
|
||||
@@ -18,6 +19,7 @@ namespace game::state::play
|
||||
auto style = ImGui::GetStyle();
|
||||
auto& io = ImGui::GetIO();
|
||||
auto& schema = character.data.menuSchema;
|
||||
auto& strings = character.data.strings;
|
||||
|
||||
slide.update(isOpen, io.DeltaTime);
|
||||
|
||||
@@ -56,8 +58,9 @@ namespace game::state::play
|
||||
ImGui::PopStyleColor();
|
||||
};
|
||||
|
||||
if (WIDGET_FX(ImGui::Button("Home", buttonSize))) world.character_focus(character, canvas, focus);
|
||||
ImGui::SetItemTooltip("%s", "Reset camera view.\n(Shortcut: Home)");
|
||||
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ToolsHomeButton).c_str(), buttonSize)))
|
||||
world.character_focus(character, canvas, focus);
|
||||
ImGui::SetItemTooltip("%s", strings.get(Strings::ToolsHomeTooltip).c_str());
|
||||
|
||||
for (int i = 0; i < (int)character.data.interactTypeNames.size(); i++)
|
||||
cursor_mode_button(character.data.interactTypeNames[i], i);
|
||||
@@ -82,7 +85,9 @@ namespace game::state::play
|
||||
|
||||
if (t <= 0.0f || t >= 1.0f)
|
||||
{
|
||||
ImGui::SetItemTooltip(isOpen ? "Close Tools" : "Open Tools");
|
||||
ImGui::SetItemTooltip("%s", strings.get(isOpen ? Strings::ToolsCloseTooltip
|
||||
: Strings::ToolsOpenTooltip)
|
||||
.c_str());
|
||||
if (result)
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace game::state::play
|
||||
{
|
||||
static constexpr float MENU_WIDTH_MULTIPLIER = 0.30f;
|
||||
static constexpr float TOOLS_WIDTH_MULTIPLIER = 0.10f;
|
||||
static constexpr float INFO_HEIGHT_MULTIPLIER = 4.0f;
|
||||
static constexpr float PADDING = 100.0f;
|
||||
|
||||
auto rect = character.rect();
|
||||
@@ -72,13 +73,18 @@ namespace game::state::play
|
||||
|
||||
rect = {rect.x - PADDING * 0.5f, rect.y - PADDING * 0.5f, rect.z + PADDING, rect.w + PADDING};
|
||||
|
||||
auto zoomFactor = std::min((float)canvas.size.x / rect.z, (float)canvas.size.y / rect.w);
|
||||
auto infoHeightPixels =
|
||||
ImGui::GetTextLineHeightWithSpacing() * INFO_HEIGHT_MULTIPLIER + ImGui::GetStyle().WindowPadding.y * 2.0f;
|
||||
auto usableHeightPixels = std::max(1.0f, (float)canvas.size.y - infoHeightPixels);
|
||||
|
||||
auto zoomFactor = std::min((float)canvas.size.x / rect.z, usableHeightPixels / rect.w);
|
||||
canvas.zoom = glm::clamp(ZOOM_MIN, math::to_percent(zoomFactor), ZOOM_MAX);
|
||||
zoomFactor = math::to_unit(canvas.zoom);
|
||||
|
||||
auto rectCenter = glm::vec2(rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f);
|
||||
auto viewSizeWorld = glm::vec2(canvas.size) / zoomFactor;
|
||||
canvas.pan = rectCenter - (vec2(viewSizeWorld.x, viewSizeWorld.y) * 0.5f);
|
||||
auto infoHeightWorld = infoHeightPixels / zoomFactor;
|
||||
canvas.pan = rectCenter - vec2(viewSizeWorld.x * 0.5f, (viewSizeWorld.y + infoHeightWorld) * 0.5f);
|
||||
auto menuWidthWorld = (canvas.size.x * MENU_WIDTH_MULTIPLIER) / zoomFactor;
|
||||
auto toolsWidthWorld = (canvas.size.x * TOOLS_WIDTH_MULTIPLIER) / zoomFactor;
|
||||
|
||||
|
||||
@@ -47,38 +47,51 @@ namespace game::state::select
|
||||
ImGui::TextUnformatted(character.name.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
if (!character.description.empty())
|
||||
{
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
ImGui::TextWrapped("%s", character.description.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
||||
|
||||
ImGui::Text("Weight: %0.2f %s", system == IMPERIAL ? weight * KG_TO_LB : weight,
|
||||
system == IMPERIAL ? "lbs" : "kg");
|
||||
ImGui::Text("Stages: %i", character.stages);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
||||
if (ImGui::BeginTabBar("##Preview Tabs"))
|
||||
{
|
||||
if (ImGui::BeginTabItem("Overview"))
|
||||
{
|
||||
if (!character.description.empty())
|
||||
{
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
ImGui::TextWrapped("%s", character.description.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
if (!character.author.empty()) ImGui::TextWrapped("Author: %s", character.author.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
|
||||
ImGui::Text("Weight: %0.2f %s", system == IMPERIAL ? weight * KG_TO_LB : weight,
|
||||
system == IMPERIAL ? "lbs" : "kg");
|
||||
ImGui::Text("Stages: %i", character.stages);
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Credits"))
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||
if (!character.credits.empty())
|
||||
ImGui::TextWrapped("%s", character.credits.c_str());
|
||||
else
|
||||
ImGui::TextUnformatted("No credits listed.");
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace game::state::select
|
||||
|
||||
auto renderSize = ImVec2(textureSize.x * scale, textureSize.y * scale);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + (availableSize.x * 0.5f) - (renderSize.y * 0.5f),
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + (availableSize.x * 0.5f) - (renderSize.x * 0.5f),
|
||||
ImGui::GetCursorPosY() + (availableSize.y * 0.5f) - (renderSize.y * 0.5f)));
|
||||
|
||||
ImGui::Image(character.render.id, renderSize);
|
||||
|
||||
@@ -10,47 +10,61 @@
|
||||
|
||||
using namespace game::util;
|
||||
using namespace game::util::imgui;
|
||||
using namespace game::resource::xml;
|
||||
|
||||
namespace game::state
|
||||
{
|
||||
void SettingsMenu::update(Resources& resources, Mode mode)
|
||||
void SettingsMenu::update(Resources& resources, Mode mode, const Strings* strings)
|
||||
{
|
||||
auto& settings = resources.settings;
|
||||
auto& measurementSystem = settings.measurementSystem;
|
||||
auto& volume = settings.volume;
|
||||
auto& color = settings.color;
|
||||
auto string_get = [&](Strings::Type type, const char* fallback) -> const char*
|
||||
{
|
||||
return strings ? strings->get(type).c_str() : fallback;
|
||||
};
|
||||
|
||||
isJustColorSet = false;
|
||||
|
||||
ImGui::SeparatorText("Measurement System");
|
||||
WIDGET_FX(ImGui::RadioButton("Metric", (int*)&measurementSystem, measurement::METRIC));
|
||||
ImGui::SetItemTooltip("%s", "Use kilograms (kg).");
|
||||
ImGui::SeparatorText(string_get(Strings::SettingsMeasurementSystem, "Measurement System"));
|
||||
WIDGET_FX(ImGui::RadioButton(string_get(Strings::SettingsMetric, "Metric"),
|
||||
(int*)&measurementSystem, measurement::METRIC));
|
||||
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsMetricTooltip, "Use kilograms (kg)."));
|
||||
ImGui::SameLine();
|
||||
WIDGET_FX(ImGui::RadioButton("Imperial", (int*)&measurementSystem, measurement::IMPERIAL));
|
||||
ImGui::SetItemTooltip("%s", "Use pounds (lbs).");
|
||||
WIDGET_FX(ImGui::RadioButton(string_get(Strings::SettingsImperial, "Imperial"),
|
||||
(int*)&measurementSystem, measurement::IMPERIAL));
|
||||
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsImperialTooltip, "Use pounds (lbs)."));
|
||||
|
||||
ImGui::SeparatorText("Sound");
|
||||
if (WIDGET_FX(ImGui::SliderInt("Volume", &volume, 0, 100, "%d%%")))
|
||||
ImGui::SeparatorText(string_get(Strings::SettingsSound, "Sound"));
|
||||
if (WIDGET_FX(
|
||||
ImGui::SliderInt(string_get(Strings::SettingsVolume, "Volume"), &volume, 0, 100, "%d%%")))
|
||||
resources.volume_set(math::to_unit((float)volume));
|
||||
ImGui::SetItemTooltip("%s", "Adjust master volume.");
|
||||
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsVolumeTooltip, "Adjust master volume."));
|
||||
|
||||
ImGui::SeparatorText("Appearance");
|
||||
ImGui::SeparatorText(string_get(Strings::SettingsAppearance, "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.");
|
||||
if (WIDGET_FX(ImGui::Checkbox(string_get(Strings::SettingsUseCharacterColor,
|
||||
"Use Character Color"),
|
||||
&settings.isUseCharacterColor)))
|
||||
isJustColorSet = true;
|
||||
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsUseCharacterColorTooltip,
|
||||
"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)))
|
||||
ImGui::ColorEdit3(string_get(Strings::SettingsColor, "Color"), value_ptr(color),
|
||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip)))
|
||||
{
|
||||
style::color_set(color);
|
||||
isJustColorSet = true;
|
||||
}
|
||||
ImGui::SetItemTooltip("%s", "Change the UI color.");
|
||||
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsColorTooltip, "Change the UI color."));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Separator();
|
||||
if (WIDGET_FX(ImGui::Button("Reset to Default", ImVec2(-FLT_MIN, 0))))
|
||||
if (WIDGET_FX(ImGui::Button(string_get(Strings::SettingsResetButton, "Reset to Default"),
|
||||
ImVec2(-FLT_MIN, 0))))
|
||||
{
|
||||
settings = resource::xml::Settings();
|
||||
style::color_set(settings.color);
|
||||
@@ -60,11 +74,19 @@ namespace game::state
|
||||
{
|
||||
ImGui::Separator();
|
||||
|
||||
if (WIDGET_FX(ImGui::Button("Save", ImVec2(-FLT_MIN, 0)))) isSave = true;
|
||||
ImGui::SetItemTooltip("%s", "Save the game.\n(Note: the game autosaves frequently.)");
|
||||
if (WIDGET_FX(
|
||||
ImGui::Button(string_get(Strings::SettingsSaveButton, "Save"), ImVec2(-FLT_MIN, 0))))
|
||||
isSave = true;
|
||||
ImGui::SetItemTooltip(
|
||||
"%s", string_get(Strings::SettingsSaveTooltip,
|
||||
"Save the game.\n(Note: the game autosaves frequently.)"));
|
||||
|
||||
if (WIDGET_FX(ImGui::Button("Return to Characters", ImVec2(-FLT_MIN, 0)))) isGoToSelect = true;
|
||||
ImGui::SetItemTooltip("%s", "Go back to the character selection screen.\nProgress will be saved.");
|
||||
if (WIDGET_FX(ImGui::Button(
|
||||
string_get(Strings::SettingsReturnToCharactersButton, "Return to Characters"),
|
||||
ImVec2(-FLT_MIN, 0))))
|
||||
isGoToSelect = true;
|
||||
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsReturnToCharactersTooltip,
|
||||
"Go back to the character selection screen.\nProgress will be saved."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../resource/xml/strings.hpp"
|
||||
#include "../resources.hpp"
|
||||
|
||||
namespace game::state
|
||||
@@ -17,6 +18,6 @@ namespace game::state
|
||||
bool isSave{};
|
||||
bool isJustColorSet{};
|
||||
|
||||
void update(Resources&, Mode = SELECT);
|
||||
void update(Resources&, Mode = SELECT, const resource::xml::Strings* = nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,16 +18,20 @@ namespace game::util::imgui::style
|
||||
static constexpr auto COLOR_BG_ALPHA = 0.90f;
|
||||
static constexpr auto COLOR_ACTIVE_MULTIPLIER = 1.30f;
|
||||
static constexpr auto COLOR_HOVERED_MULTIPLIER = 1.60f;
|
||||
static constexpr auto COLOR_ACTIVE_HOVERED_MIN = 0.1f;
|
||||
static constexpr auto COLOR_ACTIVE_HOVERED_MAX = 0.9f;
|
||||
static constexpr auto COLOR_ACCENT_MULTIPLIER = 2.0f;
|
||||
static constexpr auto COLOR_ACCENT_ACTIVE_MULTIPLIER = 2.25f;
|
||||
|
||||
auto& colors = ImGui::GetStyle().Colors;
|
||||
auto active_hovered_clamp = [](glm::vec3 color)
|
||||
{ return glm::clamp(color, glm::vec3(COLOR_ACTIVE_HOVERED_MIN), glm::vec3(COLOR_ACTIVE_HOVERED_MAX)); };
|
||||
|
||||
auto colorNew = to_imvec4(glm::vec4(color, 1.0f));
|
||||
auto colorBg = to_imvec4(glm::vec4(color * COLOR_BG_MULTIPLIER, COLOR_BG_ALPHA));
|
||||
auto colorChildBg = to_imvec4(glm::vec4(color * COLOR_BG_MULTIPLIER, 0.0f));
|
||||
auto colorActive = to_imvec4(glm::vec4(color * COLOR_ACTIVE_MULTIPLIER, 1.0f));
|
||||
auto colorHovered = to_imvec4(glm::vec4(color * COLOR_HOVERED_MULTIPLIER, 1.0f));
|
||||
auto colorActive = to_imvec4(glm::vec4(active_hovered_clamp(color * COLOR_ACTIVE_MULTIPLIER), 1.0f));
|
||||
auto colorHovered = to_imvec4(glm::vec4(active_hovered_clamp(color * COLOR_HOVERED_MULTIPLIER), 1.0f));
|
||||
auto colorAccent = to_imvec4(glm::vec4(color * COLOR_ACCENT_MULTIPLIER, 1.0f));
|
||||
auto colorAccentActive = to_imvec4(glm::vec4(color * COLOR_ACCENT_ACTIVE_MULTIPLIER, 1.0f));
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ namespace game::util::preferences
|
||||
std::filesystem::path path()
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
static constexpr auto filePath = "/snivy";
|
||||
static constexpr auto filePath = "/shweets-sim";
|
||||
std::filesystem::create_directories(filePath);
|
||||
return filePath;
|
||||
#else
|
||||
auto sdlPath = SDL_GetPrefPath(nullptr, "snivy");
|
||||
auto sdlPath = SDL_GetPrefPath(nullptr, "shweets-sim");
|
||||
if (!sdlPath) return {};
|
||||
auto filePath = std::filesystem::path(sdlPath);
|
||||
std::filesystem::create_directories(filePath);
|
||||
|
||||
@@ -12,12 +12,12 @@ namespace game::util::web_filesystem
|
||||
Module.filesystemReady = 0;
|
||||
try
|
||||
{
|
||||
FS.mkdir('/snivy');
|
||||
FS.mkdir('/shweets-sim');
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
}
|
||||
FS.mount(IDBFS, {}, '/snivy');
|
||||
FS.mount(IDBFS, {}, '/shweets-sim');
|
||||
FS.syncfs(
|
||||
true, function(err) {
|
||||
if (err) console.error('IDBFS init sync failed', err);
|
||||
@@ -50,4 +50,4 @@ namespace game::util::web_filesystem
|
||||
idbfs_flush_async();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user