Juice: pitch variation, squash, dust, trails, jiggle, breathing, hit-stop, numbers, stage-up, digest and rare flair
This commit is contained in:
1 parent
7e27ffad94
commit
fdfea89373
11 files changed
+363
-21
No files matched your search
+3
-1
@@ -150,6 +150,8 @@ 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.
|
||||||
|
|
||||||
### 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.
|
||||||
|
|
||||||
@@ -199,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.
|
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.
|
||||||
|
|
||||||
# items.json
|
# items.json
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stb_vorbis.c>
|
#include <stb_vorbis.c>
|
||||||
|
|
||||||
|
constexpr float PITCH_VARIATION = 0.06f;
|
||||||
|
|
||||||
void Audio::init()
|
void Audio::init()
|
||||||
{
|
{
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO))
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO))
|
||||||
@@ -88,6 +90,7 @@ void Audio::play(Sound* sound)
|
|||||||
SDL_AudioSpec spec = {SDL_AUDIO_S16, sound->channels, sound->rate};
|
SDL_AudioSpec spec = {SDL_AUDIO_S16, sound->channels, sound->rate};
|
||||||
SDL_ClearAudioStream(voices[voice]);
|
SDL_ClearAudioStream(voices[voice]);
|
||||||
SDL_SetAudioStreamFormat(voices[voice], &spec, nullptr);
|
SDL_SetAudioStreamFormat(voices[voice], &spec, nullptr);
|
||||||
|
SDL_SetAudioStreamFrequencyRatio(voices[voice], random_get(1 - PITCH_VARIATION, 1 + PITCH_VARIATION));
|
||||||
SDL_PutAudioStreamData(voices[voice], sound->samples, sound->frames * sound->channels * (int)sizeof(short));
|
SDL_PutAudioStreamData(voices[voice], sound->samples, sound->frames * sound->channels * (int)sizeof(short));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-4
@@ -1,5 +1,9 @@
|
|||||||
#include "play.h"
|
#include "play.h"
|
||||||
|
|
||||||
|
constexpr float BREATH_AMPLITUDE = 1.5f;
|
||||||
|
constexpr float BREATH_SPEED = 2.2f;
|
||||||
|
constexpr float JIGGLE_DAMPING = 0.12f;
|
||||||
|
constexpr float JIGGLE_STIFFNESS = 0.18f;
|
||||||
constexpr float SWAP_STEP = 0.5f;
|
constexpr float SWAP_STEP = 0.5f;
|
||||||
|
|
||||||
enum SwapMode
|
enum SwapMode
|
||||||
@@ -58,6 +62,20 @@ float Body::capacity_percent_get()
|
|||||||
return capacityMax > 0 ? stats.calories / capacityMax : 0;
|
return capacityMax > 0 ? stats.calories / capacityMax : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pushes the belly's spring, harder the bigger the body has grown.
|
||||||
|
void Body::jiggle_add(float amount)
|
||||||
|
{
|
||||||
|
jiggleVelocity += amount * (1 + (float)stage / std::max(1, (int)character->stages.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where the belly is (its expand area's null), or else the middle of the body.
|
||||||
|
Vec2 Body::belly_get()
|
||||||
|
{
|
||||||
|
std::vector<ExpandArea>& areas = character->expandAreas;
|
||||||
|
Rect rect = areas.empty() ? Rect{} : actor.null_rect_get(areas[0].null);
|
||||||
|
return rect_middle(is_rect_valid(rect) ? rect : actor.bounds_get());
|
||||||
|
}
|
||||||
|
|
||||||
bool Body::is_over_capacity()
|
bool Body::is_over_capacity()
|
||||||
{
|
{
|
||||||
return stats.calories > stats.capacity;
|
return stats.calories > stats.capacity;
|
||||||
@@ -184,7 +202,10 @@ void Body::update(std::vector<Event>& events)
|
|||||||
if (!isTalking)
|
if (!isTalking)
|
||||||
idle_play();
|
idle_play();
|
||||||
}
|
}
|
||||||
actor.update(*audio);
|
if (freeze > 0)
|
||||||
|
freeze--;
|
||||||
|
else
|
||||||
|
actor.update(*audio);
|
||||||
|
|
||||||
if (stats.isDigesting && --stats.digestionTimer <= 0)
|
if (stats.isDigesting && --stats.digestionTimer <= 0)
|
||||||
{
|
{
|
||||||
@@ -205,9 +226,12 @@ void Body::update(std::vector<Event>& events)
|
|||||||
stage_set(nextStage);
|
stage_set(nextStage);
|
||||||
isStageUp = true;
|
isStageUp = true;
|
||||||
actor.isLocked = true;
|
actor.isLocked = true;
|
||||||
|
events.push_back(EVENT_STAGE_START);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
events.push_back(EVENT_DIGESTED);
|
events.push_back(EVENT_DIGESTED);
|
||||||
|
gained = nextWeight - stats.weight;
|
||||||
|
events.push_back(EVENT_GAINED);
|
||||||
stats.weight = nextWeight;
|
stats.weight = nextWeight;
|
||||||
stats.isDigesting = false;
|
stats.isDigesting = false;
|
||||||
stats.digestionTimer = traits.digestionTime;
|
stats.digestionTimer = traits.digestionTime;
|
||||||
@@ -222,6 +246,7 @@ 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();
|
||||||
@@ -234,13 +259,21 @@ void Body::update(std::vector<Event>& events)
|
|||||||
float capacityProgress = capacity_percent_get();
|
float capacityProgress = capacity_percent_get();
|
||||||
if (stats.isDigesting)
|
if (stats.isDigesting)
|
||||||
capacityProgress *= (float)stats.digestionTimer / std::max(1, traits.digestionTime);
|
capacityProgress *= (float)stats.digestionTimer / std::max(1, traits.digestionTime);
|
||||||
for (ExpandArea& expandArea : character->expandAreas)
|
jiggleVelocity += -jiggle * JIGGLE_STIFFNESS - jiggleVelocity * JIGGLE_DAMPING;
|
||||||
|
jiggle += jiggleVelocity;
|
||||||
|
float growth = 1 + (float)stage / std::max(1, (int)character->stages.size());
|
||||||
|
float breath = std::sin(stats.playtime * BREATH_SPEED) * BREATH_AMPLITUDE * growth;
|
||||||
|
for (int i = 0; i < (int)character->expandAreas.size(); i++)
|
||||||
{
|
{
|
||||||
|
ExpandArea& expandArea = character->expandAreas[i];
|
||||||
float add = std::clamp((stageProgress + capacityProgress) * expandArea.scaleMax / 2, 0.0f, expandArea.scaleMax);
|
float add = std::clamp((stageProgress + capacityProgress) * expandArea.scaleMax / 2, 0.0f, expandArea.scaleMax);
|
||||||
|
Vec2 scale = {add, add};
|
||||||
|
if (i == 0)
|
||||||
|
scale = scale + Vec2{jiggle, breath - jiggle};
|
||||||
if (expandArea.layer >= 0)
|
if (expandArea.layer >= 0)
|
||||||
actor.layers[expandArea.layer].scale = {add, add};
|
actor.layers[expandArea.layer].scale = scale;
|
||||||
if (expandArea.null >= 0)
|
if (expandArea.null >= 0)
|
||||||
actor.nullScales[expandArea.null] = {add, add};
|
actor.nullScales[expandArea.null] = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
isTalking = is_swapping(actor, character->talk, talkTime, isTalking, SWAP_LOOP);
|
isTalking = is_swapping(actor, character->talk, talkTime, isTalking, SWAP_LOOP);
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#include "effects.h"
|
||||||
|
|
||||||
|
constexpr float FLASH_FADE = 0.04f;
|
||||||
|
constexpr float FLOATER_RISE = 0.8f;
|
||||||
|
constexpr float SPARK_DRAG = 0.95f;
|
||||||
|
constexpr float SPEED_MIN = 0.4f;
|
||||||
|
|
||||||
|
void Effects::update()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)floaters.size(); i++)
|
||||||
|
{
|
||||||
|
floaters[i].position.y -= FLOATER_RISE;
|
||||||
|
if (--floaters[i].life <= 0)
|
||||||
|
floaters.erase(floaters.begin() + i--);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (int)sparks.size(); i++)
|
||||||
|
{
|
||||||
|
Spark& spark = sparks[i];
|
||||||
|
spark.velocity = spark.velocity * SPARK_DRAG;
|
||||||
|
spark.velocity.y += spark.gravity;
|
||||||
|
spark.position = spark.position + spark.velocity;
|
||||||
|
if (--spark.life <= 0)
|
||||||
|
sparks.erase(sparks.begin() + i--);
|
||||||
|
}
|
||||||
|
flash = std::max(0.0f, flash - FLASH_FADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Effects::floater_add(Vec2 position, float value, FloaterType type)
|
||||||
|
{
|
||||||
|
floaters.push_back({position, value, FLOATER_LIFE, type});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Effects::spark_add(Spark spark)
|
||||||
|
{
|
||||||
|
spark.lifeMax = spark.life;
|
||||||
|
sparks.push_back(spark);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throws count copies of a spark out in every direction, at up to speed.
|
||||||
|
void Effects::burst_add(Spark spark, int count, float speed)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
float angle = random_get(0, 2 * PI);
|
||||||
|
float magnitude = random_get(speed * SPEED_MIN, speed);
|
||||||
|
Spark each = spark;
|
||||||
|
each.velocity = Vec2{std::cos(angle), std::sin(angle)} * magnitude + spark.velocity;
|
||||||
|
spark_add(each);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
constexpr int FLOATER_LIFE = 75;
|
||||||
|
|
||||||
|
enum FloaterType
|
||||||
|
{
|
||||||
|
FLOATER_CALORIES,
|
||||||
|
FLOATER_WEIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SparkShape
|
||||||
|
{
|
||||||
|
SPARK_CIRCLE,
|
||||||
|
SPARK_STAR
|
||||||
|
};
|
||||||
|
|
||||||
|
// A number rising from where something happened, fading as it goes.
|
||||||
|
struct Floater
|
||||||
|
{
|
||||||
|
Vec2 position;
|
||||||
|
float value = 0;
|
||||||
|
int life = 0;
|
||||||
|
FloaterType type = FLOATER_CALORIES;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A glint or puff that drifts, falls (or rises) by its gravity, and fades over its life.
|
||||||
|
struct Spark
|
||||||
|
{
|
||||||
|
Color color;
|
||||||
|
Vec2 position;
|
||||||
|
Vec2 velocity;
|
||||||
|
float gravity = 0;
|
||||||
|
float size = 0;
|
||||||
|
int life = 0;
|
||||||
|
int lifeMax = 0;
|
||||||
|
SparkShape shape = SPARK_CIRCLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Short flourishes over the world: rising numbers, sparks, and the screen flashing.
|
||||||
|
class Effects
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<Floater> floaters;
|
||||||
|
std::vector<Spark> sparks;
|
||||||
|
float flash = 0;
|
||||||
|
|
||||||
|
void update();
|
||||||
|
void floater_add(Vec2 position, float value, FloaterType type);
|
||||||
|
void spark_add(Spark spark);
|
||||||
|
void burst_add(Spark spark, int count, float speed);
|
||||||
|
};
|
||||||
+12
-2
@@ -1,6 +1,9 @@
|
|||||||
#include "play.h"
|
#include "play.h"
|
||||||
|
|
||||||
constexpr const char* FULL_SUFFIX = "Full";
|
constexpr const char* FULL_SUFFIX = "Full";
|
||||||
|
constexpr float RUB_JIGGLE = 0.3f;
|
||||||
|
constexpr float SMACK_JIGGLE = 6;
|
||||||
|
constexpr int HIT_STOP = 3;
|
||||||
|
|
||||||
constexpr float AUTO_ACCELERATION = 0.45f;
|
constexpr float AUTO_ACCELERATION = 0.45f;
|
||||||
constexpr float AUTO_ANGLE_STEP = 0.08f;
|
constexpr float AUTO_ANGLE_STEP = 0.08f;
|
||||||
@@ -63,6 +66,11 @@ void Interactions::update(Play& play, Pointer pointer)
|
|||||||
if (entry.digestionClick > 0 && body.stats.calories > 0 && !body.stats.isDigesting)
|
if (entry.digestionClick > 0 && body.stats.calories > 0 && !body.stats.isDigesting)
|
||||||
body.stats.digestionProgress += entry.digestionClick;
|
body.stats.digestionProgress += entry.digestionClick;
|
||||||
body.actor.pulse_add(entry.layer, entry.scaleAmplitude, entry.scaleCycles, entry.scaleTime);
|
body.actor.pulse_add(entry.layer, entry.scaleAmplitude, entry.scaleCycles, entry.scaleTime);
|
||||||
|
if (entry.scaleAmplitude > 0)
|
||||||
|
{
|
||||||
|
body.jiggle_add(SMACK_JIGGLE);
|
||||||
|
body.freeze = HIT_STOP;
|
||||||
|
}
|
||||||
bool isReady = cooldowns[i] <= 0;
|
bool isReady = cooldowns[i] <= 0;
|
||||||
cooldowns[i] = std::max(0, entry.dialogueCooldown);
|
cooldowns[i] = std::max(0, entry.dialogueCooldown);
|
||||||
Dialogue& dialogue = character.dialogue;
|
Dialogue& dialogue = character.dialogue;
|
||||||
@@ -82,8 +90,10 @@ void Interactions::update(Play& play, Pointer pointer)
|
|||||||
play.cursorState = CURSOR_ACTION;
|
play.cursorState = CURSOR_ACTION;
|
||||||
active.play(entry.cursorActive);
|
active.play(entry.cursorActive);
|
||||||
Vec2 moved = pointer.position - pointerPrevious;
|
Vec2 moved = pointer.position - pointerPrevious;
|
||||||
if (entry.digestionHover > 0 && body.stats.calories > 0 && !body.stats.isDigesting &&
|
bool isMoved = moved.x != 0 || moved.y != 0;
|
||||||
(moved.x != 0 || moved.y != 0))
|
if (entry.digestionHover > 0 && isMoved)
|
||||||
|
body.jiggle_add(RUB_JIGGLE);
|
||||||
|
if (entry.digestionHover > 0 && body.stats.calories > 0 && !body.stats.isDigesting && isMoved)
|
||||||
body.stats.digestionProgress += entry.digestionHover;
|
body.stats.digestionProgress += entry.digestionHover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+73
-9
@@ -8,7 +8,26 @@ constexpr float CRUMB_SPEED_X = 4;
|
|||||||
constexpr float CRUMB_SPEED_Y_MAX = 7;
|
constexpr float CRUMB_SPEED_Y_MAX = 7;
|
||||||
constexpr float CRUMB_SPEED_Y_MIN = 2;
|
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 TRAIL_COLOR = {1, 1, 1, 0.35f};
|
||||||
|
constexpr const char* DUST_PARTICLE = "Puff";
|
||||||
|
constexpr float BITE_JIGGLE = 4;
|
||||||
|
constexpr float DUST_IMPACT = 12;
|
||||||
|
constexpr float FANFARE_MIN = 0.75f;
|
||||||
|
constexpr float FANFARE_SHAKE = 0.015f;
|
||||||
|
constexpr float FANFARE_SPEED = 5;
|
||||||
constexpr float HOLD_SPIN = 0.1f;
|
constexpr float HOLD_SPIN = 0.1f;
|
||||||
|
constexpr float RARE_SPARKLE_CHANCE = 12;
|
||||||
|
constexpr float RARE_SPARKLE_RISE = -0.5f;
|
||||||
|
constexpr float SQUASH_DECAY = 0.82f;
|
||||||
|
constexpr float SQUASH_MAX = 0.35f;
|
||||||
|
constexpr float SQUASH_PER_SPEED = 0.025f;
|
||||||
|
constexpr float TRAIL_SPEED = 12;
|
||||||
|
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 TRAIL_SPARK = {TRAIL_COLOR, {}, {}, 0, 7, 14, 0, SPARK_CIRCLE};
|
||||||
|
constexpr int FANFARE_COUNT = 20;
|
||||||
|
constexpr int FANFARE_SHAKE_TIME = 12;
|
||||||
constexpr int CRUMB_COUNT = 6;
|
constexpr int CRUMB_COUNT = 6;
|
||||||
constexpr float ROTATION_GROUND_DAMPING = 0.85f;
|
constexpr float ROTATION_GROUND_DAMPING = 0.85f;
|
||||||
constexpr float ROTATION_MAX = 90;
|
constexpr float ROTATION_MAX = 90;
|
||||||
@@ -17,7 +36,7 @@ constexpr float THROW_THRESHOLD = 10;
|
|||||||
|
|
||||||
void Items::add(Play& play, int id, Vec2 position, Vec2 velocity, float rotation)
|
void Items::add(Play& play, int id, Vec2 position, Vec2 velocity, float rotation)
|
||||||
{
|
{
|
||||||
ItemEntity& entity = list.emplace_back(ItemEntity{play.character->item_actor_get(id), velocity, 0, id});
|
ItemEntity& entity = list.emplace_back(ItemEntity{play.character->item_actor_get(id), {}, velocity, 0, id});
|
||||||
entity.actor.position = position;
|
entity.actor.position = position;
|
||||||
entity.actor.rotation = rotation;
|
entity.actor.rotation = rotation;
|
||||||
discovered[id] = true;
|
discovered[id] = true;
|
||||||
@@ -46,8 +65,17 @@ void Items::crumbs_spawn(ItemEntity& source)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moves an item or crumb a tick under gravity, bouncing off the bounds and settling on the ground. True if it bounced.
|
// How special an item's rarity is, from 0 (the first, or none) to 1 (the last).
|
||||||
static bool motion_update(Play& play, ItemEntity& entity)
|
static float rarity_intensity_get(Play& play, int id)
|
||||||
|
{
|
||||||
|
int rarity = play.character->catalog.items[id].rarity;
|
||||||
|
int count = (int)play.character->catalog.rarities.size();
|
||||||
|
return rarity > 0 && count > 1 ? (float)rarity / (count - 1) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves an item or crumb a tick under gravity, bouncing off the bounds and settling on the ground. Gives the speed it
|
||||||
|
// hit something at (0 if it didn't), and squashes it by that.
|
||||||
|
static float motion_update(Play& play, ItemEntity& entity)
|
||||||
{
|
{
|
||||||
Area& area = play.character->area;
|
Area& area = play.character->area;
|
||||||
Rect bounds = play.bounds;
|
Rect bounds = play.bounds;
|
||||||
@@ -58,16 +86,17 @@ static bool motion_update(Play& play, ItemEntity& entity)
|
|||||||
gravity = gravity != 0 ? gravity : area.gravity;
|
gravity = gravity != 0 ? gravity : area.gravity;
|
||||||
float right = bounds.x + bounds.w;
|
float right = bounds.x + bounds.w;
|
||||||
float bottom = bounds.y + bounds.h;
|
float bottom = bounds.y + bounds.h;
|
||||||
bool isBounced = false;
|
float impact = 0;
|
||||||
position = position + velocity;
|
position = position + velocity;
|
||||||
rotation += entity.angularVelocity;
|
rotation += entity.angularVelocity;
|
||||||
if (position.x <= bounds.x || position.x >= right)
|
if (position.x <= bounds.x || position.x >= right)
|
||||||
{
|
{
|
||||||
position.x = position.x <= bounds.x ? bounds.x + 1 : right - 1;
|
position.x = position.x <= bounds.x ? bounds.x + 1 : right - 1;
|
||||||
|
impact = std::abs(velocity.x);
|
||||||
|
entity.squash = Vec2{-1, 1} * std::min(SQUASH_MAX, impact * SQUASH_PER_SPEED);
|
||||||
velocity.x = -velocity.x * area.friction;
|
velocity.x = -velocity.x * area.friction;
|
||||||
entity.angularVelocity = -entity.angularVelocity * area.friction;
|
entity.angularVelocity = -entity.angularVelocity * area.friction;
|
||||||
rotation = -rotation;
|
rotation = -rotation;
|
||||||
isBounced = true;
|
|
||||||
}
|
}
|
||||||
if (position.y <= bounds.y || position.y >= bottom)
|
if (position.y <= bounds.y || position.y >= bottom)
|
||||||
{
|
{
|
||||||
@@ -82,16 +111,17 @@ static bool motion_update(Play& play, ItemEntity& entity)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
impact = std::abs(velocity.y);
|
||||||
|
entity.squash = Vec2{1, -1} * std::min(SQUASH_MAX, impact * SQUASH_PER_SPEED);
|
||||||
velocity.y = -velocity.y;
|
velocity.y = -velocity.y;
|
||||||
entity.angularVelocity *= area.friction;
|
entity.angularVelocity *= area.friction;
|
||||||
isBounced = true;
|
|
||||||
}
|
}
|
||||||
velocity.y *= area.friction;
|
velocity.y *= area.friction;
|
||||||
}
|
}
|
||||||
velocity.x *= area.airResistance;
|
velocity.x *= area.airResistance;
|
||||||
velocity.y += gravity;
|
velocity.y += gravity;
|
||||||
position = {std::clamp(position.x, bounds.x, right), std::clamp(position.y, bounds.y, bottom)};
|
position = {std::clamp(position.x, bounds.x, right), std::clamp(position.y, bounds.y, bottom)};
|
||||||
return isBounced;
|
return impact;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forgets which item is being eaten, and lets go of the held one unless asked to keep it.
|
// Forgets which item is being eaten, and lets go of the held one unless asked to keep it.
|
||||||
@@ -209,6 +239,13 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
audio.play(character.sounds[SOUND_NEW_ITEM]);
|
audio.play(character.sounds[SOUND_NEW_ITEM]);
|
||||||
}
|
}
|
||||||
add(play, id, position);
|
add(play, id, position);
|
||||||
|
if (rarity_intensity_get(play, id) >= FANFARE_MIN)
|
||||||
|
{
|
||||||
|
Spark spark = FANFARE_SPARK;
|
||||||
|
spark.position = position;
|
||||||
|
play.effects.burst_add(spark, FANFARE_COUNT, FANFARE_SPEED);
|
||||||
|
play.camera.shake_add(FANFARE_SHAKE, FANFARE_SHAKE_TIME);
|
||||||
|
}
|
||||||
stats.totalSpawned++;
|
stats.totalSpawned++;
|
||||||
play.particles_spawn(character.catalog.summonParticles, position);
|
play.particles_spawn(character.catalog.summonParticles, position);
|
||||||
}
|
}
|
||||||
@@ -256,6 +293,8 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
traits.digestionRateMin, traits.digestionRateMax);
|
traits.digestionRateMin, traits.digestionRateMax);
|
||||||
stats.totalConsumed++;
|
stats.totalConsumed++;
|
||||||
crumbs_spawn(entity);
|
crumbs_spawn(entity);
|
||||||
|
play.effects.floater_add(entity.actor.position, item.calories, FLOATER_CALORIES);
|
||||||
|
body.jiggle_add(BITE_JIGGLE);
|
||||||
removed = held;
|
removed = held;
|
||||||
drop();
|
drop();
|
||||||
feeding.animation = body.actor.animation;
|
feeding.animation = body.actor.animation;
|
||||||
@@ -297,6 +336,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
if (isThrown)
|
if (isThrown)
|
||||||
text_play(HOOK_THROW);
|
text_play(HOOK_THROW);
|
||||||
entity.velocity = entity.velocity - delta;
|
entity.velocity = entity.velocity - delta;
|
||||||
|
entity.isThrown = isThrown;
|
||||||
release(isThrown);
|
release(isThrown);
|
||||||
}
|
}
|
||||||
if (removed >= 0)
|
if (removed >= 0)
|
||||||
@@ -347,8 +387,32 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
position = {std::clamp(position.x, bounds.x, bounds.x + bounds.w),
|
position = {std::clamp(position.x, bounds.x, bounds.x + bounds.w),
|
||||||
std::clamp(position.y, bounds.y, bounds.y + bounds.h)};
|
std::clamp(position.y, bounds.y, bounds.y + bounds.h)};
|
||||||
}
|
}
|
||||||
else if (motion_update(play, entity))
|
else
|
||||||
audio.play(character.sounds[SOUND_BOUNCE]);
|
{
|
||||||
|
float impact = motion_update(play, entity);
|
||||||
|
if (impact > 0)
|
||||||
|
audio.play(character.sounds[SOUND_BOUNCE]);
|
||||||
|
if (impact >= DUST_IMPACT)
|
||||||
|
play.particles_spawn({DUST_PARTICLE}, position);
|
||||||
|
entity.isThrown &= vec2_length(entity.velocity) >= TRAIL_SPEED;
|
||||||
|
if (entity.isThrown)
|
||||||
|
{
|
||||||
|
Spark spark = TRAIL_SPARK;
|
||||||
|
spark.position = position;
|
||||||
|
play.effects.spark_add(spark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entity.squash = entity.squash * SQUASH_DECAY;
|
||||||
|
for (LayerChange& layer : entity.actor.layers)
|
||||||
|
layer.scale = entity.squash * PERCENT;
|
||||||
|
if (i != held && is_chance(rarity_intensity_get(play, entity.id) * RARE_SPARKLE_CHANCE))
|
||||||
|
{
|
||||||
|
Rect rect = entity.actor.bounds_get();
|
||||||
|
Spark spark = RARE_SPARK;
|
||||||
|
spark.position = {rect.x + random_get(0, rect.w), rect.y + random_get(0, rect.h)};
|
||||||
|
spark.velocity = {0, RARE_SPARKLE_RISE};
|
||||||
|
play.effects.spark_add(spark);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < (int)crumbs.size(); i++)
|
for (int i = 0; i < (int)crumbs.size(); i++)
|
||||||
|
|||||||
+45
-1
@@ -11,7 +11,24 @@ 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 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_EPSILON = 0.001f;
|
||||||
|
constexpr float STAGE_FLASH = 0.7f;
|
||||||
|
constexpr float STAGE_JIGGLE = 15;
|
||||||
|
constexpr float STAGE_PUNCH = 0.08f;
|
||||||
|
constexpr float STAGE_SHAKE = 0.04f;
|
||||||
|
constexpr float STAGE_SPEED = 8;
|
||||||
|
constexpr int DIGEST_COUNT = 10;
|
||||||
|
constexpr int DIGEST_SHAKE_TIME = 40;
|
||||||
|
constexpr int STAGE_COUNT = 28;
|
||||||
|
constexpr int STAGE_FREEZE = 5;
|
||||||
|
constexpr int STAGE_SHAKE_TIME = 30;
|
||||||
constexpr float CHEAT_TIME = 5;
|
constexpr float CHEAT_TIME = 5;
|
||||||
constexpr float FOCUS_PADDING = 100;
|
constexpr float FOCUS_PADDING = 100;
|
||||||
constexpr float SHAKE_DECAY = 0.97f;
|
constexpr float SHAKE_DECAY = 0.97f;
|
||||||
@@ -176,6 +193,25 @@ 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.position = body.belly_get();
|
||||||
|
if (event == EVENT_GAINED && body.gained > 0)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
effects.burst_add(spark, STAGE_COUNT, STAGE_SPEED);
|
||||||
|
effects.flash = STAGE_FLASH;
|
||||||
|
camera.shake_add(STAGE_SHAKE, STAGE_SHAKE_TIME);
|
||||||
|
camera.punch = STAGE_PUNCH;
|
||||||
|
body.jiggle_add(STAGE_JIGGLE);
|
||||||
|
body.freeze = STAGE_FREEZE;
|
||||||
|
}
|
||||||
if (event == EVENT_DIGESTED && !text.isScene && text.is_interruptible())
|
if (event == EVENT_DIGESTED && !text.isScene && text.is_interruptible())
|
||||||
text.set(*this, dialogue.hook_get(HOOK_DIGEST));
|
text.set(*this, dialogue.hook_get(HOOK_DIGEST));
|
||||||
if (event == EVENT_STAGE_UP && !text.isScene)
|
if (event == EVENT_STAGE_UP && !text.isScene)
|
||||||
@@ -240,6 +276,7 @@ void Play::update(Vec2 screenSize)
|
|||||||
area_set();
|
area_set();
|
||||||
area.update(*audio);
|
area.update(*audio);
|
||||||
|
|
||||||
|
effects.update();
|
||||||
for (int i = 0; i < (int)particles.size(); i++)
|
for (int i = 0; i < (int)particles.size(); i++)
|
||||||
{
|
{
|
||||||
particles[i].update(*audio);
|
particles[i].update(*audio);
|
||||||
@@ -290,10 +327,14 @@ Vec2 Camera::world_get(Vec2 screen)
|
|||||||
return pan + screen / (zoom / PERCENT);
|
return pan + screen / (zoom / PERCENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The world's transform to the screen, zoomed further about the screen's middle by the punch.
|
||||||
Affine Camera::view_get(Vec2 screenSize)
|
Affine Camera::view_get(Vec2 screenSize)
|
||||||
{
|
{
|
||||||
float scale = zoom / PERCENT;
|
float scale = zoom / PERCENT;
|
||||||
return {scale, 0, 0, scale, -pan.x * scale + shake.x * screenSize.x, -pan.y * scale + shake.y * screenSize.y};
|
Vec2 offset = Vec2{-pan.x * scale + shake.x * screenSize.x, -pan.y * scale + shake.y * screenSize.y} * (1 + punch) -
|
||||||
|
screenSize / 2 * punch;
|
||||||
|
scale *= 1 + punch;
|
||||||
|
return {scale, 0, 0, scale, offset.x, offset.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::shake_add(float magnitude, int time)
|
void Camera::shake_add(float magnitude, int time)
|
||||||
@@ -344,6 +385,9 @@ void Camera::update(Play& play, Vec2 screenSize)
|
|||||||
if (ImGui::IsKeyPressed(ImGuiKey_Home))
|
if (ImGui::IsKeyPressed(ImGuiKey_Home))
|
||||||
play.focus(screenSize);
|
play.focus(screenSize);
|
||||||
|
|
||||||
|
punch = punch * PUNCH_DECAY;
|
||||||
|
if (punch < PUNCH_EPSILON)
|
||||||
|
punch = 0;
|
||||||
if (shakeTime > 0)
|
if (shakeTime > 0)
|
||||||
{
|
{
|
||||||
shakeTime--;
|
shakeTime--;
|
||||||
|
|||||||
+18
-2
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "character.h"
|
#include "character.h"
|
||||||
#include "dialogue.h"
|
#include "dialogue.h"
|
||||||
|
#include "effects.h"
|
||||||
#include "save.h"
|
#include "save.h"
|
||||||
|
|
||||||
constexpr float DIGESTION_MAX = 100;
|
constexpr float DIGESTION_MAX = 100;
|
||||||
@@ -31,7 +32,10 @@ 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_STAGE_START,
|
||||||
EVENT_STAGE_UP,
|
EVENT_STAGE_UP,
|
||||||
EVENT_STAGE_FINAL
|
EVENT_STAGE_FINAL
|
||||||
};
|
};
|
||||||
@@ -50,14 +54,17 @@ struct Pointer
|
|||||||
bool isRightClicked = false;
|
bool isRightClicked = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An item in the world, or a crumb of one (which has a life, in ticks).
|
// An item in the world, or a crumb of one (which has a life, in ticks). Squash is the added scale of its last impact,
|
||||||
|
// and a thrown item trails until it slows down.
|
||||||
struct ItemEntity
|
struct ItemEntity
|
||||||
{
|
{
|
||||||
Actor actor;
|
Actor actor;
|
||||||
|
Vec2 squash;
|
||||||
Vec2 velocity;
|
Vec2 velocity;
|
||||||
float angularVelocity = 0;
|
float angularVelocity = 0;
|
||||||
int id = 0;
|
int id = 0;
|
||||||
int life = 0;
|
int life = 0;
|
||||||
|
bool isThrown = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Toast
|
struct Toast
|
||||||
@@ -68,7 +75,8 @@ struct Toast
|
|||||||
|
|
||||||
class Play;
|
class Play;
|
||||||
|
|
||||||
// The character's body: its stats, digestion and stages, and the animations they choose.
|
// The character's body: its stats, digestion and stages, and the animations they choose. The belly (the first expand
|
||||||
|
// area) breathes and jiggles on a spring, and freeze holds the body still for a hit's few ticks.
|
||||||
class Body
|
class Body
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -79,7 +87,11 @@ public:
|
|||||||
Character* character = nullptr;
|
Character* character = nullptr;
|
||||||
Audio* audio = nullptr;
|
Audio* audio = nullptr;
|
||||||
float blinkTime = 0;
|
float blinkTime = 0;
|
||||||
|
float gained = 0;
|
||||||
|
float jiggle = 0;
|
||||||
|
float jiggleVelocity = 0;
|
||||||
float talkTime = 0;
|
float talkTime = 0;
|
||||||
|
int freeze = 0;
|
||||||
int stage = 0;
|
int stage = 0;
|
||||||
bool isBlinking = false;
|
bool isBlinking = false;
|
||||||
bool isStageUp = false;
|
bool isStageUp = false;
|
||||||
@@ -93,7 +105,9 @@ public:
|
|||||||
void idle_override_set(const std::string& animation);
|
void idle_override_set(const std::string& animation);
|
||||||
void stage_set(int index);
|
void stage_set(int index);
|
||||||
void values_clamp();
|
void values_clamp();
|
||||||
|
void jiggle_add(float amount);
|
||||||
std::string idle_get();
|
std::string idle_get();
|
||||||
|
Vec2 belly_get();
|
||||||
Rect eat_rect_get(const EatArea& eatArea);
|
Rect eat_rect_get(const EatArea& eatArea);
|
||||||
float capacity_max_get();
|
float capacity_max_get();
|
||||||
float capacity_percent_get();
|
float capacity_percent_get();
|
||||||
@@ -180,6 +194,7 @@ class Camera
|
|||||||
public:
|
public:
|
||||||
Vec2 pan;
|
Vec2 pan;
|
||||||
Vec2 shake;
|
Vec2 shake;
|
||||||
|
float punch = 0;
|
||||||
float zoom = ZOOM_LADDER[0];
|
float zoom = ZOOM_LADDER[0];
|
||||||
int shakeTime = 0;
|
int shakeTime = 0;
|
||||||
int shakeTimeMax = 0;
|
int shakeTimeMax = 0;
|
||||||
@@ -202,6 +217,7 @@ public:
|
|||||||
Autofeed autofeed;
|
Autofeed autofeed;
|
||||||
Body body;
|
Body body;
|
||||||
Camera camera;
|
Camera camera;
|
||||||
|
Effects effects;
|
||||||
Interactions interactions;
|
Interactions interactions;
|
||||||
Items items;
|
Items items;
|
||||||
Text text;
|
Text text;
|
||||||
|
|||||||
+66
-2
@@ -25,6 +25,14 @@ 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_SHAKE = 2;
|
||||||
|
constexpr float BAR_SHAKE_SPEED = 40;
|
||||||
|
constexpr float FLOATER_OUTLINE = 2;
|
||||||
|
constexpr float OVERSTUFFED_PULSE = 0.35f;
|
||||||
|
constexpr float OVERSTUFFED_SPEED = 6;
|
||||||
|
constexpr float STAR_WAIST = 0.3f;
|
||||||
constexpr float BAR_WIDTH_MIN = 120;
|
constexpr float BAR_WIDTH_MIN = 120;
|
||||||
constexpr float BUTTON_SIZE_MAX = 96;
|
constexpr float BUTTON_SIZE_MAX = 96;
|
||||||
constexpr float BUTTON_SIZE_MIN = 56;
|
constexpr float BUTTON_SIZE_MIN = 56;
|
||||||
@@ -558,9 +566,16 @@ void Ui::taskbar_draw(Play& play, ImVec2 screen)
|
|||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::TextUnformatted(labels[i]);
|
ImGui::TextUnformatted(labels[i]);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
float time = (float)ImGui::GetTime();
|
||||||
|
barFills[i] += (fills[i] - barFills[i]) * std::min(1.0f, BAR_EASE * ImGui::GetIO().DeltaTime);
|
||||||
if (i == 1 && overstuffed > 0)
|
if (i == 1 && overstuffed > 0)
|
||||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(1, 1 - overstuffed, 1 - overstuffed, 1));
|
{
|
||||||
ImGui::ProgressBar(fills[i], {widths[i], 0}, bars[i].c_str());
|
float red = 1 - overstuffed * (1 - OVERSTUFFED_PULSE * (1 + std::sin(time * OVERSTUFFED_SPEED)) / 2);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(1, red, red, 1));
|
||||||
|
ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||||
|
ImGui::SetCursorScreenPos({cursor.x + std::sin(time * BAR_SHAKE_SPEED) * BAR_SHAKE * overstuffed, cursor.y});
|
||||||
|
}
|
||||||
|
ImGui::ProgressBar(barFills[i], {widths[i], 0}, bars[i].c_str());
|
||||||
if (i == 1 && overstuffed > 0)
|
if (i == 1 && overstuffed > 0)
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
if (i == 0 && ImGui::BeginItemTooltip())
|
if (i == 0 && ImGui::BeginItemTooltip())
|
||||||
@@ -709,6 +724,54 @@ 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.
|
||||||
|
void Ui::effects_draw(Play& play, ImVec2 screen)
|
||||||
|
{
|
||||||
|
Effects& effects = play.effects;
|
||||||
|
Affine view = play.camera.view_get({screen.x, screen.y});
|
||||||
|
ImDrawList* drawList = ImGui::GetBackgroundDrawList();
|
||||||
|
for (Spark& spark : effects.sparks)
|
||||||
|
{
|
||||||
|
Vec2 at = view * spark.position;
|
||||||
|
float fade = (float)spark.life / std::max(1, spark.lifeMax);
|
||||||
|
float size = spark.size * view.a * fade;
|
||||||
|
ImU32 color = ImGui::GetColorU32(ImVec4(spark.color.r, spark.color.g, spark.color.b, spark.color.a * fade));
|
||||||
|
if (spark.shape == SPARK_CIRCLE)
|
||||||
|
{
|
||||||
|
drawList->AddCircleFilled({at.x, at.y}, size, color);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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},
|
||||||
|
color);
|
||||||
|
drawList->AddQuadFilled({at.x - size, at.y}, {at.x, at.y - waist}, {at.x + size, at.y}, {at.x, at.y + waist},
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImFont* font = ImGui::GetFont();
|
||||||
|
for (Floater& floater : effects.floaters)
|
||||||
|
{
|
||||||
|
std::string text = floater.type == FLOATER_CALORIES ? text_format("+%.0f kcal", floater.value)
|
||||||
|
: "+" + weight_format(floater.value, *measurement);
|
||||||
|
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.w = alpha;
|
||||||
|
ImVec2 size = font->CalcTextSizeA(FONT_HEADER_2, FLT_MAX, 0, text.c_str());
|
||||||
|
Vec2 at = view * floater.position;
|
||||||
|
ImVec2 position = {at.x - size.x / 2, at.y - size.y / 2};
|
||||||
|
ImU32 outline = ImGui::GetColorU32(ImVec4(0, 0, 0, alpha));
|
||||||
|
for (int x = -1; x <= 1; x++)
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
if (effects.flash > 0)
|
||||||
|
drawList->AddRectFilled({0, 0}, screen, ImGui::GetColorU32(ImVec4(1, 1, 1, effects.flash)));
|
||||||
|
}
|
||||||
|
|
||||||
// Fading messages stacked down from the top right, newest first.
|
// Fading messages stacked down from the top right, newest first.
|
||||||
void Ui::toasts_draw(Play& play)
|
void Ui::toasts_draw(Play& play)
|
||||||
{
|
{
|
||||||
@@ -994,6 +1057,7 @@ void Ui::play_update(Play& play, Settings& settings)
|
|||||||
character = play.character;
|
character = play.character;
|
||||||
measurement = &settings.measurement;
|
measurement = &settings.measurement;
|
||||||
ImVec2 screen = ImGui::GetIO().DisplaySize;
|
ImVec2 screen = ImGui::GetIO().DisplaySize;
|
||||||
|
effects_draw(play, screen);
|
||||||
if (play.items.tooltip >= 0 && !play.isBlocked && ImGui::BeginTooltip())
|
if (play.items.tooltip >= 0 && !play.isBlocked && ImGui::BeginTooltip())
|
||||||
{
|
{
|
||||||
item_details_draw(*character, &character->catalog.items[play.items.tooltip]);
|
item_details_draw(*character, &character->catalog.items[play.items.tooltip]);
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public:
|
|||||||
Commands* commands = nullptr;
|
Commands* commands = nullptr;
|
||||||
Measurement* measurement = nullptr;
|
Measurement* measurement = nullptr;
|
||||||
Renderer* renderer = nullptr;
|
Renderer* renderer = nullptr;
|
||||||
|
float barFills[3] = {};
|
||||||
float buttonSize = 0;
|
float buttonSize = 0;
|
||||||
float taskbarSlide = 1;
|
float taskbarSlide = 1;
|
||||||
int characterIndex = 0;
|
int characterIndex = 0;
|
||||||
@@ -100,4 +101,5 @@ private:
|
|||||||
void settings_draw(Settings& settings);
|
void settings_draw(Settings& settings);
|
||||||
void text_draw(Play& play, ImVec2 screen);
|
void text_draw(Play& play, ImVec2 screen);
|
||||||
void toasts_draw(Play& play);
|
void toasts_draw(Play& play);
|
||||||
|
void effects_draw(Play& play, ImVec2 screen);
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user