This commit is contained in:
shweet committed 2026-08-09 00:20:29 -04:00
1 parent a5904b917b
commit 019a2f73fb
78 files changed
+197829 -191493

No files matched your search

+4
View File
@@ -730,6 +730,7 @@ namespace game::resource::xml
X(KILL, soundKill) \
X(LEVEL_UP, soundLevelUp) \
X(LEVEL_UP_READY, soundLevelUpReady) \
X(NEW_ITEM, soundNewItem) \
X(OPEN, soundOpen) \
X(RELEASE, soundRelease) \
X(SELECT, soundSelect) \
@@ -804,6 +805,7 @@ namespace game::resource::xml
root->soundKill = {};
root->soundLevelUp = {};
root->soundLevelUpReady = {};
root->soundNewItem = {};
root->soundOpen = {};
root->soundRelease = {};
root->soundSelect = {};
@@ -873,6 +875,8 @@ namespace game::resource::xml
sound_entry_add(*element, root->soundLevelUp);
for (auto* element : schema.children_get(*sounds, Schema::Element::LEVEL_UP_READY))
sound_entry_add(*element, root->soundLevelUpReady);
for (auto* element : schema.children_get(*sounds, Schema::Element::NEW_ITEM))
sound_entry_add(*element, root->soundNewItem);
for (auto* element : schema.children_get(*sounds, Schema::Element::OPEN))
sound_entry_add(*element, root->soundOpen);
for (auto* element : schema.children_get(*sounds, Schema::Element::RELEASE))
+2
View File
@@ -97,6 +97,7 @@
X(LowCapacity, LOW_CAPACITY) \
X(Menu, MENU) \
X(New, NEW) \
X(NewItem, NEW_ITEM) \
X(Null, NULL_) \
X(NullAnimation, NULL_ANIMATION) \
X(NullAnimations, NULL_ANIMATIONS) \
@@ -468,6 +469,7 @@
X(DATA, MEMBER, soundLevelUp, SoundEntryCollection, "Sound Level Up", NONE, \
"", "", "", soundLevelUp) \
X(DATA, MEMBER, soundLevelUpReady, SoundEntryCollection, "Sound Level Up Ready", NONE, "", "", "", soundLevelUpReady) \
X(DATA, MEMBER, soundNewItem, SoundEntryCollection, "Sound New Item", NONE, "", "", "", soundNewItem) \
X(DATA, MEMBER, soundOpen, SoundEntryCollection, "Sound Open", NONE, "", "", "", \
soundOpen) \
X(DATA, MEMBER, soundRelease, SoundEntryCollection, "Sound Release", NONE, \
+4 -3
View File
@@ -21,7 +21,7 @@ namespace game::resource::xml
X(InteractSpawnItemCooldownFormat, "TextInteractSpawnItemCooldownFormat", "Next item in %0.2fs") \
X(InteractStats, "TextInteractStats", "Stats") \
X(InteractWeightFormat, "TextInteractWeightFormat", "Weight: %0.2f %s (Stage: %i)") \
X(InteractCapacityFormat, "TextInteractCapacityFormat", "Capacity: %0.0f kcal (Max: %0.0f kcal)") \
X(InteractCapacityFormat, "TextInteractCapacityFormat", "Capacity: %0.0f / %0.0f kcal") \
X(InteractDigestionRateFormat, "TextInteractDigestionRateFormat", "Digestion Rate: %0.2f%%/sec") \
X(InteractTotalCaloriesFormat, "TextInteractTotalCaloriesFormat", "Total Calories Consumed: %0.0f kcal") \
X(InteractTotalItemsSpawnedFormat, "TextInteractTotalItemsSpawnedFormat", "Total Items Spawned: %i") \
@@ -35,9 +35,9 @@ namespace game::resource::xml
X(SettingsFullscreen, "TextSettingsFullscreen", "Fullscreen") \
X(SettingsMeasurementSystem, "TextSettingsMeasurementSystem", "Measurement System") \
X(SettingsMetric, "TextSettingsMetric", "Metric") \
X(SettingsMetricTooltip, "TextSettingsMetricTooltip", "Use kilograms (kg).") \
X(SettingsMetricTooltip, "TextSettingsMetricTooltip", "Use kilograms/grams (kg/g).") \
X(SettingsImperial, "TextSettingsImperial", "Imperial") \
X(SettingsImperialTooltip, "TextSettingsImperialTooltip", "Use pounds (lbs).") \
X(SettingsImperialTooltip, "TextSettingsImperialTooltip", "Use pounds/ounces (lbs/oz).") \
X(SettingsSound, "TextSettingsSound", "Sound") \
X(SettingsVolume, "TextSettingsVolume", "Volume") \
X(SettingsVolumeTooltip, "TextSettingsVolumeTooltip", "Adjust master volume.") \
@@ -57,6 +57,7 @@ namespace game::resource::xml
"Go back to the character selection screen.\nProgress will be saved.") \
X(ToastCheatsUnlocked, "TextToastCheatsUnlocked", "Cheats unlocked!") \
X(ToastSaving, "TextToastSaving", "Saving...") \
X(ToastNewItemFound, "TextToastNewItemFound", "New item found!") \
X(ToolsHomeButton, "TextToolsHomeButton", "Home") \
X(ToolsHomeTooltip, "TextToolsHomeTooltip", "Reset camera view.\n(Shortcut: Home)") \
X(ToolsOpenTooltip, "TextToolsOpenTooltip", "Open Tools") \
+6
View File
@@ -311,6 +311,12 @@ namespace game::state
auto& interactionCursor = autofeed.active_get() ? autofeedCursor : cursor;
itemManager.isAutomation = autofeed.active_get();
itemManager.update(character, interactionCursor, areaManager, text, world.bounds, worldCanvas);
auto* itemRoot = character.data.itemSchema.root();
for (int i = 0; i < itemManager.newItemFoundCount; i++)
{
toasts.push(character.data.strings.get(Strings::ToastNewItemFound));
if (itemRoot) itemRoot->soundNewItem.play();
}
characterManager.update(character, interactionCursor, text, worldCanvas);
for (auto& particle : itemManager.particles)
particleManager.spawn(character.data, particle.label, particle.position);
+55 -18
View File
@@ -15,10 +15,14 @@ namespace game::state::play
{
constexpr float CURSOR_SPEED = 9.0f;
constexpr float DIGEST_CURSOR_SPEED = 3.0f;
constexpr float CURSOR_ACCELERATION = 0.45f;
constexpr float DIGEST_CURSOR_ACCELERATION = 0.15f;
constexpr float CURSOR_SPEED_REFERENCE_AREA = 512.0f * 512.0f;
constexpr float CURSOR_SPEED_SCALE_MIN = 0.5f;
constexpr float CURSOR_SPEED_SCALE_MAX = 2.0f;
constexpr float CURSOR_BRAKING_ACCELERATION_MULTIPLIER = 2.0f;
constexpr float CALORIE_EPSILON = 0.001f;
constexpr float CURSOR_TARGET_EPSILON = 0.001f;
constexpr float FOOD_TIE_PICK_WEIGHT = 1.0f;
glm::vec2 rect_center(glm::vec4 rect) { return {rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f}; }
@@ -31,21 +35,51 @@ namespace game::state::play
value.z > 0.0f && value.w > 0.0f;
}
bool move_toward(Entity& cursor, glm::vec2 target, float speed)
bool move_toward(Entity& cursor, glm::vec2& velocity, glm::vec2 target, float speed, float acceleration)
{
if (!is_finite(cursor.position)) cursor.position = {};
if (!is_finite(cursor.position))
{
cursor.position = {};
velocity = {};
}
if (!is_finite(target)) return false;
auto delta = target - cursor.position;
auto distance = glm::length(delta);
if (!std::isfinite(distance)) return false;
if (distance <= speed || distance <= 0.001f)
if (distance <= CURSOR_TARGET_EPSILON)
{
cursor.position = target;
velocity = {};
return true;
}
cursor.position += delta / distance * speed;
auto targetSpeed = glm::min(speed, std::sqrt(CURSOR_BRAKING_ACCELERATION_MULTIPLIER * acceleration * distance));
auto desiredVelocity = delta / distance * targetSpeed;
auto velocityDelta = desiredVelocity - velocity;
auto velocityDeltaDistance = glm::length(velocityDelta);
if (!std::isfinite(velocityDeltaDistance))
velocity = {};
else if (velocityDeltaDistance > acceleration)
velocity += velocityDelta / velocityDeltaDistance * acceleration;
else
velocity = desiredVelocity;
auto speedCurrent = glm::length(velocity);
if (!std::isfinite(speedCurrent))
{
velocity = {};
return false;
}
if (speedCurrent >= distance)
{
cursor.position = target;
velocity = {};
return true;
}
cursor.position += velocity;
return false;
}
@@ -224,13 +258,14 @@ namespace game::state::play
return nullptr;
}
void state_reset(Entity& cursor, bool& isItemMouseHeld, bool& isDigestMouseHeld, int& targetItemIndex,
int& digestClickCooldown)
void state_reset(Entity& cursor, glm::vec2& cursorVelocity, bool& isItemMouseHeld, bool& isDigestMouseHeld,
int& targetItemIndex, int& digestClickCooldown)
{
isItemMouseHeld = false;
isDigestMouseHeld = false;
targetItemIndex = -1;
digestClickCooldown = 0;
cursorVelocity = {};
if (!is_finite(cursor.position)) cursor.position = {};
}
}
@@ -243,13 +278,13 @@ namespace game::state::play
if (!isEnabled)
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
if (character.isStageUp || character.isJustStageUp || character.isJustStageFinal)
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
@@ -257,7 +292,7 @@ namespace game::state::play
if (!is_food(character, itemManager))
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
@@ -271,6 +306,8 @@ namespace game::state::play
auto speedScale = cursor_speed_scale_get(character);
auto cursorSpeed = CURSOR_SPEED * speedScale;
auto digestCursorSpeed = DIGEST_CURSOR_SPEED * speedScale;
auto cursorAcceleration = CURSOR_ACCELERATION * speedScale;
auto digestCursorAcceleration = DIGEST_CURSOR_ACCELERATION * speedScale;
auto eatRect = glm::vec4{};
auto isEatTarget = eat_rect_get(character, eatRect);
@@ -281,7 +318,7 @@ namespace game::state::play
{
isActive = false;
cursor.isVisible = false;
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
auto& schemaItem = schema.items[heldItem.schemaID];
@@ -290,7 +327,7 @@ namespace game::state::play
{
isActive = true;
cursor.isVisible = true;
move_toward(cursor, rect_center(eatRect), cursorSpeed);
move_toward(cursor, cursorVelocity, rect_center(eatRect), cursorSpeed, cursorAcceleration);
itemInput.isMouseLeftDown = true;
itemManager.inputOverride = itemInput;
@@ -319,13 +356,13 @@ namespace game::state::play
auto rect = item.rect();
if (!is_finite(rect))
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
isActive = true;
cursor.isVisible = true;
move_toward(cursor, rect_center(rect), cursorSpeed);
move_toward(cursor, cursorVelocity, rect_center(rect), cursorSpeed, cursorAcceleration);
auto isInItemRect = math::is_point_in_rectf(rect, cursor.position);
@@ -341,7 +378,7 @@ namespace game::state::play
auto isWorldFood = is_world_food(character, itemManager);
if (character.calories <= CALORIE_EPSILON && !isWorldFood)
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
@@ -350,20 +387,20 @@ namespace game::state::play
auto* interactArea = digestion_area_get(character);
if (!interactArea)
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
auto rect = character.null_frame_rect(character.data.null_id_get(interactArea->null));
if (!is_finite(rect))
{
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
return;
}
auto radius = glm::max(1.0f, glm::min(rect.z, rect.w) * 0.25f);
digestAngle += 0.08f;
auto targetWorld = rect_center(rect) + glm::vec2(std::cos(digestAngle), std::sin(digestAngle)) * radius;
auto isAtTarget = move_toward(cursor, targetWorld, digestCursorSpeed);
auto isAtTarget = move_toward(cursor, cursorVelocity, targetWorld, digestCursorSpeed, digestCursorAcceleration);
isActive = true;
cursor.isVisible = true;
@@ -394,7 +431,7 @@ namespace game::state::play
return;
}
state_reset(cursor, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
state_reset(cursor, cursorVelocity, isItemMouseHeld, isDigestMouseHeld, targetItemIndex, digestClickCooldown);
}
void Autofeed::toggle() { isEnabled = !isEnabled; }
+1
View File
@@ -14,6 +14,7 @@ namespace game::state::play
int targetItemIndex{-1};
int digestClickCooldown{};
float digestAngle{};
glm::vec2 cursorVelocity{};
bool isEnabled{};
bool isActive{};
+6 -1
View File
@@ -226,6 +226,7 @@ namespace game::state::play
if (isItemHovered != isItemHoveredPrevious && !isItemHovered) isJustItemHoveredStopped = true;
if (spawnDelayTicks > 0) spawnDelayTicks--;
if (discoveredItemIDs.size() != schema.items.size()) discoveredItemIDs.resize(schema.items.size());
newItemFoundCount = 0;
for (auto& id : queuedItemIDs)
{
@@ -237,7 +238,11 @@ namespace game::state::play
auto& anm2 = itemSchema.anm2s.at(id);
items.emplace_back(anm2, position, id);
character.totalItemsSpawned++;
if (id >= 0 && id < (int)discoveredItemIDs.size()) discoveredItemIDs[id] = true;
if (id >= 0 && id < (int)discoveredItemIDs.size())
{
if (!discoveredItemIDs[id]) newItemFoundCount++;
discoveredItemIDs[id] = true;
}
particles_queue(itemSchema, resource::xml::Schema::Element::SUMMON, position);
}
queuedItemIDs.clear();
+1
View File
@@ -44,6 +44,7 @@ namespace game::state::play
std::vector<int> queuedItemIDs{};
std::vector<bool> discoveredItemIDs{};
int newItemFoundCount{};
int spawnDelayTicks{};
struct Particle
{
+24 -12
View File
@@ -54,15 +54,15 @@ namespace game::state::play::menu
{
auto* settings = resources.settings.root();
auto system = settings->measurementSystem == "Imperial" ? measurement::IMPERIAL : measurement::METRIC;
auto weight = character.weight_get(system);
auto weight = measurement::weight_display_get(character.weight, system);
auto stage = character.stage_get();
auto stageMax = character.stage_max_get();
auto stageCount = character.stage_count_get();
auto stageWeight = character.stage_threshold_get(stage, system);
auto stageNextWeight = character.stage_threshold_next_get(system);
auto unitString = (system == measurement::IMPERIAL ? "lbs" : "kg");
auto stageWeight = measurement::weight_display_get(character.stage_threshold_get(stage), system);
auto stageNextWeight = measurement::weight_display_get(character.stage_threshold_next_get(), system);
auto weightString = util::string::format_commas(weight, 2) + " " + unitString;
auto weightString = util::string::format_commas(weight.value, measurement::WEIGHT_DISPLAY_DECIMAL_DIGITS) + " " +
weight.unit;
imgui::text_unformatted_fit_draw(weightString.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, Font::HEADER_2),
Font::HEADER_2);
ImGui::SetItemTooltip("%s", weightString.c_str());
@@ -81,9 +81,9 @@ namespace game::state::play::menu
ImGui::TextUnformatted(strings.get(Strings::InfoMaxedOut).c_str());
else
{
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::Text(strings.get(Strings::InfoStageStartFormat).c_str(), stageWeight.value, stageWeight.unit);
ImGui::Text(strings.get(Strings::InfoStageCurrentFormat).c_str(), weight.value, weight.unit);
ImGui::Text(strings.get(Strings::InfoStageNextFormat).c_str(), stageNextWeight.value, stageNextWeight.unit);
}
ImGui::PopStyleColor();
ImGui::EndTooltip();
@@ -100,14 +100,26 @@ namespace game::state::play::menu
auto overstuffedPercent = std::max(ZERO_FLOAT, (calories - capacity) / (character.max_capacity() - capacity));
auto caloriesColor = ImVec4(1.0f, 1.0f - overstuffedPercent, 1.0f - overstuffedPercent, 1.0f);
auto capacityMax = character.max_capacity();
ImGui::PushStyleColor(ImGuiCol_Text, caloriesColor);
auto caloriesString = std::format("{:.0f} kcal / {:.0f} kcal", calories,
character.is_over_capacity() ? character.max_capacity() : character.capacity);
auto caloriesString = std::format("{:.0f} / {:.0f} kcal", calories, capacity);
auto caloriesTooltipStart = std::format("{:.0f} / {:.0f} ", calories, capacity);
auto caloriesTooltipEffective = std::format("({:.0f})", capacityMax);
imgui::text_unformatted_fit_draw(caloriesString.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, Font::HEADER_2), Font::HEADER_2);
ImGui::SetItemTooltip("%s", caloriesString.c_str());
ImGui::PopStyleColor();
if (ImGui::BeginItemTooltip())
{
ImGui::TextUnformatted(caloriesTooltipStart.c_str());
ImGui::SameLine(ZERO_FLOAT, ZERO_FLOAT);
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
ImGui::TextUnformatted(caloriesTooltipEffective.c_str());
ImGui::PopStyleColor();
ImGui::SameLine(ZERO_FLOAT, ZERO_FLOAT);
ImGui::TextUnformatted(" kcal");
ImGui::EndTooltip();
}
auto digestionProgress = character.isDigesting
? (float)character.digestionTimer / character.data.root()->digestionTimerMax
@@ -171,7 +183,7 @@ namespace game::state::play::menu
ImGui::PopFont();
};
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_1);
ImGui::PushFont(ImGui::GetFont(), resource::Font::HUGE);
ImGui::SeparatorText(character.data.root()->name.c_str());
ImGui::PopFont();
+6 -3
View File
@@ -5,6 +5,7 @@
#include "../../util/imgui/style.hpp"
#include "../../util/imgui/widget.hpp"
#include "../../util/measurement.hpp"
#include "../../util/string.hpp"
#include "../../util/vector.hpp"
using namespace game::util;
@@ -41,7 +42,7 @@ namespace game::state::select
auto* saveRoot = save.root();
auto isSaveValid = saveRoot && saveRoot->type == resource::xml::Schema::Element::SAVE;
auto* savedCharacter = isSaveValid ? save.child_get(*saveRoot, resource::xml::Schema::Element::CHARACTER) : nullptr;
auto weight = savedCharacter ? (double)savedCharacter->weightKilograms : (double)character->weight;
auto weight = savedCharacter ? (double)savedCharacter->weightKilograms : (double)character->weightKilograms;
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_3);
@@ -74,8 +75,10 @@ namespace game::state::select
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
ImGui::Text("Weight: %0.2f %s", system == IMPERIAL ? weight * KG_TO_LB : weight,
system == IMPERIAL ? "lbs" : "kg");
auto weightDisplay = weight_display_get(weight, system);
auto weightString =
string::format_commas(weightDisplay.value, WEIGHT_DISPLAY_DECIMAL_DIGITS) + " " + weightDisplay.unit;
ImGui::Text("Weight: %s", weightString.c_str());
auto stages = resources.characterPreviews[characterIndex].get_all(resource::xml::Schema::Element::STAGE);
ImGui::Text("Stages: %i", (int)stages.size() + 1);
+24 -1
View File
@@ -9,4 +9,27 @@ namespace game::util::measurement
};
constexpr auto KG_TO_LB = 2.20462;
}
constexpr auto KG_TO_G = 1000.0;
constexpr auto LB_TO_OZ = 16.0;
constexpr auto SMALL_WEIGHT_UNIT_THRESHOLD = 1.0;
constexpr auto WEIGHT_DISPLAY_DECIMAL_DIGITS = 2;
struct WeightDisplay
{
double value{};
const char* unit{};
};
inline WeightDisplay weight_display_get(double kilograms, System system)
{
if (system == IMPERIAL)
{
auto pounds = kilograms * KG_TO_LB;
if (pounds < SMALL_WEIGHT_UNIT_THRESHOLD) return {pounds * LB_TO_OZ, "oz"};
return {pounds, "lbs"};
}
if (kilograms < SMALL_WEIGHT_UNIT_THRESHOLD) return {kilograms * KG_TO_G, "g"};
return {kilograms, "kg"};
}
}