Crumbs from eaten food
This commit is contained in:
1 parent
8b343d5a4a
commit
7e27ffad94
3 files changed
+105
-36
No files matched your search
+97
-36
@@ -1,6 +1,15 @@
|
||||
#include "play.h"
|
||||
|
||||
constexpr float CRUMB_LIFE = 1.5f;
|
||||
constexpr float CRUMB_SHRINK = 0.3f;
|
||||
constexpr float CRUMB_SIZE_MAX = 0.35f;
|
||||
constexpr float CRUMB_SIZE_MIN = 0.15f;
|
||||
constexpr float CRUMB_SPEED_X = 4;
|
||||
constexpr float CRUMB_SPEED_Y_MAX = 7;
|
||||
constexpr float CRUMB_SPEED_Y_MIN = 2;
|
||||
constexpr float CRUMB_SPIN = 15;
|
||||
constexpr float HOLD_SPIN = 0.1f;
|
||||
constexpr int CRUMB_COUNT = 6;
|
||||
constexpr float ROTATION_GROUND_DAMPING = 0.85f;
|
||||
constexpr float ROTATION_MAX = 90;
|
||||
constexpr float ROTATION_RETURN_SPEED = 120;
|
||||
@@ -14,6 +23,77 @@ void Items::add(Play& play, int id, Vec2 position, Vec2 velocity, float rotation
|
||||
discovered[id] = true;
|
||||
}
|
||||
|
||||
// Bursts slices of an item's graphic from where it is, to fall and bounce like items until they shrink away.
|
||||
void Items::crumbs_spawn(ItemEntity& source)
|
||||
{
|
||||
for (int i = 0; i < CRUMB_COUNT; i++)
|
||||
{
|
||||
ItemEntity& crumb = crumbs.emplace_back(source);
|
||||
float fraction = random_get(CRUMB_SIZE_MIN, CRUMB_SIZE_MAX);
|
||||
for (LayerChange& layer : crumb.actor.layers)
|
||||
{
|
||||
if (!layer.isRegion)
|
||||
continue;
|
||||
Vec2 size = layer.size * fraction;
|
||||
layer.crop = {layer.crop.x + random_get(0, layer.size.x - size.x),
|
||||
layer.crop.y + random_get(0, layer.size.y - size.y)};
|
||||
layer.size = size;
|
||||
layer.pivot = size / 2;
|
||||
}
|
||||
crumb.velocity = {random_get(-CRUMB_SPEED_X, CRUMB_SPEED_X), -random_get(CRUMB_SPEED_Y_MIN, CRUMB_SPEED_Y_MAX)};
|
||||
crumb.angularVelocity = random_get(-CRUMB_SPIN, CRUMB_SPIN);
|
||||
crumb.life = (int)(CRUMB_LIFE * TICK_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
// Moves an item or crumb a tick under gravity, bouncing off the bounds and settling on the ground. True if it bounced.
|
||||
static bool motion_update(Play& play, ItemEntity& entity)
|
||||
{
|
||||
Area& area = play.character->area;
|
||||
Rect bounds = play.bounds;
|
||||
Vec2& position = entity.actor.position;
|
||||
Vec2& velocity = entity.velocity;
|
||||
float& rotation = *entity.actor.rotation;
|
||||
float gravity = play.character->catalog.items[entity.id].gravity;
|
||||
gravity = gravity != 0 ? gravity : area.gravity;
|
||||
float right = bounds.x + bounds.w;
|
||||
float bottom = bounds.y + bounds.h;
|
||||
bool isBounced = false;
|
||||
position = position + velocity;
|
||||
rotation += entity.angularVelocity;
|
||||
if (position.x <= bounds.x || position.x >= right)
|
||||
{
|
||||
position.x = position.x <= bounds.x ? bounds.x + 1 : right - 1;
|
||||
velocity.x = -velocity.x * area.friction;
|
||||
entity.angularVelocity = -entity.angularVelocity * area.friction;
|
||||
rotation = -rotation;
|
||||
isBounced = true;
|
||||
}
|
||||
if (position.y <= bounds.y || position.y >= bottom)
|
||||
{
|
||||
if (position.y >= bottom && velocity.y <= gravity)
|
||||
{
|
||||
position.y = bottom;
|
||||
velocity = {velocity.x * area.friction, 0};
|
||||
rotation = std::remainder(rotation, 360.0f);
|
||||
float step = ROTATION_RETURN_SPEED * TICK;
|
||||
rotation = std::abs(rotation) <= step ? 0 : rotation - std::copysign(step, rotation);
|
||||
entity.angularVelocity *= ROTATION_GROUND_DAMPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
velocity.y = -velocity.y;
|
||||
entity.angularVelocity *= area.friction;
|
||||
isBounced = true;
|
||||
}
|
||||
velocity.y *= area.friction;
|
||||
}
|
||||
velocity.x *= area.airResistance;
|
||||
velocity.y += gravity;
|
||||
position = {std::clamp(position.x, bounds.x, right), std::clamp(position.y, bounds.y, bottom)};
|
||||
return isBounced;
|
||||
}
|
||||
|
||||
// Forgets which item is being eaten, and lets go of the held one unless asked to keep it.
|
||||
void Items::drop(Hold hold)
|
||||
{
|
||||
@@ -175,6 +255,7 @@ void Items::update(Play& play, Pointer pointer)
|
||||
stats.digestionRate = std::clamp(stats.digestionRate + item.digestionBonus,
|
||||
traits.digestionRateMin, traits.digestionRateMax);
|
||||
stats.totalConsumed++;
|
||||
crumbs_spawn(entity);
|
||||
removed = held;
|
||||
drop();
|
||||
feeding.animation = body.actor.animation;
|
||||
@@ -224,20 +305,11 @@ void Items::update(Play& play, Pointer pointer)
|
||||
|
||||
int moved = -1;
|
||||
bool isTargetLocked = pointer.target >= 0 && pointer.target < (int)list.size();
|
||||
float right = bounds.x + bounds.w;
|
||||
float bottom = bounds.y + bounds.h;
|
||||
for (int i = 0; i < (int)list.size(); i++)
|
||||
{
|
||||
ItemEntity& entity = list[i];
|
||||
Vec2& position = entity.actor.position;
|
||||
Vec2& velocity = entity.velocity;
|
||||
float& rotation = *entity.actor.rotation;
|
||||
float gravity = character.catalog.items[entity.id].gravity;
|
||||
gravity = gravity != 0 ? gravity : area.gravity;
|
||||
entity.actor.update(audio);
|
||||
position = position + velocity;
|
||||
rotation += entity.angularVelocity;
|
||||
|
||||
if (is_point_in_rect(entity.actor.bounds_get(), pointer.position) && !pointer.isCaptured &&
|
||||
(!isTargetLocked || i == pointer.target))
|
||||
{
|
||||
@@ -269,39 +341,28 @@ void Items::update(Play& play, Pointer pointer)
|
||||
}
|
||||
}
|
||||
|
||||
if (i != held)
|
||||
if (i == held)
|
||||
{
|
||||
if (position.x <= bounds.x || position.x >= right)
|
||||
{
|
||||
position.x = position.x <= bounds.x ? bounds.x + 1 : right - 1;
|
||||
velocity.x = -velocity.x * area.friction;
|
||||
entity.angularVelocity = -entity.angularVelocity * area.friction;
|
||||
rotation = -rotation;
|
||||
*entity.actor.rotation += entity.angularVelocity;
|
||||
position = {std::clamp(position.x, bounds.x, bounds.x + bounds.w),
|
||||
std::clamp(position.y, bounds.y, bounds.y + bounds.h)};
|
||||
}
|
||||
else if (motion_update(play, entity))
|
||||
audio.play(character.sounds[SOUND_BOUNCE]);
|
||||
}
|
||||
if (position.y <= bounds.y || position.y >= bottom)
|
||||
|
||||
for (int i = 0; i < (int)crumbs.size(); i++)
|
||||
{
|
||||
if (position.y >= bottom && velocity.y <= gravity)
|
||||
ItemEntity& crumb = crumbs[i];
|
||||
if (--crumb.life <= 0)
|
||||
{
|
||||
position.y = bottom;
|
||||
velocity = {velocity.x * area.friction, 0};
|
||||
rotation = std::remainder(rotation, 360.0f);
|
||||
float step = ROTATION_RETURN_SPEED * TICK;
|
||||
rotation = std::abs(rotation) <= step ? 0 : rotation - std::copysign(step, rotation);
|
||||
entity.angularVelocity *= ROTATION_GROUND_DAMPING;
|
||||
crumbs.erase(crumbs.begin() + i--);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
velocity.y = -velocity.y;
|
||||
entity.angularVelocity *= area.friction;
|
||||
audio.play(character.sounds[SOUND_BOUNCE]);
|
||||
}
|
||||
velocity.y *= area.friction;
|
||||
}
|
||||
velocity.x *= area.airResistance;
|
||||
velocity.y += gravity;
|
||||
}
|
||||
position = {std::clamp(position.x, bounds.x, right), std::clamp(position.y, bounds.y, bottom)};
|
||||
motion_update(play, crumb);
|
||||
float shrink = std::min(1.0f, crumb.life / (CRUMB_LIFE * CRUMB_SHRINK * TICK_RATE));
|
||||
for (LayerChange& layer : crumb.actor.layers)
|
||||
layer.scale = Vec2{shrink - 1, shrink - 1} * PERCENT;
|
||||
}
|
||||
|
||||
if (moved >= 0 && moved < (int)list.size() - 1)
|
||||
|
||||
@@ -273,8 +273,12 @@ void Play::render(Renderer& renderer, Vec2 screenSize)
|
||||
area.render(renderer, view);
|
||||
body.actor.render(renderer, view);
|
||||
if (!text.isScene)
|
||||
{
|
||||
for (ItemEntity& entity : items.list)
|
||||
entity.actor.render(renderer, view);
|
||||
for (ItemEntity& crumb : items.crumbs)
|
||||
crumb.actor.render(renderer, view);
|
||||
}
|
||||
if (autofeed.isActive)
|
||||
autoCursor.render(renderer, view);
|
||||
for (Actor& particle : particles)
|
||||
|
||||
@@ -50,12 +50,14 @@ struct Pointer
|
||||
bool isRightClicked = false;
|
||||
};
|
||||
|
||||
// An item in the world, or a crumb of one (which has a life, in ticks).
|
||||
struct ItemEntity
|
||||
{
|
||||
Actor actor;
|
||||
Vec2 velocity;
|
||||
float angularVelocity = 0;
|
||||
int id = 0;
|
||||
int life = 0;
|
||||
};
|
||||
|
||||
struct Toast
|
||||
@@ -116,6 +118,7 @@ struct Feeding
|
||||
class Items
|
||||
{
|
||||
public:
|
||||
std::vector<ItemEntity> crumbs;
|
||||
std::vector<ItemEntity> list;
|
||||
std::vector<int> spawnQueue;
|
||||
std::vector<bool> discovered;
|
||||
@@ -130,6 +133,7 @@ public:
|
||||
void update(Play& play, Pointer pointer);
|
||||
void add(Play& play, int id, Vec2 position, Vec2 velocity = {}, float rotation = 0);
|
||||
void drop(Hold hold = HOLD_RELEASE);
|
||||
void crumbs_spawn(ItemEntity& source);
|
||||
void spawn_queue_add(Play& play, int id);
|
||||
void spawn_queue_add(Play& play);
|
||||
int spawn_delay_get(Play& play);
|
||||
|
||||
Reference in new issue
Block a user