Weapons built from traits, fashion, Hourglass, About tab, and Ithaca
- Every attack is now assembled from traits on its definition instead of per-weapon code: an anchor and path (thrusts, out-and-back, arcs), shared flight physics (gravity, drag, acceleration, homing, and one wall response: end, burst, stick, crawl, bounce, stop, pass, return, latch), and flags like bouncy, drifting, tethered, orbiting, instant and arming. Melee are projectiles too. - Shots have a lifetime and a fade (when damage bottoms out). Range is speed times lifetime. Ricochets and the Vacuum wind lifetime back. - The Vacuum pulls anyone's shots, and its airblast flips them out along the aim, claiming enemies' (fading into your colour). - New trinket: Hourglass, longer projectile lifetime and range. Ropes and chains lengthen with it and the Accelerator. - Fashion: 23 items worn on the head, face or torso, chosen in Profile. - Flamethrower flames hit continuously, the whip coils and sweeps, bouncing shots roll over ramps, and Ricochet relights grenade fuses. - Profile tab, colour tooltips, option hints on every setting, an About tab with credits and links, and the Ithaca font. - Loadouts lock once readied, tomatoes clear with the match, and the round-over pause is 4 seconds. - CC0 license. Tools and CLAUDE.md are no longer tracked, and the web shell moves to web/.
This commit is contained in:
1 parent
e55596b276
commit
e9d8cac926
101 files changed
+5787
-7148
No files matched your search
+18
-36
@@ -12,8 +12,6 @@ constexpr float FLASH_CROSS = 0.5f;
|
||||
constexpr float FLASH_LENGTH = 16;
|
||||
constexpr float FLASH_LIFE = 0.05f;
|
||||
constexpr float FLASH_WIDTH = 6;
|
||||
constexpr float RING_LIFE = 0.3f;
|
||||
constexpr int RING_WIDTH = 2;
|
||||
constexpr float AFTERIMAGE_LIFE = 0.12f;
|
||||
constexpr float NUMBER_GAP = 8;
|
||||
constexpr float POPUP_FADE = 0.4f;
|
||||
@@ -57,10 +55,10 @@ void Effects::burst(Burst burst, Vec2 position, float angle, Color color)
|
||||
}
|
||||
|
||||
// Callers may ask every tick. Copies are only kept every few ticks so trails stay spaced out.
|
||||
void Effects::afterimage_spawn(Pose pose)
|
||||
void Effects::afterimage_spawn(Pose pose, int worn)
|
||||
{
|
||||
if (ticks % AFTERIMAGE_INTERVAL == 0)
|
||||
effect_add(afterimages, Afterimage{pose, AFTERIMAGE_LIFE});
|
||||
effect_add(afterimages, Afterimage{pose, AFTERIMAGE_LIFE, worn});
|
||||
}
|
||||
|
||||
// Drawn before the player and attacks so trails sit behind what made them.
|
||||
@@ -71,6 +69,7 @@ void Effects::afterimage_draw(Game& game)
|
||||
Pose pose = afterimage.pose;
|
||||
pose.placement.color = color_fade(pose.placement.color, AFTERIMAGE_ALPHA * afterimage.life / AFTERIMAGE_LIFE);
|
||||
pose.draw(game.renderer);
|
||||
game.sprites.fashion_draw(game.renderer, pose, afterimage.worn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +121,21 @@ void Effects::flash_spawn(Vec2 position, float angle, float size, Color color)
|
||||
effect_add(flashes, Flash{color, position, angle, FLASH_LIFE, size});
|
||||
}
|
||||
|
||||
void Effects::ring_spawn(Vec2 position, float radius, Color color)
|
||||
// A particle at position, shrinking and fading as its life runs out: an outline in its colour around a core shaded by
|
||||
// its fill. The outline is four strips so the fading core never shows it through.
|
||||
void particle_draw(Renderer& renderer, Particle& particle, Vec2 position)
|
||||
{
|
||||
effect_add(rings, Ring{color, position, RING_LIFE, radius});
|
||||
float fade = particle.life / particle.lifeMax;
|
||||
float size = std::ceil(particle.size * fade);
|
||||
float outer = size + PARTICLE_OUTLINE * 2;
|
||||
Vec2 corner = {std::round(position.x - size / 2), std::round(position.y - size / 2)};
|
||||
Vec2 edge = corner - Vec2{PARTICLE_OUTLINE, PARTICLE_OUTLINE};
|
||||
Color color = color_fade(particle.color, fade);
|
||||
renderer.rect_draw({edge.x, edge.y, outer, PARTICLE_OUTLINE}, color);
|
||||
renderer.rect_draw({edge.x, corner.y + size, outer, PARTICLE_OUTLINE}, color);
|
||||
renderer.rect_draw({edge.x, corner.y, PARTICLE_OUTLINE, size}, color);
|
||||
renderer.rect_draw({corner.x + size, corner.y, PARTICLE_OUTLINE, size}, color);
|
||||
renderer.rect_draw({corner.x, corner.y, size, size}, color_fade(color_shade(particle.color, particle.fill), fade));
|
||||
}
|
||||
|
||||
void Effects::dps_record(int amount)
|
||||
@@ -164,10 +175,6 @@ void Effects::update()
|
||||
flash.life -= TICK;
|
||||
std::erase_if(flashes, [](Flash& flash) { return flash.life <= 0; });
|
||||
|
||||
for (Ring& ring : rings)
|
||||
ring.life -= TICK;
|
||||
std::erase_if(rings, [](Ring& ring) { return ring.life <= 0; });
|
||||
|
||||
for (Afterimage& afterimage : afterimages)
|
||||
afterimage.life -= TICK;
|
||||
std::erase_if(afterimages, [](Afterimage& afterimage) { return afterimage.life <= 0; });
|
||||
@@ -209,32 +216,8 @@ void Effects::draw(Game& game)
|
||||
renderer.line_draw(flash.position - across, flash.position + across, width * FLASH_CROSS, flash.color);
|
||||
}
|
||||
|
||||
// Each ring eases out to its full radius as it fades.
|
||||
for (Ring& ring : rings)
|
||||
{
|
||||
float fade = ring.life / RING_LIFE;
|
||||
float radius = ring.radius * (1 - fade * fade);
|
||||
for (int i = 0; i < RING_WIDTH; i++)
|
||||
renderer.ring_draw(ring.position, std::max(1.0f, radius - i), color_fade(ring.color, fade));
|
||||
}
|
||||
|
||||
// Each particle shrinks and fades out. The outline is four strips so the fading core never shows it through.
|
||||
for (Particle& particle : particles)
|
||||
{
|
||||
float fade = particle.life / particle.lifeMax;
|
||||
float size = std::ceil(particle.size * fade);
|
||||
float outer = size + PARTICLE_OUTLINE * 2;
|
||||
Vec2 position = particle.position - particle.velocity * behind;
|
||||
Vec2 corner = {std::round(position.x - size / 2), std::round(position.y - size / 2)};
|
||||
Vec2 edge = corner - Vec2{PARTICLE_OUTLINE, PARTICLE_OUTLINE};
|
||||
Color color = color_fade(particle.color, fade);
|
||||
renderer.rect_draw({edge.x, edge.y, outer, PARTICLE_OUTLINE}, color);
|
||||
renderer.rect_draw({edge.x, corner.y + size, outer, PARTICLE_OUTLINE}, color);
|
||||
renderer.rect_draw({edge.x, corner.y, PARTICLE_OUTLINE, size}, color);
|
||||
renderer.rect_draw({corner.x + size, corner.y, PARTICLE_OUTLINE, size}, color);
|
||||
renderer.rect_draw({corner.x, corner.y, size, size},
|
||||
color_fade(color_shade(particle.color, particle.fill), fade));
|
||||
}
|
||||
particle_draw(renderer, particle, particle.position - particle.velocity * behind);
|
||||
|
||||
for (Popup& popup : popups)
|
||||
{
|
||||
@@ -258,6 +241,5 @@ void Effects::clear()
|
||||
hits.clear();
|
||||
particles.clear();
|
||||
popups.clear();
|
||||
rings.clear();
|
||||
shake = 0;
|
||||
}
|
||||
Reference in new issue
Block a user