1
0
Fork 0
forked from NotAShelf/rogged
rogged/libs/combat/effects.zig
NotAShelf 22ab6fc6eb
combat: rewrite in Zig; add basic damage types and weapon archetypes
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ic8055a1cf6bdad1aca13673ea171b4b46a6a6964
2026-04-05 20:29:12 +03:00

151 lines
4.1 KiB
Zig

const c = @import("c.zig");
pub const EffectParams = struct {
duration: c_int,
intensity: c_int,
};
pub fn procForClass(dmg_class: c.DamageClass) c.StatusEffectType {
return switch (dmg_class) {
c.DMG_SLASH => c.EFFECT_BLEED,
c.DMG_IMPACT => c.EFFECT_STUN,
c.DMG_PIERCE => c.EFFECT_WEAKEN,
c.DMG_FIRE => c.EFFECT_BURN,
c.DMG_POISON => c.EFFECT_POISON,
else => c.EFFECT_NONE,
};
}
pub fn name(effect: c.StatusEffectType) ?[*:0]const u8 {
return switch (effect) {
c.EFFECT_POISON => "Poisoned",
c.EFFECT_BLEED => "Bleeding",
c.EFFECT_STUN => "Stunned",
c.EFFECT_BURN => "Burning",
c.EFFECT_WEAKEN => "Weakened",
else => null,
};
}
pub fn paramsFor(effect: c.StatusEffectType) EffectParams {
return switch (effect) {
c.EFFECT_BLEED => .{ .duration = 4, .intensity = c.BLEED_STACK_DAMAGE },
c.EFFECT_STUN => .{ .duration = 1, .intensity = 0 },
c.EFFECT_WEAKEN => .{ .duration = 3, .intensity = c.WEAKEN_ATTACK_REDUCTION },
c.EFFECT_BURN => .{ .duration = 2, .intensity = c.BURN_BASE_DAMAGE },
c.EFFECT_POISON => .{ .duration = 5, .intensity = c.POISON_BASE_DAMAGE },
else => .{ .duration = 0, .intensity = 0 },
};
}
pub fn clampCount(count: c_int) usize {
if (count < 0) return 0;
if (count > c.MAX_EFFECTS) return @intCast(c.MAX_EFFECTS);
return @intCast(count);
}
pub fn has(effects: [*c]const c.StatusEffect, count: c_int, effect_type: c.StatusEffectType) bool {
const safe_count = clampCount(count);
for (0..safe_count) |i| {
if (effects[i].type == effect_type and effects[i].duration > 0)
return true;
}
return false;
}
pub fn apply(
effects: [*c]c.StatusEffect,
count: [*c]c_int,
effect_type: c.StatusEffectType,
duration: c_int,
intensity: c_int,
) void {
const safe_count = clampCount(count[0]);
for (0..safe_count) |i| {
if (effects[i].type == effect_type and effects[i].duration > 0) {
if (effect_type == c.EFFECT_BLEED) {
effects[i].intensity += intensity;
if (effects[i].duration < duration)
effects[i].duration = duration;
} else {
effects[i].duration = duration;
if (intensity > effects[i].intensity)
effects[i].intensity = intensity;
}
return;
}
}
if (safe_count < @as(usize, @intCast(c.MAX_EFFECTS))) {
effects[safe_count] = .{
.type = effect_type,
.duration = duration,
.intensity = intensity,
};
count[0] = @intCast(safe_count + 1);
}
}
fn compact(effects: [*c]c.StatusEffect, count: [*c]c_int) void {
var write: usize = 0;
const safe_count = clampCount(count[0]);
for (0..safe_count) |read| {
if (effects[read].duration > 0) {
if (write != read)
effects[write] = effects[read];
write += 1;
}
}
count[0] = @intCast(write);
}
fn tickOne(eff: *c.StatusEffect, hp: *c_int) c_int {
if (eff.duration <= 0) return 0;
var dmg: c_int = 0;
switch (eff.type) {
c.EFFECT_POISON, c.EFFECT_BLEED, c.EFFECT_BURN => {
dmg = eff.intensity;
hp.* -= dmg;
},
else => {},
}
eff.duration -= 1;
return dmg;
}
pub fn tickPlayer(p: [*c]c.Player) c_int {
if (p == null) return 0;
var total: c_int = 0;
const safe_count = clampCount(p[0].effect_count);
for (0..safe_count) |i| {
total += tickOne(&p[0].effects[i], &p[0].hp);
}
compact(&p[0].effects, &p[0].effect_count);
return total;
}
pub fn tickEnemy(e: [*c]c.Enemy) c_int {
if (e == null) return 0;
if (e[0].alive == 0) return 0;
var total: c_int = 0;
const safe_count = clampCount(e[0].effect_count);
for (0..safe_count) |i| {
total += tickOne(&e[0].effects[i], &e[0].hp);
}
if (e[0].hp <= 0) {
e[0].hp = 0;
e[0].alive = 0;
}
compact(&e[0].effects, &e[0].effect_count);
return total;
}