Line trails, ring on impact, colored shadowed numbers, subtler pitch, no digest rumble

This commit is contained in:
shweet committed 2026-11-13 00:54:34 -05:00
1 parent 4fac37b7f5
commit ee3430d5cb
8 files changed
+33 -40

No files matched your search

+2 -2
View File
@@ -150,7 +150,7 @@ The areas which will expand based on how full the character is and their progres
| `null` | The null that will expand. | | `null` | The null that will expand. |
| `scaleMax` | The scale (in percent) that will be added at maximum. Know that a scale of 100 is the normal scale of an animation. | | `scaleMax` | The scale (in percent) that will be added at maximum. Know that a scale of 100 is the normal scale of an animation. |
The first expand area is treated as the belly: it breathes, jiggles when smacked, rubbed or fed, and is where digestion and stage-up effects come from. The first expand area is treated as the belly: it breathes, jiggles when smacked, rubbed or fed, and is where stage-up effects and gained weight come from.
### interactTypes, interactAreas ### interactTypes, interactAreas
`interactTypes` is an object of interaction types by name, giving each type's defaults. Rub, Smack, Kiss and Turn Around come from common. A type gets a button on the taskbar only if one of the character's areas uses it. `interactAreas` is a list of the concrete areas on a character which can be interacted with; usually for belly rubs, kisses, etc. Each area names its `type` and takes all of that type's settings, changing whichever it gives itself. `interactTypes` is an object of interaction types by name, giving each type's defaults. Rub, Smack, Kiss and Turn Around come from common. A type gets a button on the taskbar only if one of the character's areas uses it. `interactAreas` is a list of the concrete areas on a character which can be interacted with; usually for belly rubs, kisses, etc. Each area names its `type` and takes all of that type's settings, changing whichever it gives itself.
@@ -201,7 +201,7 @@ The UI's sounds.
| `sounds` | `open` and `close` (the taskbar or one of its windows), `hover` and `select` (a widget), and `cheats` (after entering a special code to activate cheats). | | `sounds` | `open` and `close` (the taskbar or one of its windows), `hover` and `select` (a widget), and `cheats` (after entering a special code to activate cheats). |
### particles ### particles
An object of particles by name, each with the `anm2` and `animation` it plays once where spawned. The `Puff` particle is also used for the dust when an item lands hard. An object of particles by name, each with the `anm2` and `animation` it plays once where spawned. The `Circle` particle is also spawned where an item lands hard.
# items.json # items.json
+1 -1
View File
@@ -2,7 +2,7 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <stb_vorbis.c> #include <stb_vorbis.c>
constexpr float PITCH_VARIATION = 0.06f; constexpr float PITCH_VARIATION = 0.025f;
void Audio::init() void Audio::init()
{ {
-1
View File
@@ -246,7 +246,6 @@ void Body::update(std::vector<Event>& events)
stats.isDigesting = true; stats.isDigesting = true;
stats.digestionTimer = traits.digestionTime; stats.digestionTimer = traits.digestionTime;
audio->play(character->sounds[SOUND_DIGEST]); audio->play(character->sounds[SOUND_DIGEST]);
events.push_back(EVENT_DIGEST_START);
} }
} }
values_clamp(); values_clamp();
+4 -1
View File
@@ -14,6 +14,7 @@ enum FloaterType
enum SparkShape enum SparkShape
{ {
SPARK_CIRCLE, SPARK_CIRCLE,
SPARK_LINE,
SPARK_STAR SPARK_STAR
}; };
@@ -26,10 +27,12 @@ struct Floater
FloaterType type = FLOATER_CALORIES; FloaterType type = FLOATER_CALORIES;
}; };
// A glint or puff that drifts, falls (or rises) by its gravity, and fades over its life. // A glint, puff or line (from its position to its end) that drifts, falls (or rises) by its gravity, and fades over its
// life.
struct Spark struct Spark
{ {
Color color; Color color;
Vec2 end;
Vec2 position; Vec2 position;
Vec2 velocity; Vec2 velocity;
float gravity = 0; float gravity = 0;
+10 -8
View File
@@ -10,9 +10,9 @@ constexpr float CRUMB_SPEED_Y_MIN = 2;
constexpr float CRUMB_SPIN = 15; constexpr float CRUMB_SPIN = 15;
constexpr Color GOLD = {1, 0.85f, 0.3f, 1}; constexpr Color GOLD = {1, 0.85f, 0.3f, 1};
constexpr Color TRAIL_COLOR = {1, 1, 1, 0.35f}; constexpr Color TRAIL_COLOR = {1, 1, 1, 0.35f};
constexpr const char* DUST_PARTICLE = "Puff"; constexpr const char* IMPACT_PARTICLE = "Circle";
constexpr float BITE_JIGGLE = 4; constexpr float BITE_JIGGLE = 4;
constexpr float DUST_IMPACT = 12; constexpr float IMPACT_SPEED = 12;
constexpr float FANFARE_MIN = 0.75f; constexpr float FANFARE_MIN = 0.75f;
constexpr float FANFARE_SHAKE = 0.015f; constexpr float FANFARE_SHAKE = 0.015f;
constexpr float FANFARE_SPEED = 5; constexpr float FANFARE_SPEED = 5;
@@ -23,9 +23,9 @@ constexpr float SQUASH_DECAY = 0.82f;
constexpr float SQUASH_MAX = 0.35f; constexpr float SQUASH_MAX = 0.35f;
constexpr float SQUASH_PER_SPEED = 0.025f; constexpr float SQUASH_PER_SPEED = 0.025f;
constexpr float TRAIL_SPEED = 12; constexpr float TRAIL_SPEED = 12;
constexpr Spark FANFARE_SPARK = {GOLD, {}, {}, 0.05f, 16, 50, 0, SPARK_STAR}; constexpr Spark FANFARE_SPARK = {GOLD, {}, {}, {}, 0.05f, 16, 50, 0, SPARK_STAR};
constexpr Spark RARE_SPARK = {GOLD, {}, {}, 0, 10, 40, 0, SPARK_STAR}; constexpr Spark RARE_SPARK = {GOLD, {}, {}, {}, 0, 10, 40, 0, SPARK_STAR};
constexpr Spark TRAIL_SPARK = {TRAIL_COLOR, {}, {}, 0, 7, 14, 0, SPARK_CIRCLE}; constexpr Spark TRAIL_SPARK = {TRAIL_COLOR, {}, {}, {}, 0, 6, 18, 0, SPARK_LINE};
constexpr int FANFARE_COUNT = 20; constexpr int FANFARE_COUNT = 20;
constexpr int FANFARE_SHAKE_TIME = 12; constexpr int FANFARE_SHAKE_TIME = 12;
constexpr int CRUMB_COUNT = 6; constexpr int CRUMB_COUNT = 6;
@@ -389,16 +389,18 @@ void Items::update(Play& play, Pointer pointer)
} }
else else
{ {
Vec2 previous = position;
float impact = motion_update(play, entity); float impact = motion_update(play, entity);
if (impact > 0) if (impact > 0)
audio.play(character.sounds[SOUND_BOUNCE]); audio.play(character.sounds[SOUND_BOUNCE]);
if (impact >= DUST_IMPACT) if (impact >= IMPACT_SPEED)
play.particles_spawn({DUST_PARTICLE}, position); play.particles_spawn({IMPACT_PARTICLE}, position);
entity.isThrown &= vec2_length(entity.velocity) >= TRAIL_SPEED; entity.isThrown &= vec2_length(entity.velocity) >= TRAIL_SPEED;
if (entity.isThrown) if (entity.isThrown)
{ {
Spark spark = TRAIL_SPARK; Spark spark = TRAIL_SPARK;
spark.position = position; spark.position = previous;
spark.end = position;
play.effects.spark_add(spark); play.effects.spark_add(spark);
} }
} }
+2 -14
View File
@@ -11,12 +11,8 @@ constexpr ImGuiKey CHEAT_KEYS[] = {ImGuiKey_UpArrow, ImGuiKey_DownArrow, ImGu
ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A}; ImGuiKey_RightArrow, ImGuiKey_B, ImGuiKey_A};
constexpr const char* KEY_DISCOVERED = "discovered"; constexpr const char* KEY_DISCOVERED = "discovered";
constexpr const char* KEY_ITEM = "item"; constexpr const char* KEY_ITEM = "item";
constexpr Spark DIGEST_SPARK = {{1, 1, 1, 0.5f}, {}, {}, -0.06f, 10, 50, 0, SPARK_CIRCLE}; constexpr Spark STAGE_SPARK = {{1, 0.85f, 0.3f, 1}, {}, {}, {}, 0.06f, 18, 70, 0, SPARK_STAR};
constexpr Spark STAGE_SPARK = {{1, 0.85f, 0.3f, 1}, {}, {}, 0.06f, 18, 70, 0, SPARK_STAR};
constexpr float AUTOSAVE_TIME = 30; constexpr float AUTOSAVE_TIME = 30;
constexpr float DIGEST_JIGGLE = 3;
constexpr float DIGEST_SHAKE = 0.006f;
constexpr float DIGEST_SPEED = 2;
constexpr float PUNCH_DECAY = 0.93f; constexpr float PUNCH_DECAY = 0.93f;
constexpr float PUNCH_EPSILON = 0.001f; constexpr float PUNCH_EPSILON = 0.001f;
constexpr float STAGE_FLASH = 0.7f; constexpr float STAGE_FLASH = 0.7f;
@@ -24,8 +20,6 @@ constexpr float STAGE_JIGGLE = 15;
constexpr float STAGE_PUNCH = 0.08f; constexpr float STAGE_PUNCH = 0.08f;
constexpr float STAGE_SHAKE = 0.04f; constexpr float STAGE_SHAKE = 0.04f;
constexpr float STAGE_SPEED = 8; constexpr float STAGE_SPEED = 8;
constexpr int DIGEST_COUNT = 10;
constexpr int DIGEST_SHAKE_TIME = 40;
constexpr int STAGE_COUNT = 28; constexpr int STAGE_COUNT = 28;
constexpr int STAGE_FREEZE = 5; constexpr int STAGE_FREEZE = 5;
constexpr int STAGE_SHAKE_TIME = 30; constexpr int STAGE_SHAKE_TIME = 30;
@@ -193,16 +187,10 @@ void Play::events_handle(Vec2 screenSize)
Dialogue& dialogue = character->dialogue; Dialogue& dialogue = character->dialogue;
for (Event event : events) for (Event event : events)
{ {
Spark spark = event == EVENT_STAGE_START ? STAGE_SPARK : DIGEST_SPARK; Spark spark = STAGE_SPARK;
spark.position = body.belly_get(); spark.position = body.belly_get();
if (event == EVENT_GAINED && body.gained > 0) if (event == EVENT_GAINED && body.gained > 0)
effects.floater_add(spark.position, body.gained, FLOATER_WEIGHT); effects.floater_add(spark.position, body.gained, FLOATER_WEIGHT);
if (event == EVENT_DIGEST_START)
{
effects.burst_add(spark, DIGEST_COUNT, DIGEST_SPEED);
camera.shake_add(DIGEST_SHAKE, DIGEST_SHAKE_TIME);
body.jiggle_add(DIGEST_JIGGLE);
}
if (event == EVENT_STAGE_START) if (event == EVENT_STAGE_START)
{ {
effects.burst_add(spark, STAGE_COUNT, STAGE_SPEED); effects.burst_add(spark, STAGE_COUNT, STAGE_SPEED);
-1
View File
@@ -32,7 +32,6 @@ enum Hold
// What happened to the body in a tick, answered once it's done. // What happened to the body in a tick, answered once it's done.
enum Event enum Event
{ {
EVENT_DIGEST_START,
EVENT_DIGESTED, EVENT_DIGESTED,
EVENT_GAINED, EVENT_GAINED,
EVENT_STAGE_START, EVENT_STAGE_START,
+14 -12
View File
@@ -25,11 +25,11 @@ constexpr float INDICATOR_SIZE = 0.35f;
constexpr float INDICATOR_SPEED = 2; constexpr float INDICATOR_SPEED = 2;
constexpr float ITEM_SIZE = 96; constexpr float ITEM_SIZE = 96;
constexpr float ROUNDING = 10; constexpr float ROUNDING = 10;
constexpr ImVec4 CALORIES_COLOR = {1, 0.85f, 0.3f, 1};
constexpr float BAR_EASE = 6; constexpr float BAR_EASE = 6;
constexpr float BAR_SHAKE = 2; constexpr float BAR_SHAKE = 2;
constexpr float BAR_SHAKE_SPEED = 40; constexpr float BAR_SHAKE_SPEED = 40;
constexpr float FLOATER_OUTLINE = 2; constexpr float FLOATER_SHADOW = 3;
constexpr float FLOATER_SHADOW_ALPHA = 0.6f;
constexpr float OVERSTUFFED_PULSE = 0.35f; constexpr float OVERSTUFFED_PULSE = 0.35f;
constexpr float OVERSTUFFED_SPEED = 6; constexpr float OVERSTUFFED_SPEED = 6;
constexpr float STAR_WAIST = 0.3f; constexpr float STAR_WAIST = 0.3f;
@@ -724,7 +724,8 @@ void Ui::text_draw(Play& play, ImVec2 screen)
ImGui::PopFont(); ImGui::PopFont();
} }
// The flourishes over the world: sparks, rising numbers (outlined so they read on anything), and the screen's flash. // The flourishes over the world: sparks, rising numbers (in the character's color, shadowed so they read on anything),
// and the screen's flash.
void Ui::effects_draw(Play& play, ImVec2 screen) void Ui::effects_draw(Play& play, ImVec2 screen)
{ {
Effects& effects = play.effects; Effects& effects = play.effects;
@@ -741,6 +742,12 @@ void Ui::effects_draw(Play& play, ImVec2 screen)
drawList->AddCircleFilled({at.x, at.y}, size, color); drawList->AddCircleFilled({at.x, at.y}, size, color);
continue; continue;
} }
if (spark.shape == SPARK_LINE)
{
Vec2 end = view * spark.end;
drawList->AddLine({at.x, at.y}, {end.x, end.y}, color, size);
continue;
}
float waist = size * STAR_WAIST; float waist = size * STAR_WAIST;
drawList->AddQuadFilled({at.x, at.y - size}, {at.x + waist, at.y}, {at.x, at.y + size}, {at.x - waist, at.y}, drawList->AddQuadFilled({at.x, at.y - size}, {at.x + waist, at.y}, {at.x, at.y + size}, {at.x - waist, at.y},
color); color);
@@ -754,18 +761,13 @@ void Ui::effects_draw(Play& play, ImVec2 screen)
std::string text = floater.type == FLOATER_CALORIES ? text_format("+%.0f kcal", floater.value) std::string text = floater.type == FLOATER_CALORIES ? text_format("+%.0f kcal", floater.value)
: "+" + weight_format(floater.value, *measurement); : "+" + weight_format(floater.value, *measurement);
float alpha = std::min(1.0f, (float)floater.life / (FLOATER_LIFE / 2)); float alpha = std::min(1.0f, (float)floater.life / (FLOATER_LIFE / 2));
ImVec4 color = floater.type == FLOATER_CALORIES ? CALORIES_COLOR : ImGui::GetStyleColorVec4(ImGuiCol_PlotHistogram); Color tint = play.character->color;
color.w = alpha; ImVec4 color = {tint.r, tint.g, tint.b, alpha};
ImVec2 size = font->CalcTextSizeA(FONT_HEADER_2, FLT_MAX, 0, text.c_str()); ImVec2 size = font->CalcTextSizeA(FONT_HEADER_2, FLT_MAX, 0, text.c_str());
Vec2 at = view * floater.position; Vec2 at = view * floater.position;
ImVec2 position = {at.x - size.x / 2, at.y - size.y / 2}; ImVec2 position = {at.x - size.x / 2, at.y - size.y / 2};
ImU32 outline = ImGui::GetColorU32(ImVec4(0, 0, 0, alpha)); drawList->AddText(font, FONT_HEADER_2, {position.x + FLOATER_SHADOW, position.y + FLOATER_SHADOW},
for (int x = -1; x <= 1; x++) ImGui::GetColorU32(ImVec4(0, 0, 0, alpha * FLOATER_SHADOW_ALPHA)), text.c_str());
for (int y = -1; y <= 1; y++)
if (x != 0 || y != 0)
drawList->AddText(font, FONT_HEADER_2,
{position.x + x * FLOATER_OUTLINE, position.y + y * FLOATER_OUTLINE}, outline,
text.c_str());
drawList->AddText(font, FONT_HEADER_2, position, ImGui::GetColorU32(color), text.c_str()); drawList->AddText(font, FONT_HEADER_2, position, ImGui::GetColorU32(color), text.c_str());
} }
if (effects.flash > 0) if (effects.flash > 0)