Whatever The Fuck Man

This commit is contained in:
2026-07-04 00:15:26 -04:00
parent ce77175162
commit 2efed2a443
162 changed files with 13119 additions and 7417 deletions
+175 -126
View File
@@ -21,12 +21,12 @@ namespace game::state
{
namespace
{
int durability_animation_index_get(const resource::xml::Item& schema, const resource::xml::Anm2& anm2, int durability,
int durabilityMax)
int durability_animation_index_get(const resource::xml::Schema& 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));
auto animationName = schema.root()->animationChew + std::to_string(std::max(0, durability));
return anm2.animationMap.contains(animationName) ? anm2.animationMap.at(animationName) : -1;
}
}
@@ -35,19 +35,29 @@ namespace game::state
{
if (!isWindows) return World::CENTER;
return menu.isOpen && tools.isOpen ? World::MENU_TOOLS
: menu.isOpen ? World::MENU
: tools.isOpen ? World::TOOLS
: World::CENTER;
auto isToolsOpen = tools.isOpen && !menu.is_fullscreen_visible_get();
return menu.isOpen && isToolsOpen ? World::MENU_TOOLS
: menu.isOpen ? World::MENU
: isToolsOpen ? World::TOOLS
: World::CENTER;
}
bool Play::cursor_enabled_get() const
{
auto* root = character.data.cursorSchema.root();
return root && root->type == Schema::Element::CURSOR;
}
void Play::start_sequence_begin()
{
auto& dialogue = character.data.dialogue;
if (!dialogue.start.is_valid()) return;
auto* start = dialogue.get(Schema::Element::START);
if (!start) return;
character.queue_play({.animation = dialogue.start.animation, .isInterruptible = false});
character.tick();
character.play({.animation = start->animation,
.appendID = character.animation_append_id_get(),
.interrupt = Entity::Interrupt::NEVER});
character.update();
text.entry = nullptr;
text.isEnabled = false;
isWindows = false;
@@ -59,7 +69,7 @@ namespace game::state
void Play::end_sequence_begin()
{
auto& dialogue = character.data.dialogue;
if (!dialogue.end.is_valid()) return;
if (!dialogue.get(Schema::Element::END)) return;
text.entry = nullptr;
text.isEnabled = false;
@@ -71,68 +81,92 @@ namespace game::state
void Play::set(Resources& resources, int selectedCharacterIndex, enum Game game)
{
auto& data = resources.character_get(selectedCharacterIndex);
auto& saveData = data.save;
auto* saveRoot = data.save.root();
auto isSaveValid = saveRoot && saveRoot->type == Schema::Element::SAVE;
auto* saveCharacter = isSaveValid ? data.save.child_get(*saveRoot, Schema::Element::CHARACTER) : nullptr;
auto* saveInventory = isSaveValid ? data.save.child_get(*saveRoot, Schema::Element::INVENTORY) : nullptr;
auto* saveItems = isSaveValid ? data.save.child_get(*saveRoot, Schema::Element::ITEMS) : nullptr;
auto* savePan = isSaveValid ? data.save.child_get(*saveRoot, Schema::Element::PAN) : nullptr;
auto* saveZoom = isSaveValid ? data.save.child_get(*saveRoot, Schema::Element::ZOOM) : nullptr;
auto& itemSchema = data.itemSchema;
auto& dialogue = data.dialogue;
auto& menuSchema = data.menuSchema;
auto* characterRoot = data.root();
this->characterIndex = selectedCharacterIndex;
resources.last_character_save(selectedCharacterIndex);
cheatCodeIndex = 0;
cheatCodeStartTime = 0.0;
character =
entity::Character(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y));
character = Entity(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y));
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);
glm::clamp(character.digestionRate, characterRoot->digestionRateMin, characterRoot->digestionRateMax);
character.eatSpeed =
glm::clamp(character.eatSpeed, characterRoot->eatSpeedMinMultiplier, characterRoot->eatSpeedMaxMultiplier);
character.capacity =
glm::clamp(character.capacity, characterRoot->capacityMinCalories, characterRoot->capacityMaxCalories);
auto* alternateSpritesheet = data.alternate_spritesheet();
auto isAlternateSpritesheet =
(game == NEW_GAME && math::random_percent_roll(data.alternateSpritesheet.chanceOnNewGame));
alternateSpritesheet && game == NEW_GAME && math::random_percent_roll(alternateSpritesheet->chanceOnNewGame);
if (isAlternateSpritesheet || saveData.isAlternateSpritesheet)
if (isAlternateSpritesheet || (isSaveValid && saveRoot->isAlternateSpritesheet))
{
character.spritesheet_set(entity::Character::ALTERNATE);
if (game == NEW_GAME) character.data.alternateSpritesheet.sound.play();
character.spritesheet_set(Entity::ALTERNATE);
if (game == NEW_GAME && alternateSpritesheet) alternateSpritesheet->soundEntry.sound.play();
}
character.totalCaloriesConsumed = saveData.totalCaloriesConsumed;
character.totalFoodItemsEaten = saveData.totalFoodItemsEaten;
character.totalCaloriesConsumed = saveCharacter ? saveCharacter->totalCaloriesConsumed : 0;
character.totalFoodItemsEaten = saveCharacter ? saveCharacter->totalFoodItemsEaten : 0;
autofeed = play::Autofeed{};
characterManager = CharacterManager{};
particleManager = ParticleManager{};
areaManager = AreaManager{};
cursor = entity::Cursor(character.data.cursorSchema.anm2);
cursor.interactTypeID = character.data.interactTypeNames.empty() ? -1 : 0;
cursor = Entity{};
cursor.entityType = CURSOR;
cursor.interactTypeID = character.data.interact_type_names_get().empty() ? -1 : 0;
autofeedCursor = Entity{};
autofeedCursor.entityType = AUTOCURSOR;
autofeedCursor.interactTypeID = cursor.interactTypeID;
autofeedCursor.position = cursor.position;
autofeedCursor.isVisible = false;
if (cursor_enabled_get())
{
cursor = Entity(character.data.cursorSchema.baseAnm2);
cursor.entityType = CURSOR;
cursor.interactTypeID = character.data.interact_type_names_get().empty() ? -1 : 0;
autofeedCursor = Entity(character.data.cursorSchema.baseAnm2);
autofeedCursor.entityType = AUTOCURSOR;
autofeedCursor.interactTypeID = cursor.interactTypeID;
autofeedCursor.position = cursor.position;
autofeedCursor.isVisible = false;
}
menu.inventory = play::menu::Inventory{};
for (auto& [id, quantity] : saveData.inventory)
for (auto* item : saveInventory ? data.save.children_get(*saveInventory, Schema::Element::ITEM)
: std::vector<Schema::Element*>{})
{
if (quantity == 0) continue;
menu.inventory.values[id] = quantity;
if (item->quantity == 0) continue;
menu.inventory.values[item->id] = item->quantity;
}
itemManager = ItemManager{};
for (auto& item : saveData.items)
for (auto* item :
saveItems ? data.save.children_get(*saveItems, Schema::Element::ITEM) : std::vector<Schema::Element*>{})
{
auto& anm2 = itemSchema.anm2s.at(item.id);
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.durability, animationIndex, item.velocity,
item.rotation);
auto& anm2 = itemSchema.anm2s.at(item->id);
auto& schemaItem = itemSchema.items.at(item->id);
auto durabilityMax = schemaItem.isDurability ? schemaItem.durability : 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->durability, animationIndex,
item->velocity, item->rotation);
}
imgui::style::widget_set(menuSchema.rounding);
imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select);
imgui::widget::sounds_set(&menuSchema.root()->soundHover, &menuSchema.root()->soundSelect);
play::style::color_set(resources, character);
menu.arcade = play::menu::Arcade(character);
menu.arcade.skillCheck.totalPlays = saveData.skillCheck.totalPlays;
menu.arcade.skillCheck.highScore = saveData.skillCheck.highScore;
menu.arcade.skillCheck.bestCombo = saveData.skillCheck.bestCombo;
menu.arcade.skillCheck.gradeCounts = saveData.skillCheck.gradeCounts;
menu.arcade.skillCheck.isHighScoreAchieved = saveData.skillCheck.highScore > 0 ? true : false;
menu.arcade.orbit.highScore = saveData.orbit.highScore;
text.entry = nullptr;
text.isEnabled = false;
@@ -142,7 +176,7 @@ namespace game::state
menu.isCheats = false;
#endif
isPostgame = saveData.isPostgame;
isPostgame = isSaveValid && saveRoot->isPostgame;
if (character.stage_get() >= character.stage_max_get()) isPostgame = true;
if (isPostgame) menu.isCheats = true;
@@ -150,12 +184,16 @@ namespace game::state
if (auto font = character.data.menuSchema.font.get()) ImGui::GetIO().FontDefault = font;
character.queue_idle_animation();
character.tick();
character.play({.animation = character.idle_animation_get(), .appendID = character.animation_append_id_get()});
character.update();
areaManager.set(character);
worldCanvas.size_set(imgui::to_vec2(ImGui::GetMainViewport()->Size));
world.set(character, worldCanvas, focus_get());
if (savePan) worldCanvas.pan = savePan->position;
if (saveZoom && saveZoom->zoom >= World::ZOOM_MIN)
worldCanvas.zoom = glm::clamp(World::ZOOM_MIN, saveZoom->zoom, World::ZOOM_MAX);
if (game == NEW_GAME && dialogue.start.is_valid()) start_sequence_begin();
if (game == NEW_GAME && dialogue.get(Schema::Element::START)) start_sequence_begin();
if (isPostgame)
{
@@ -173,25 +211,13 @@ namespace game::state
void Play::exit(Resources& resources)
{
imgui::style::color_set(resources.settings.color);
imgui::style::color_set(resources.settings.root()->color);
imgui::style::widget_set();
imgui::widget::sounds_set(nullptr, nullptr);
ImGui::GetIO().FontDefault = resources.font.get();
save(resources);
}
void Play::tick(Resources&)
{
character.tick();
cursor.tick();
menu.tick();
toasts.tick();
text.tick(character);
for (auto& item : itemManager.items)
item.tick();
}
void Play::update(Resources& resources)
{
static constexpr std::array<ImGuiKey, 10> CHEAT_CODE = {
@@ -200,10 +226,16 @@ namespace game::state
static constexpr std::array<ImGuiKey, 6> CHEAT_INPUT_KEYS = {
ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A};
static constexpr auto CHEAT_CODE_INPUT_TIME_SECONDS = 5.0;
static constexpr auto UI_ALPHA_MAX = 1.0f;
auto focus = focus_get();
auto& dialogue = character.data.dialogue;
cursor.isVisible = true;
auto isCursorEnabled = cursor_enabled_get();
if (isCursorEnabled)
{
cursor.isVisible = true;
autofeedCursor.isVisible = false;
}
if (!menu.isCheats)
{
@@ -233,7 +265,7 @@ namespace game::state
cheatCodeIndex = 0;
cheatCodeStartTime = 0.0;
toasts.push(character.data.strings.get(Strings::ToastCheatsUnlocked));
character.data.menuSchema.sounds.cheatsActivated.play();
character.data.menuSchema.root()->soundCheatsActivated.play();
}
}
@@ -246,32 +278,19 @@ namespace game::state
if (isWindows)
{
menu.update(resources, itemManager, character, cursor, text, worldCanvas);
tools.update(character, cursor, world, focus, worldCanvas);
info.update(resources, character);
auto isToolsDisabled = menu.is_fullscreen_visible_get();
auto uiAlpha = UI_ALPHA_MAX - menu.fullscreen_alpha_get();
tools.update(character, cursor, world, focus, worldCanvas, isToolsDisabled, uiAlpha);
info.update(resources, character, uiAlpha);
menu.update(resources, itemManager, character, text, autofeed);
toasts.update();
#if DEBUG
if (menu.isDebugOpen && ImGui::IsKeyPressed(ImGuiKey_F8, false)) end_sequence_begin();
#endif
if (menu.debug.isStartSequenceRequested)
{
menu.debug.isStartSequenceRequested = false;
start_sequence_begin();
}
if (menu.debug.isEndSequenceRequested)
{
menu.debug.isEndSequenceRequested = false;
end_sequence_begin();
}
}
auto isEndSequenceActive = isEndBegin && !isEndEnd;
itemManager.isDisabled = isEndSequenceActive;
characterManager.isDisabled = isEndSequenceActive;
if (text.isEnabled) text.update(character);
if (text.isEnabled) text.update(character, worldCanvas);
if (isStart)
{
@@ -279,16 +298,17 @@ namespace game::state
{
if (auto animation = character.animation_get())
{
if (animation->isLoop || character.state == entity::Actor::STOPPED)
if (animation->isLoop || character.state == Entity::STOPPED)
{
text.set(dialogue.get(dialogue.start.id), character);
auto* start = dialogue.get(Schema::Element::START);
text.set(start ? dialogue.dialogue_entry_get(*start) : nullptr, character);
isStartBegin = true;
}
}
}
else if (!isStartEnd)
{
if (text.entry->is_last())
if (text.entry && text.is_terminal() && text.is_finished())
{
isWindows = true;
isStartEnd = true;
@@ -306,7 +326,8 @@ namespace game::state
{
if (character.is_animation_finished())
{
text.set(dialogue.get(dialogue.end.id), character);
auto* end = dialogue.get(Schema::Element::END);
text.set(end ? dialogue.dialogue_entry_get(*end) : nullptr, character);
isEndBegin = true;
isWindows = false;
tools.isOpen = false;
@@ -319,7 +340,7 @@ namespace game::state
}
else if (!isEndEnd)
{
if (text.entry->is_last())
if (text.entry && text.is_terminal() && text.is_finished())
{
menu.isOpen = true;
isWindows = true;
@@ -332,13 +353,29 @@ namespace game::state
}
}
itemManager.update(character, cursor, areaManager, text, World::BOUNDS, worldCanvas);
characterManager.update(character, cursor, text, worldCanvas);
autofeed.update(resources, character, autofeedCursor, menu.inventory, itemManager, characterManager, worldCanvas);
auto& interactionCursor = autofeed.active_get() ? autofeedCursor : cursor;
itemManager.isAutomation = autofeed.active_get();
itemManager.update(character, interactionCursor, areaManager, text, World::BOUNDS, worldCanvas);
characterManager.update(character, interactionCursor, text, worldCanvas);
for (auto& particle : itemManager.particles)
particleManager.spawn(character.data, particle.label, particle.position);
for (auto& particle : characterManager.particles)
particleManager.spawn(character.data, particle.label, particle.position);
if (isCursorEnabled && !autofeed.active_get() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
!itemManager.isItemHovered && !characterManager.isHovering && cursor.cursorState == Entity::DEFAULT)
if (auto* cursorRoot = character.data.cursorSchema.root())
if (auto animation = cursorRoot->animationClick.get())
cursor.play({.animation = *animation, .mode = Entity::PLAY_FORCE, .interrupt = Entity::Interrupt::ALWAYS});
character.update();
areaManager.update(character);
particleManager.update();
cursor.update();
if (autofeed.active_get()) autofeedCursor.update();
world.update(character, cursor, worldCanvas, focus);
worldCanvas.tick();
worldCanvas.update();
if (autosaveTime += ImGui::GetIO().DeltaTime; autosaveTime > AUTOSAVE_TIME || menu.settingsMenu.isSave)
{
@@ -354,70 +391,82 @@ namespace game::state
auto& rectShader = resources.shaders[shader::RECT];
auto size = imgui::to_ivec2(ImGui::GetMainViewport()->Size);
auto& bgTexture = character.data.areaSchema.areas.at(areaManager.get(character)).texture;
auto windowModel = math::quad_model_get(vec2(size));
auto worldModel = math::quad_model_get(bgTexture.size);
worldCanvas.bind();
worldCanvas.size_set(size);
worldCanvas.clear();
worldCanvas.texture_render(textureShader, bgTexture.id, worldModel);
areaManager.render(character, textureShader, rectShader, worldCanvas);
character.render(textureShader, rectShader, worldCanvas);
for (auto& item : itemManager.items)
item.render(textureShader, rectShader, worldCanvas);
if (menu.debug.isBoundsDisplay)
{
auto boundsModel =
math::quad_model_get(glm::vec2(World::BOUNDS.z, World::BOUNDS.w), glm::vec2(World::BOUNDS.x, World::BOUNDS.y),
glm::vec2(World::BOUNDS.x, World::BOUNDS.y) * 0.5f);
worldCanvas.rect_render(rectShader, boundsModel);
}
auto isCursorEnabled = cursor_enabled_get();
if (isCursorEnabled && autofeed.active_get()) autofeedCursor.render(textureShader, rectShader, worldCanvas);
particleManager.render(textureShader, rectShader, worldCanvas);
worldCanvas.unbind();
auto isCursor = isCursorEnabled && !resources.isDeveloper;
canvas.bind();
canvas.texture_render(textureShader, worldCanvas, windowModel);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
cursor.render(textureShader, rectShader, canvas);
if (isCursor) cursor.render(textureShader, rectShader, canvas);
canvas.unbind();
}
void Play::save(Resources& resources)
{
resource::xml::Save save;
resource::xml::Schema save;
auto& root = save.element_add(resource::xml::Schema::Element::SAVE);
root.isPostgame = isPostgame;
root.isAlternateSpritesheet = character.spritesheetType == Entity::ALTERNATE;
save.weight = character.weight;
save.calories = character.calories;
save.capacity = character.capacity;
save.digestionRate = character.digestionRate;
save.eatSpeed = character.eatSpeed;
save.digestionProgress = character.digestionProgress;
save.isDigesting = character.isDigesting;
save.digestionTimer = character.digestionTimer;
save.totalCaloriesConsumed = character.totalCaloriesConsumed;
save.totalFoodItemsEaten = character.totalFoodItemsEaten;
save.skillCheck.totalPlays = menu.arcade.skillCheck.totalPlays;
save.skillCheck.highScore = menu.arcade.skillCheck.highScore;
save.skillCheck.bestCombo = menu.arcade.skillCheck.bestCombo;
save.skillCheck.gradeCounts = menu.arcade.skillCheck.gradeCounts;
save.orbit.highScore = menu.arcade.orbit.highScore;
save.isPostgame = isPostgame;
save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE;
auto& panElement = save.element_add(resource::xml::Schema::Element::PAN, 0);
panElement.position = worldCanvas.pan;
auto& zoomElement = save.element_add(resource::xml::Schema::Element::ZOOM, 0);
zoomElement.zoom = worldCanvas.zoom;
auto& characterElement = save.element_add(resource::xml::Schema::Element::CHARACTER, 0);
characterElement.weightKilograms = character.weight;
characterElement.calories = character.calories;
characterElement.capacityCalories = character.capacity;
characterElement.digestionRate = character.digestionRate;
characterElement.eatSpeedMultiplier = character.eatSpeed;
characterElement.digestionProgress = character.digestionProgress;
characterElement.isDigesting = character.isDigesting;
characterElement.digestionTimer = character.digestionTimer;
characterElement.totalCaloriesConsumed = character.totalCaloriesConsumed;
characterElement.totalFoodItemsEaten = character.totalFoodItemsEaten;
auto inventoryIndex = (int)save.elements.size();
save.element_add(resource::xml::Schema::Element::INVENTORY, 0);
for (auto& [id, quantity] : menu.inventory.values)
{
if (quantity == 0) continue;
save.inventory[id] = quantity;
auto& item = save.element_add(resource::xml::Schema::Element::ITEM, inventoryIndex);
item.id = id;
item.quantity = quantity;
}
auto itemsIndex = (int)save.elements.size();
save.element_add(resource::xml::Schema::Element::ITEMS, 0);
for (auto& item : itemManager.items)
save.items.emplace_back(item.schemaID, item.durability, item.position, item.velocity,
*item.overrides[item.rotationOverrideID].frame.rotation);
save.isValid = true;
{
auto& itemElement = save.element_add(resource::xml::Schema::Element::ITEM, itemsIndex);
itemElement.id = item.schemaID;
itemElement.durability = item.durability;
itemElement.position = item.position;
itemElement.velocity = item.velocity;
itemElement.rotation = *item.overrides[item.rotationOverrideID].frame.rotation;
}
resources.character_save_set(characterIndex, save);
save.serialize(character.data.save_path_get());