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
+103
-77
@@ -30,13 +30,13 @@ constexpr float JUMP_RELEASE_SPEED = 160;
|
||||
constexpr float KICK_COOLDOWN = 1.2f;
|
||||
constexpr float LAUNCH_DRAG = 160;
|
||||
constexpr float LAND_SPEED = 150;
|
||||
// The most charge a normal attack can carry, just short of a full charge's charged attack.
|
||||
constexpr float CHARGE_NORMAL_MAX = 0.999f;
|
||||
constexpr float MAX_FALL = 520;
|
||||
constexpr float ROPE_MIN = 24;
|
||||
constexpr float SLIDE_DUST_INTERVAL = 0.08f;
|
||||
constexpr float SWING_ACCELERATION = 500;
|
||||
constexpr float SWING_SPEED_MAX = 420;
|
||||
constexpr float POUND_AREAS[LEVEL_COUNT] = {15, 20, 25};
|
||||
constexpr float POUND_DAMAGES[LEVEL_COUNT] = {20, 35, 50};
|
||||
constexpr float POUND_SCALE_MAX = 3;
|
||||
constexpr float POUND_SPEED = 700;
|
||||
constexpr Vec2 HAND_OFFSET = {4, 2};
|
||||
@@ -47,7 +47,9 @@ constexpr float WALL_LIFT = 0.25f;
|
||||
constexpr float WALL_MEMORY = 0.6f;
|
||||
constexpr float WALL_REFLECT = 1.2f;
|
||||
constexpr float WALL_POINT_HEIGHT = 0.4f;
|
||||
constexpr Vec2 KICK_VELOCITY = {220, -330};
|
||||
constexpr Vec2 KICK_VELOCITY = {380, -260};
|
||||
constexpr float KICK_STACK_BOOST = 0.25f;
|
||||
constexpr float HITSTOP_RUN_MAX = 1;
|
||||
constexpr Vec2 WALL_JUMP_VELOCITY = {180, -JUMP_SPEED};
|
||||
constexpr float CROUCH_HEIGHT = 0.75f;
|
||||
constexpr float CROUCH_DAMP = 0.5f;
|
||||
@@ -108,6 +110,7 @@ void Fighter::position_set(Sim& sim, Vec2 spawn)
|
||||
safe = spawn;
|
||||
echoes.clear();
|
||||
hitstop = 0;
|
||||
hitstopRun = 0;
|
||||
isLaunched = false;
|
||||
}
|
||||
|
||||
@@ -133,12 +136,12 @@ void Fighter::damage_take(Sim& sim, Damage damage)
|
||||
bool isRacing = sim.match.is_racing();
|
||||
health -= isRacing || sim.match.state == MATCH_PRACTICE ? 0 : damage.amount;
|
||||
flash = 1;
|
||||
hitstop = std::max(hitstop, damage.hitstop);
|
||||
hitstop_add(damage.hitstop);
|
||||
|
||||
Vec2 away = center_get() - damage.source;
|
||||
Vec2 direction = vec2_normalize(vec2_normalize(vec2_length(away) > 1 ? away : Vec2{0, -1}) + Vec2{0, -KNOCK_LIFT});
|
||||
float knock = std::min(KNOCK_MAX, KNOCK_BASE + damage.amount * KNOCK_PER_DAMAGE) * damage.knock *
|
||||
(isRacing ? RACE_KNOCK : 1) * knock_absorb_get();
|
||||
(isRacing ? RACE_KNOCK : 1) * stat_get(STAT_KNOCKBACK_TAKEN);
|
||||
knock_take(direction * knock, damage.owner);
|
||||
if (health > 0)
|
||||
return;
|
||||
@@ -155,19 +158,24 @@ void Fighter::damage_take(Sim& sim, Damage damage)
|
||||
void Fighter::update(Sim& sim)
|
||||
{
|
||||
// Out of the round, it still keeps track of its buttons, so a held one (like Ready on the results) counts once.
|
||||
// Its hitstop runs out too, or the attacks it left out would stay frozen with it.
|
||||
if (!isAlive)
|
||||
{
|
||||
hitstop = std::max(0.0f, hitstop - TICK);
|
||||
buttonsPrevious = controls.buttons;
|
||||
return;
|
||||
}
|
||||
cornerPrevious = {body.box.x, body.box.y};
|
||||
flash = std::max(0.0f, flash - FLASH_DECAY * TICK);
|
||||
shockCooldown -= TICK;
|
||||
donkWindow -= TICK;
|
||||
if (hitstop > 0)
|
||||
{
|
||||
hitstop -= TICK;
|
||||
hitstopRun += TICK;
|
||||
return;
|
||||
}
|
||||
hitstopRun = 0;
|
||||
|
||||
for (float& cooldown : cooldowns)
|
||||
cooldown = std::max(0.0f, cooldown - TICK);
|
||||
@@ -248,8 +256,8 @@ void Fighter::update(Sim& sim)
|
||||
{
|
||||
float impact = std::min(fallSpeed / POUND_SPEED, POUND_SCALE_MAX);
|
||||
sim.weapons.blast_spawn(sim, {.position = feet,
|
||||
.area = trinket_sum_get(TRINKET_WEIGHTS, POUND_AREAS) * std::sqrt(impact),
|
||||
.damage = trinket_sum_get(TRINKET_WEIGHTS, POUND_DAMAGES) * impact,
|
||||
.area = stat_get(STAT_SLAM_AREA) * std::sqrt(impact),
|
||||
.damage = stat_get(STAT_SLAM_DAMAGE) * impact,
|
||||
.owner = index,
|
||||
.sound = SOUND_POUND});
|
||||
}
|
||||
@@ -309,8 +317,8 @@ void Fighter::run_update(Attack* hook)
|
||||
float direction = (float)is_down(CONTROL_RIGHT) - (float)is_down(CONTROL_LEFT);
|
||||
float speed = std::abs(body.velocity.x);
|
||||
float sign = body.velocity.x < 0 ? -1 : 1;
|
||||
float boots = trinket_get(TRINKET_BOOTS, 1);
|
||||
float runSpeed = RUN_SPEED * boots * (1 + trinket_get(TRINKET_SPEEDOMETER, 0)) * (isCrouching ? CRAWL_SPEED : 1);
|
||||
float boots = stat_get(STAT_RUN_SPEED);
|
||||
float runSpeed = RUN_SPEED * boots * (isCrouching ? CRAWL_SPEED : 1);
|
||||
if (dashTimer > 0)
|
||||
return;
|
||||
if (isLaunched)
|
||||
@@ -323,9 +331,9 @@ void Fighter::run_update(Attack* hook)
|
||||
bool isAirborne = !body.isGrounded && !hook;
|
||||
bool isFastFalling = isAirborne && is_down(CONTROL_DOWN) && !isSliding;
|
||||
bool isFloating = isAirborne && is_down(CONTROL_UP);
|
||||
float weights = isFastFalling ? trinket_get(TRINKET_WEIGHTS, 1) : 1;
|
||||
float weights = isFastFalling ? stat_get(STAT_FAST_FALL) : 1;
|
||||
float gravity = GRAVITY * (isFastFalling ? FAST_FALL_GRAVITY * weights : 1);
|
||||
gravity *= isFloating ? trinket_get(TRINKET_FEATHER, 1) : 1;
|
||||
gravity *= isFloating ? stat_get(STAT_FLOAT_GRAVITY) : 1;
|
||||
gravity *= isSpinning ? SPIN_GRAVITY : 1;
|
||||
float maxFall = isFastFalling ? FAST_FALL_SPEED * weights : MAX_FALL;
|
||||
if (body.velocity.y > maxFall)
|
||||
@@ -382,7 +390,7 @@ void Fighter::rope_update(Attack& hook)
|
||||
float reel = (float)is_down(CONTROL_DOWN) - (float)is_down(CONTROL_UP);
|
||||
reel *= WEAPON_DEFS[hook.weapon].levels[hook.key >= 0 ? slot_level_get(hook.key) : 0].reel;
|
||||
if (hook.isPulling)
|
||||
reel = -hook.stats.speed * trinket_get(TRINKET_ABSORBER, 1);
|
||||
reel = -hook.stats.speed * stat_get(STAT_MOVEMENT);
|
||||
hook.length = std::clamp(hook.length + reel * TICK, ROPE_MIN, hook.stats.range);
|
||||
hook.isPulling &= hook.length > ROPE_MIN;
|
||||
|
||||
@@ -418,7 +426,7 @@ void Fighter::slot_update(Sim& sim)
|
||||
// Shrinker, Embiggener and crouching: resizes the box around the feet to the fighter's current size.
|
||||
void Fighter::size_update()
|
||||
{
|
||||
scale = trinket_get(TRINKET_SHRINKER, 1) * trinket_get(TRINKET_EMBIGGENER, 1);
|
||||
scale = stat_get(STAT_SIZE);
|
||||
float height = BOX_HEIGHT * scale * (isCrouching || isSliding ? CROUCH_HEIGHT : 1);
|
||||
Vec2 feet = {center_get().x, body.box.y + body.box.h};
|
||||
body.box = {feet.x - BOX_WIDTH * scale / 2, feet.y - height, BOX_WIDTH * scale, height};
|
||||
@@ -430,7 +438,7 @@ void Fighter::size_update()
|
||||
// works while there's a dash's worth left to build up before all of them are spent.
|
||||
void Fighter::dash_update(Sim& sim, bool isJumpPressed)
|
||||
{
|
||||
float speed = trinket_best_get(TRINKET_WINGS, 0);
|
||||
float speed = stat_get(STAT_DASH_SPEED);
|
||||
dashTimer -= TICK;
|
||||
dashCooldown = std::max(0.0f, dashCooldown - TICK);
|
||||
|
||||
@@ -450,7 +458,7 @@ void Fighter::dash_update(Sim& sim, bool isJumpPressed)
|
||||
// off, on a cooldown so it can't climb forever. 3: Jump wall jumps freely.
|
||||
void Fighter::climb_update(Sim& sim)
|
||||
{
|
||||
int level = trinket_get(TRINKET_CLIMBING, 0);
|
||||
int level = stat_get(STAT_CLIMB);
|
||||
int wall = wall_get(sim);
|
||||
kickTimer -= TICK;
|
||||
slideWall = 0;
|
||||
@@ -469,9 +477,10 @@ void Fighter::climb_update(Sim& sim)
|
||||
}
|
||||
|
||||
Control away = wall > 0 ? CONTROL_LEFT : CONTROL_RIGHT;
|
||||
// Each copy past the first kicks off a quarter harder.
|
||||
if (level >= 2 && kickTimer <= 0 && is_pressed(away))
|
||||
{
|
||||
wall_push(sim, wall, KICK_VELOCITY);
|
||||
wall_push(sim, wall, KICK_VELOCITY * (1 + KICK_STACK_BOOST * (trinket_count(TRINKET_CLIMBING) - 1)));
|
||||
kickTimer = KICK_COOLDOWN;
|
||||
isJumping = false;
|
||||
}
|
||||
@@ -600,9 +609,9 @@ void Fighter::charge_update(Sim& sim)
|
||||
bool isLeftPressed = is_pressed(CONTROL_ATTACK);
|
||||
bool isRightPressed = is_pressed(CONTROL_CHARGE);
|
||||
if (is_released(CONTROL_ATTACK))
|
||||
sim.weapons.wisps_release(*this);
|
||||
sim.weapons.orbits_release(*this);
|
||||
|
||||
// A latched grapple lets go when clicked with the grapple in hand; another weapon fires as it hangs on.
|
||||
// A latched grapple lets go when clicked with the grapple in hand. Another weapon fires as it hangs on.
|
||||
if (hook && hook->key == weapon_key(slot) && (isLeftPressed || isRightPressed))
|
||||
{
|
||||
sim.weapons.attacks_release(*this, ATTACK_HOOK);
|
||||
@@ -615,8 +624,28 @@ void Fighter::charge_update(Sim& sim)
|
||||
return;
|
||||
}
|
||||
|
||||
// A full charge let go of during a cooldown goes first, the moment the weapon in hand is ready (see below).
|
||||
// Attacking while charging spends the charge on the normal attack (as strong as the charge built, short of the
|
||||
// charged one) once it's ready. A weapon whose charge just strengthens its shot lets it go as if released instead,
|
||||
// and a Mine's detonator leaves the charge (the next mine) be.
|
||||
int key = weapon_key(slot);
|
||||
int weapon = weapon_get();
|
||||
bool isDetonator = weapon >= 0 && WEAPON_DEFS[weapon].normal.kind == ATTACK_DETONATE;
|
||||
bool isReady = key < 0 || cooldowns[key] <= 0;
|
||||
if (isChargeQueued && isReady)
|
||||
{
|
||||
sim.weapons.fire(sim, *this, 1);
|
||||
isChargeQueued = false;
|
||||
isReady = false;
|
||||
}
|
||||
isFireBlocked &= is_down(CONTROL_ATTACK);
|
||||
if (is_down(CONTROL_ATTACK) && !isFireBlocked)
|
||||
if (is_down(CONTROL_ATTACK) && !isFireBlocked && isCharging && isReady && !isDetonator)
|
||||
{
|
||||
bool isScaled = weapon >= 0 && WEAPON_DEFS[weapon].isChargeScaled;
|
||||
sim.weapons.fire(sim, *this, isScaled ? charge : std::min(charge, CHARGE_NORMAL_MAX));
|
||||
charge_cancel(sim);
|
||||
}
|
||||
else if (is_down(CONTROL_ATTACK) && !isFireBlocked)
|
||||
sim.weapons.fire(sim, *this, 0);
|
||||
|
||||
if (isRightPressed && weapon_get() >= 0)
|
||||
@@ -625,7 +654,7 @@ void Fighter::charge_update(Sim& sim)
|
||||
if (isCharging && is_down(CONTROL_CHARGE) && charge < 1)
|
||||
{
|
||||
float previous = charge;
|
||||
charge = std::min(1.0f, charge + TICK / CHARGE_TIME * trinket_get(TRINKET_DRINK, 1));
|
||||
charge = std::min(1.0f, charge + TICK / CHARGE_TIME * stat_get(STAT_CHARGE_RATE));
|
||||
if (previous < CHARGE_VISIBLE && charge >= CHARGE_VISIBLE)
|
||||
sim.cues->charge_play(*this);
|
||||
if (charge >= 1)
|
||||
@@ -636,29 +665,44 @@ void Fighter::charge_update(Sim& sim)
|
||||
}
|
||||
}
|
||||
|
||||
// Let go, the charge drops back to nothing and fires. A full charge let go of while the weapon in hand is cooling
|
||||
// down waits, going the moment it's ready (see above).
|
||||
if (isCharging && is_released(CONTROL_CHARGE))
|
||||
{
|
||||
sim.weapons.fire(sim, *this, charge);
|
||||
sim.cues->charge_stop(*this);
|
||||
bool isCooling = key >= 0 && cooldowns[key] > 0;
|
||||
if (charge >= 1 && isCooling)
|
||||
isChargeQueued = true;
|
||||
else
|
||||
sim.weapons.fire(sim, *this, charge);
|
||||
charge = 0;
|
||||
isCharging = false;
|
||||
}
|
||||
}
|
||||
|
||||
// How much of a trinket's own cooldown is left, from 1 just used to 0 ready. Wings dashes and Climb Claw kicks
|
||||
// have one, and every other trinket is always 0.
|
||||
float Fighter::trinket_cooldown_get(int trinket)
|
||||
// have one, and every other trinket is always 0. Each Wings copy holds a dash of its own, the last copy's spent first
|
||||
// and refilled first, and with no copy given it's whether the next dash is ready.
|
||||
float Fighter::trinket_cooldown_get(int trinket, int copy)
|
||||
{
|
||||
if (trinket == TRINKET_WINGS)
|
||||
{
|
||||
int spare = std::max(0, trinket_count(TRINKET_WINGS) - 1);
|
||||
return std::clamp((dashCooldown - DASH_COOLDOWN * spare) / DASH_COOLDOWN, 0.0f, 1.0f);
|
||||
copy = copy >= 0 ? copy : std::max(0, trinket_count(TRINKET_WINGS) - 1);
|
||||
return std::clamp((dashCooldown - DASH_COOLDOWN * copy) / DASH_COOLDOWN, 0.0f, 1.0f);
|
||||
}
|
||||
if (copy > 0)
|
||||
return 0;
|
||||
if (trinket == TRINKET_CLIMBING)
|
||||
return std::max(0.0f, kickTimer) / KICK_COOLDOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Freezes it in hitstop for at least amount, though never for more than a second in a row however much it's hit.
|
||||
void Fighter::hitstop_add(float amount)
|
||||
{
|
||||
hitstop = std::max(hitstop, std::min(amount, HITSTOP_RUN_MAX - hitstopRun));
|
||||
}
|
||||
|
||||
// The radius an Iron Weights slam would blast if the fighter landed at its current speed, or 0 if it isn't falling
|
||||
// fast enough to slam at all.
|
||||
float Fighter::pound_area_get()
|
||||
@@ -666,7 +710,7 @@ float Fighter::pound_area_get()
|
||||
if (!is_trinket_equipped(TRINKET_WEIGHTS) || body.isGrounded || body.velocity.y <= MAX_FALL)
|
||||
return 0;
|
||||
float impact = std::min(body.velocity.y / POUND_SPEED, POUND_SCALE_MAX);
|
||||
return trinket_sum_get(TRINKET_WEIGHTS, POUND_AREAS) * std::sqrt(impact);
|
||||
return stat_get(STAT_SLAM_AREA) * std::sqrt(impact);
|
||||
}
|
||||
|
||||
// Moves the fighter's middle to centre, keeping its momentum, without drawing it sliding across (teleports).
|
||||
@@ -679,9 +723,9 @@ void Fighter::teleport_to(Vec2 centre)
|
||||
|
||||
// An outside push, like a blast, recoil or a lunge. It counts as a launch, so releasing jump won't cut it short.
|
||||
// Crouching halves it (knockback, already halved before it gets here, isn't damped again).
|
||||
void Fighter::impulse_add(Vec2 impulse, bool isDamped)
|
||||
void Fighter::impulse_add(Vec2 impulse)
|
||||
{
|
||||
body.velocity = body.velocity + impulse * (isDamped && isCrouching ? CROUCH_DAMP : 1);
|
||||
body.velocity = body.velocity + impulse * (isCrouching ? CROUCH_DAMP : 1);
|
||||
isJumping = false;
|
||||
}
|
||||
|
||||
@@ -693,7 +737,8 @@ void Fighter::knock_take(Vec2 knock, int owner)
|
||||
knock = knock * (isCrouching ? CROUCH_DAMP : 1);
|
||||
float strength = vec2_length(knock);
|
||||
Vec2 direction = vec2_normalize(knock);
|
||||
impulse_add(direction * std::max(0.0f, strength - vec2_dot(body.velocity, direction)), false);
|
||||
body.velocity = body.velocity + direction * std::max(0.0f, strength - vec2_dot(body.velocity, direction));
|
||||
isJumping = false;
|
||||
launcher = owner;
|
||||
isLaunched |= strength >= LAUNCH_START_SPEED;
|
||||
}
|
||||
@@ -711,6 +756,7 @@ void Fighter::charge_cancel(Sim& sim)
|
||||
sim.cues->charge_stop(*this);
|
||||
charge = 0;
|
||||
isCharging = false;
|
||||
isChargeQueued = false;
|
||||
}
|
||||
|
||||
// The weapon in the selected hotbar slot, or -1 if the slot is empty.
|
||||
@@ -738,12 +784,11 @@ const LevelDef& Fighter::level_get(int weaponSlot)
|
||||
}
|
||||
|
||||
// Puts an item in a slot (or empties it, with -1) at the item's level.
|
||||
void Fighter::item_set(bool isTrinket, int itemSlot, int item)
|
||||
void Fighter::item_set(ItemKind kind, int itemSlot, int item)
|
||||
{
|
||||
(isTrinket ? trinketLoadout : weaponLoadout)[itemSlot] = item;
|
||||
loadout_get(kind)[itemSlot] = item;
|
||||
if (item >= 0)
|
||||
(isTrinket ? trinketSlotLevels : weaponSlotLevels)[itemSlot] =
|
||||
(isTrinket ? trinketItemLevels : weaponItemLevels)[item];
|
||||
slot_levels_get(kind)[itemSlot] = item_levels_get(kind)[item];
|
||||
}
|
||||
|
||||
// Every item and every copy at one level, as a match sets them each round.
|
||||
@@ -755,63 +800,44 @@ void Fighter::levels_fill(int weaponLevel, int trinketLevel)
|
||||
std::fill(std::begin(trinketSlotLevels), std::end(trinketSlotLevels), trinketLevel);
|
||||
}
|
||||
|
||||
// What a trinket does, from its equipped copies, or the neutral value without any. Copies add what each does beyond
|
||||
// neutral (Boots 1.3x and 1.3x make 1.6x, Pebble Helmets' damages add), except that ones taking something away (below
|
||||
// a neutral of 1, like Shrinker or Feather) multiply, so they never pass zero; the trinket's cap holds the total.
|
||||
float Fighter::trinket_get(TrinketId trinket, float neutral)
|
||||
// A trinket stat from the equipped copies (across trinkets, like Shrinker and Embiggener's sizes), or its neutral
|
||||
// value without any. A lone copy counts exactly as it is. Otherwise, by the stat's stacking, the best copy counts, or
|
||||
// what each adds beyond neutral adds up (Boots 1.3x and 1.3x make 1.6x, Pebble Helmets' damages add) while what each
|
||||
// takes away below it multiplies in (so Shrinkers never pass zero). The stat's cap holds the total.
|
||||
float Fighter::stat_get(TrinketStat stat)
|
||||
{
|
||||
const TrinketDef& def = TRINKET_DEFS[trinket];
|
||||
float total = neutral;
|
||||
bool isFirst = true;
|
||||
const TrinketStatDef& def = TRINKET_STATS[stat];
|
||||
float best = 0;
|
||||
float gained = 0;
|
||||
float kept = 1;
|
||||
float only = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < trinketSlots; i++)
|
||||
{
|
||||
if (trinketLoadout[i] != trinket)
|
||||
int trinket = trinketLoadout[i];
|
||||
float value = trinket >= 0 ? TRINKET_DEFS[trinket].levels[trinketSlotLevels[i]].*def.field : 0;
|
||||
if (value == 0)
|
||||
continue;
|
||||
// The first copy counts exactly as it is (adding and taking back the neutral value would round it).
|
||||
float value = def.values[trinketSlotLevels[i]];
|
||||
total = isFirst ? value : neutral != 0 && value < neutral ? total * value / neutral : total + value - neutral;
|
||||
isFirst = false;
|
||||
only = value;
|
||||
count++;
|
||||
best = std::max(best, value);
|
||||
if (value < def.neutral)
|
||||
kept *= value / def.neutral;
|
||||
else
|
||||
gained += value - def.neutral;
|
||||
}
|
||||
float total = count == 0 ? def.neutral
|
||||
: count == 1 ? only
|
||||
: def.stack == STACK_BEST ? best
|
||||
: (def.neutral + gained) * kept;
|
||||
return def.cap > 0 ? std::min(total, def.cap) : total;
|
||||
}
|
||||
|
||||
// The best equipped copy's value, or the neutral value without any.
|
||||
float Fighter::trinket_best_get(TrinketId trinket, float neutral)
|
||||
{
|
||||
int level = trinket_level_get(trinket);
|
||||
return level < 0 ? neutral : TRINKET_DEFS[trinket].values[level];
|
||||
}
|
||||
|
||||
// A per-level number for a trinket (like Iron Weights' slam damage): the copies' added up, or 0 without any.
|
||||
float Fighter::trinket_sum_get(int trinket, const float* table)
|
||||
{
|
||||
float total = 0;
|
||||
for (int i = 0; i < trinketSlots; i++)
|
||||
total += trinketLoadout[i] == trinket ? table[trinketSlotLevels[i]] : 0;
|
||||
return total;
|
||||
}
|
||||
|
||||
// The best equipped copy's level, or -1 without any.
|
||||
int Fighter::trinket_level_get(int trinket)
|
||||
{
|
||||
int level = -1;
|
||||
for (int i = 0; i < trinketSlots; i++)
|
||||
if (trinketLoadout[i] == trinket)
|
||||
level = std::max(level, trinketSlotLevels[i]);
|
||||
return level;
|
||||
}
|
||||
|
||||
int Fighter::trinket_count(int trinket)
|
||||
{
|
||||
return std::count(trinketLoadout, trinketLoadout + trinketSlots, trinket);
|
||||
}
|
||||
|
||||
// What Shock Absorber leaves of knockback taken: part of the way to what it leaves of weapon movement.
|
||||
float Fighter::knock_absorb_get()
|
||||
{
|
||||
return 1 - (1 - trinket_get(TRINKET_ABSORBER, 1)) * ABSORB_KNOCK_SHARE;
|
||||
}
|
||||
|
||||
bool Fighter::is_trinket_equipped(int trinket)
|
||||
{
|
||||
for (int item : trinketLoadout)
|
||||
|
||||
Reference in new issue
Block a user