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"
|
#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 float HOLD_SPIN = 0.1f;
|
||||||
|
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;
|
||||||
constexpr float ROTATION_RETURN_SPEED = 120;
|
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;
|
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.
|
// Forgets which item is being eaten, and lets go of the held one unless asked to keep it.
|
||||||
void Items::drop(Hold hold)
|
void Items::drop(Hold hold)
|
||||||
{
|
{
|
||||||
@@ -175,6 +255,7 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
stats.digestionRate = std::clamp(stats.digestionRate + item.digestionBonus,
|
stats.digestionRate = std::clamp(stats.digestionRate + item.digestionBonus,
|
||||||
traits.digestionRateMin, traits.digestionRateMax);
|
traits.digestionRateMin, traits.digestionRateMax);
|
||||||
stats.totalConsumed++;
|
stats.totalConsumed++;
|
||||||
|
crumbs_spawn(entity);
|
||||||
removed = held;
|
removed = held;
|
||||||
drop();
|
drop();
|
||||||
feeding.animation = body.actor.animation;
|
feeding.animation = body.actor.animation;
|
||||||
@@ -224,20 +305,11 @@ void Items::update(Play& play, Pointer pointer)
|
|||||||
|
|
||||||
int moved = -1;
|
int moved = -1;
|
||||||
bool isTargetLocked = pointer.target >= 0 && pointer.target < (int)list.size();
|
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++)
|
for (int i = 0; i < (int)list.size(); i++)
|
||||||
{
|
{
|
||||||
ItemEntity& entity = list[i];
|
ItemEntity& entity = list[i];
|
||||||
Vec2& position = entity.actor.position;
|
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);
|
entity.actor.update(audio);
|
||||||
position = position + velocity;
|
|
||||||
rotation += entity.angularVelocity;
|
|
||||||
|
|
||||||
if (is_point_in_rect(entity.actor.bounds_get(), pointer.position) && !pointer.isCaptured &&
|
if (is_point_in_rect(entity.actor.bounds_get(), pointer.position) && !pointer.isCaptured &&
|
||||||
(!isTargetLocked || i == pointer.target))
|
(!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)
|
*entity.actor.rotation += entity.angularVelocity;
|
||||||
{
|
position = {std::clamp(position.x, bounds.x, bounds.x + bounds.w),
|
||||||
position.x = position.x <= bounds.x ? bounds.x + 1 : right - 1;
|
std::clamp(position.y, bounds.y, bounds.y + bounds.h)};
|
||||||
velocity.x = -velocity.x * area.friction;
|
}
|
||||||
entity.angularVelocity = -entity.angularVelocity * area.friction;
|
else if (motion_update(play, entity))
|
||||||
rotation = -rotation;
|
|
||||||
audio.play(character.sounds[SOUND_BOUNCE]);
|
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;
|
crumbs.erase(crumbs.begin() + i--);
|
||||||
velocity = {velocity.x * area.friction, 0};
|
continue;
|
||||||
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
|
motion_update(play, crumb);
|
||||||
{
|
float shrink = std::min(1.0f, crumb.life / (CRUMB_LIFE * CRUMB_SHRINK * TICK_RATE));
|
||||||
velocity.y = -velocity.y;
|
for (LayerChange& layer : crumb.actor.layers)
|
||||||
entity.angularVelocity *= area.friction;
|
layer.scale = Vec2{shrink - 1, shrink - 1} * PERCENT;
|
||||||
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)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moved >= 0 && moved < (int)list.size() - 1)
|
if (moved >= 0 && moved < (int)list.size() - 1)
|
||||||
|
|||||||
@@ -273,8 +273,12 @@ void Play::render(Renderer& renderer, Vec2 screenSize)
|
|||||||
area.render(renderer, view);
|
area.render(renderer, view);
|
||||||
body.actor.render(renderer, view);
|
body.actor.render(renderer, view);
|
||||||
if (!text.isScene)
|
if (!text.isScene)
|
||||||
|
{
|
||||||
for (ItemEntity& entity : items.list)
|
for (ItemEntity& entity : items.list)
|
||||||
entity.actor.render(renderer, view);
|
entity.actor.render(renderer, view);
|
||||||
|
for (ItemEntity& crumb : items.crumbs)
|
||||||
|
crumb.actor.render(renderer, view);
|
||||||
|
}
|
||||||
if (autofeed.isActive)
|
if (autofeed.isActive)
|
||||||
autoCursor.render(renderer, view);
|
autoCursor.render(renderer, view);
|
||||||
for (Actor& particle : particles)
|
for (Actor& particle : particles)
|
||||||
|
|||||||
@@ -50,12 +50,14 @@ struct Pointer
|
|||||||
bool isRightClicked = false;
|
bool isRightClicked = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An item in the world, or a crumb of one (which has a life, in ticks).
|
||||||
struct ItemEntity
|
struct ItemEntity
|
||||||
{
|
{
|
||||||
Actor actor;
|
Actor actor;
|
||||||
Vec2 velocity;
|
Vec2 velocity;
|
||||||
float angularVelocity = 0;
|
float angularVelocity = 0;
|
||||||
int id = 0;
|
int id = 0;
|
||||||
|
int life = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Toast
|
struct Toast
|
||||||
@@ -116,6 +118,7 @@ struct Feeding
|
|||||||
class Items
|
class Items
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
std::vector<ItemEntity> crumbs;
|
||||||
std::vector<ItemEntity> list;
|
std::vector<ItemEntity> list;
|
||||||
std::vector<int> spawnQueue;
|
std::vector<int> spawnQueue;
|
||||||
std::vector<bool> discovered;
|
std::vector<bool> discovered;
|
||||||
@@ -130,6 +133,7 @@ public:
|
|||||||
void update(Play& play, Pointer pointer);
|
void update(Play& play, Pointer pointer);
|
||||||
void add(Play& play, int id, Vec2 position, Vec2 velocity = {}, float rotation = 0);
|
void add(Play& play, int id, Vec2 position, Vec2 velocity = {}, float rotation = 0);
|
||||||
void drop(Hold hold = HOLD_RELEASE);
|
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 id);
|
||||||
void spawn_queue_add(Play& play);
|
void spawn_queue_add(Play& play);
|
||||||
int spawn_delay_get(Play& play);
|
int spawn_delay_get(Play& play);
|
||||||
|
|||||||
Reference in new issue
Block a user