Split play into body, items, interactions, autofeed and camera, with events, commands and literal strings

This commit is contained in:
shweet committed 2026-11-12 22:14:04 -05:00
1 parent 167f1a5ddd
commit 3e9a0cbd34
21 files changed
+2504 -2431

No files matched your search

+81 -71
View File
@@ -6,6 +6,11 @@ constexpr float FONT_SIZE = 20;
constexpr const char* EFFECT_NAMES[] = {"animation", "big", "color", "delay", "oscillation", "scene",
"screenshake", "shake", "silent", "small", "sound"};
constexpr const char* USE_NAMES[] = {"", "edible", "tool"};
constexpr const char* HOOK_NAMES[] = {
#define X(id, name) name,
HOOKS(X)
#undef X
};
struct SoundSource
{
@@ -42,13 +47,23 @@ Entry* Dialogue::pool_get(const std::string& pool)
return entry_get(found->second[SDL_rand((Sint32)found->second.size())]);
}
Entry* Dialogue::hook_get(const std::string& hook)
Entry* Dialogue::hook_get(Hook hook)
{
auto found = hooks.find(hook);
if (found == hooks.end())
return nullptr;
Entry* entry = pool_get(found->second);
return entry ? entry : entry_get(found->second);
Entry* entry = pool_get(hooks[hook]);
return entry ? entry : entry_get(hooks[hook]);
}
bool Dialogue::is_hook(Hook hook)
{
return pools.contains(hooks[hook]) || entries.contains(hooks[hook]);
}
int Catalog::item_get(const std::string& name)
{
for (int i = 0; i < (int)items.size(); i++)
if (items[i].name == name)
return i;
return -1;
}
void Character::preview_load(const std::string& path, Renderer& renderer)
@@ -65,7 +80,7 @@ void Character::preview_load(const std::string& path, Renderer& renderer)
description = json_string(root, "description");
credits = json_string(root, "credits");
color = json_color(root, "color", color);
weight = json_float(root, "weight");
traits.weight = json_float(root, "weight");
anm2Path = json_string(root, "anm2");
portrait = root["portrait"].error() ? nullptr : renderer.texture_get(folder + json_string(root, "portrait"));
render = root["render"].error() ? nullptr : renderer.texture_get(folder + json_string(root, "render"));
@@ -120,35 +135,34 @@ static void interact_parse(object source, InteractArea& area, Character& charact
static void character_parse(object root, Character& character, Renderer& renderer, Audio& audio)
{
character.anm2 = character.anm2_get(character.anm2Path, renderer, audio);
character.weightMax = json_float(root, "weightMax", character.weight);
character.capacity = json_float(root, "capacity");
character.capacityMin = json_float(root, "capacityMin");
character.capacityMax = json_float(root, "capacityMax", character.capacity);
character.overstuffedMultiplier = json_float(root, "overstuffedMultiplier", 1);
character.overstuffedDigestBonus = json_float(root, "overstuffedDigestBonus");
character.caloriesToKilogram = json_float(root, "caloriesToKilogram");
character.digestionRate = json_float(root, "digestionRate") / TICK_RATE;
character.digestionRateMin = json_float(root, "digestionRateMin") / TICK_RATE;
character.digestionRateMax = json_float(root, "digestionRateMax", character.digestionRate * TICK_RATE) / TICK_RATE;
character.digestionTime = json_int(root, "digestionTime");
character.blinkChance = json_float(root, "blinkChance");
character.gurgleChance = json_float(root, "gurgleChance");
character.gurgleCapacityMultiplier = json_float(root, "gurgleCapacityMultiplier");
Traits& traits = character.traits;
traits.weightMax = json_float(root, "weightMax", traits.weight);
traits.capacity = json_float(root, "capacity");
traits.capacityMin = json_float(root, "capacityMin");
traits.capacityMax = json_float(root, "capacityMax", traits.capacity);
traits.overstuffedMultiplier = json_float(root, "overstuffedMultiplier", 1);
traits.overstuffedDigestBonus = json_float(root, "overstuffedDigestBonus");
traits.caloriesToKilogram = json_float(root, "caloriesToKilogram");
traits.digestionRate = json_float(root, "digestionRate") / TICK_RATE;
traits.digestionRateMin = json_float(root, "digestionRateMin") / TICK_RATE;
traits.digestionRateMax = json_float(root, "digestionRateMax", traits.digestionRate * TICK_RATE) / TICK_RATE;
traits.digestionTime = json_int(root, "digestionTime");
traits.blinkChance = json_float(root, "blinkChance");
traits.gurgleChance = json_float(root, "gurgleChance");
traits.gurgleCapacityMultiplier = json_float(root, "gurgleCapacityMultiplier");
character.dialoguePool = json_string(root, "dialoguePool");
object alternate = json_object(root, "alternate");
if (!alternate["texture"].error())
character.alternateTexture = renderer.texture_get(character.folder + json_string(alternate, "texture"));
character.alternate.texture = renderer.texture_get(character.folder + json_string(alternate, "texture"));
if (!alternate["sound"].error())
character.alternateSound = audio.sound_get(character.folder + json_string(alternate, "sound"));
character.alternateChance = json_float(alternate, "chance");
character.alternateSheet = json_int(alternate, "spritesheet");
character.alternate.sound = audio.sound_get(character.folder + json_string(alternate, "sound"));
character.alternate.chance = json_float(alternate, "chance");
character.alternate.sheet = json_int(alternate, "spritesheet");
object animations = json_object(root, "animations");
character.animationStart = json_string(animations, "start");
character.animationIdle = json_string(animations, "idle");
character.animationIdleFull = json_string(animations, "idleFull");
character.animationStageUp = json_string(animations, "stageUp");
character.animations = {json_string(animations, "idle"), json_string(animations, "idleFull"),
json_string(animations, "stageUp"), json_string(animations, "start")};
Anm2* anm2 = character.anm2;
auto layer_get = [&](object source, const char* key) {
@@ -157,10 +171,9 @@ static void character_parse(object root, Character& character, Renderer& rendere
auto null_get = [&](object source, const char* key) {
return anm2 ? anm2->null_get(json_string(source, key)) : -1;
};
character.talkSource = layer_get(json_object(root, "talk"), "source");
character.talkDestination = layer_get(json_object(root, "talk"), "destination");
character.blinkSource = layer_get(json_object(root, "blink"), "source");
character.blinkDestination = layer_get(json_object(root, "blink"), "destination");
character.talk = {layer_get(json_object(root, "talk"), "destination"), layer_get(json_object(root, "talk"), "source")};
character.blink = {layer_get(json_object(root, "blink"), "destination"),
layer_get(json_object(root, "blink"), "source")};
for (element entry : json_array(root, "eatAreas"))
{
@@ -225,26 +238,28 @@ static void character_parse(object root, Character& character, Renderer& rendere
}
object cursor = json_object(root, "cursor");
character.cursor = character.anm2_get(json_string(cursor, "anm2"), renderer, audio);
object cursorAnimations = json_object(cursor, "animations");
character.cursorIdle = json_string(cursorAnimations, "idle");
character.cursorClick = json_string(cursorAnimations, "click");
character.cursorHover = json_string(cursorAnimations, "hover");
character.cursorGrab = json_string(cursorAnimations, "grab");
character.cursorPan = json_string(cursorAnimations, "pan");
character.cursorZoom = json_string(cursorAnimations, "zoom");
character.cursorDispose = json_string(cursorAnimations, "dispose");
character.grabParticles = json_strings(json_object(cursor, "particles"), "grab");
auto cursor_get = [&](const char* key) {
return json_string(cursorAnimations, key);
};
character.cursor = {cursor_get("click"),
cursor_get("dispose"),
cursor_get("grab"),
cursor_get("hover"),
cursor_get("idle"),
cursor_get("pan"),
cursor_get("zoom"),
json_strings(json_object(cursor, "particles"), "grab"),
character.anm2_get(json_string(cursor, "anm2"), renderer, audio)};
object menu = json_object(root, "menu");
ImFontAtlas* fonts = ImGui::GetIO().Fonts;
if (!menu["font"].error())
character.font = fonts->AddFontFromFileTTF((character.folder + json_string(menu, "font")).c_str(), FONT_SIZE);
if (!menu["iconsFont"].error())
character.iconsFont =
fonts->AddFontFromFileTTF((character.folder + json_string(menu, "iconsFont")).c_str(), FONT_SIZE);
character.rounding = json_float(menu, "rounding");
character.homeGlyph = json_string(menu, "homeGlyph");
auto font_get = [&](const char* key) {
std::string path = json_string(menu, key);
return path.empty() ? nullptr
: ImGui::GetIO().Fonts->AddFontFromFileTTF((character.folder + path).c_str(), FONT_SIZE);
};
character.menu = {json_string(menu, "homeGlyph"), font_get("font"), font_get("iconsFont"),
json_float(menu, "rounding")};
for (auto [name, value] : json_object(root, "particles"))
{
@@ -253,22 +268,19 @@ static void character_parse(object root, Character& character, Renderer& rendere
character.particles[std::string(name)] = {
json_string(fields, "animation"), character.anm2_get(json_string(fields, "anm2"), renderer, audio)};
}
object strings = json_object(root, "strings");
for (int i = 0; i < STRING_COUNT; i++)
character.strings[i] = json_string(strings, STRING_NAMES[i], STRING_DEFAULTS[i]);
}
static void items_parse(object root, Character& character, Renderer& renderer, Audio& audio)
{
Catalog& catalog = character.catalog;
Anm2* baseAnm2 = character.anm2_get(json_string(root, "anm2"), renderer, audio);
character.itemSheet = json_int(root, "spritesheet");
character.spawnDelay = json_int(root, "spawnDelay", character.spawnDelay);
character.spawnDelayMultiplier = json_float(root, "spawnDelayMultiplier", character.spawnDelayMultiplier);
character.spawnLimit = json_int(root, "spawnLimit", character.spawnLimit);
catalog.sheet = json_int(root, "spritesheet");
catalog.spawnDelay = json_int(root, "spawnDelay", catalog.spawnDelay);
catalog.spawnDelayMultiplier = json_float(root, "spawnDelayMultiplier", catalog.spawnDelayMultiplier);
catalog.spawnLimit = json_int(root, "spawnLimit", catalog.spawnLimit);
object particles = json_object(root, "particles");
character.disposeParticles = json_strings(particles, "dispose");
character.summonParticles = json_strings(particles, "summon");
catalog.disposeParticles = json_strings(particles, "dispose");
catalog.summonParticles = json_strings(particles, "summon");
object categories = json_object(root, "categories");
std::vector<std::string> rarityNames;
@@ -278,7 +290,7 @@ static void items_parse(object root, Character& character, Renderer& renderer, A
if (value.get(fields))
continue;
std::string sound = json_string(fields, "sound");
character.rarities.push_back({std::string(name), sound.empty() ? nullptr : audio.sound_get(character.folder + sound),
catalog.rarities.push_back({std::string(name), sound.empty() ? nullptr : audio.sound_get(character.folder + sound),
json_float(fields, "weight")});
rarityNames.emplace_back(name);
}
@@ -288,7 +300,7 @@ static void items_parse(object root, Character& character, Renderer& renderer, A
object fields;
if (entry.get(fields))
continue;
Item& item = character.items.emplace_back();
Item& item = catalog.items.emplace_back();
item.name = json_string(fields, "name", "???");
item.description = json_string(fields, "description", "???");
item.category = json_string(fields, "category");
@@ -323,12 +335,9 @@ static void dialogue_parse(object root, Character& character, Audio& audio)
if (!value.get(fields))
dialogue.pauses[std::string(letter)] = {json_int(fields, "delay"), json_is(fields, "isSilent")};
}
for (auto [hook, value] : json_object(root, "hooks"))
{
std::string_view target;
if (!value.get(target))
dialogue.hooks[std::string(hook)] = target;
}
object hooks = json_object(root, "hooks");
for (int i = 0; i < HOOK_COUNT; i++)
dialogue.hooks[i] = json_string(hooks, HOOK_NAMES[i]);
for (auto [pool, value] : json_object(root, "pools"))
{
array list;
@@ -418,10 +427,11 @@ void Character::load(Renderer& renderer, Audio& audio)
// An actor for an item, showing its texture in place of its anm2's item spritesheet.
Actor Character::item_actor_get(int item)
{
Actor actor(items[item].anm2);
Item& entry = catalog.items[item];
Actor actor(entry.anm2);
actor.isStatic = true;
if (items[item].texture && itemSheet >= 0 && itemSheet < (int)actor.sheets.size())
actor.sheets[itemSheet] = items[item].texture;
if (entry.texture && catalog.sheet >= 0 && catalog.sheet < (int)actor.sheets.size())
actor.sheets[catalog.sheet] = entry.texture;
return actor;
}