forked from NotAShelf/rogged
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I983d5980f8d14ccebc6b681100af8a146a6a6964
192 lines
5.9 KiB
Zig
192 lines
5.9 KiB
Zig
const c = @import("c.zig");
|
|
const event = @import("event.zig");
|
|
const effects = @import("effects.zig");
|
|
|
|
fn rng(min: c_int, max: c_int) c_int {
|
|
return c.rng_int(min, max);
|
|
}
|
|
|
|
fn applyResistance(damage: c_int, resistance: c_int) c_int {
|
|
var r = resistance;
|
|
if (r > 75) r = 75;
|
|
const factor = 100 - r;
|
|
var result = @divTrunc(damage * factor, 100);
|
|
if (result < 1) result = 1;
|
|
return result;
|
|
}
|
|
|
|
fn rollProc(
|
|
target_effects: [*c]c.StatusEffect,
|
|
target_count: [*c]c_int,
|
|
dmg_class: c.DamageClass,
|
|
status_chance: c_int,
|
|
) c.StatusEffectType {
|
|
if (status_chance <= 0 or rng(0, 99) >= status_chance)
|
|
return c.EFFECT_NONE;
|
|
|
|
const eff_type = effects.procForClass(dmg_class);
|
|
if (eff_type == c.EFFECT_NONE)
|
|
return c.EFFECT_NONE;
|
|
|
|
const params = effects.paramsFor(eff_type);
|
|
effects.apply(target_effects, target_count, eff_type, params.duration, params.intensity);
|
|
return eff_type;
|
|
}
|
|
|
|
pub fn playerAttack(p: [*c]c.Player, e: [*c]c.Enemy) void {
|
|
if (p == null or e == null) return;
|
|
if (e[0].alive == 0) return;
|
|
|
|
event.reset();
|
|
|
|
if (e[0].dodge > 0 and rng(0, 99) < e[0].dodge) {
|
|
event.last.is_player_damage = 0;
|
|
event.last.was_dodged = 1;
|
|
event.last.message = "Enemy dodged!";
|
|
return;
|
|
}
|
|
|
|
// Read weapon stats or unarmed defaults
|
|
var dmg_class: c.DamageClass = c.DMG_IMPACT;
|
|
var crit_chance: c_int = c.UNARMED_CRIT_CHANCE;
|
|
var crit_mult: c_int = c.UNARMED_CRIT_MULT;
|
|
var status_chance: c_int = c.UNARMED_STATUS_CHANCE;
|
|
|
|
if (p[0].has_weapon != 0) {
|
|
dmg_class = p[0].equipped_weapon.dmg_class;
|
|
crit_chance = p[0].equipped_weapon.crit_chance;
|
|
crit_mult = p[0].equipped_weapon.crit_multiplier;
|
|
status_chance = p[0].equipped_weapon.status_chance;
|
|
}
|
|
|
|
var base_attack = p[0].attack;
|
|
if (effects.has(&p[0].effects, p[0].effect_count, c.EFFECT_WEAKEN))
|
|
base_attack -= c.WEAKEN_ATTACK_REDUCTION;
|
|
if (base_attack < 1) base_attack = 1;
|
|
|
|
var damage = base_attack;
|
|
|
|
if (rng(0, 99) < crit_chance) {
|
|
damage = @divTrunc(damage * crit_mult, 100);
|
|
event.last.is_critical = 1;
|
|
}
|
|
|
|
const variance = rng(80, 120);
|
|
damage = @divTrunc(damage * variance, 100);
|
|
if (damage < 1) damage = 1;
|
|
|
|
const res_index: usize = @intCast(dmg_class);
|
|
if (res_index < c.NUM_DMG_CLASSES) {
|
|
damage = applyResistance(damage, e[0].resistance[res_index]);
|
|
}
|
|
|
|
if (damage == 0) {
|
|
event.last.damage = 0;
|
|
event.last.is_player_damage = 0;
|
|
event.last.message = "No effect!";
|
|
return;
|
|
}
|
|
|
|
if (e[0].block > 0 and rng(0, 99) < 30) {
|
|
var blocked = e[0].block;
|
|
if (blocked > damage) blocked = damage;
|
|
damage -= blocked;
|
|
if (damage < 1) damage = 1;
|
|
event.last.was_blocked = 1;
|
|
event.last.block_amount = blocked;
|
|
}
|
|
|
|
e[0].hp -= damage;
|
|
event.last.damage = damage;
|
|
event.last.is_player_damage = 0;
|
|
|
|
const applied = rollProc(&e[0].effects, &e[0].effect_count, dmg_class, status_chance);
|
|
event.last.applied_effect = applied;
|
|
|
|
if (e[0].hp <= 0) {
|
|
e[0].hp = 0;
|
|
e[0].alive = 0;
|
|
event.last.message = "Enemy killed!";
|
|
} else if (applied != c.EFFECT_NONE) {
|
|
event.last.message = switch (applied) {
|
|
c.EFFECT_BLEED => "Hit! Bleeding!",
|
|
c.EFFECT_STUN => "Hit! Stunned!",
|
|
c.EFFECT_WEAKEN => "Hit! Weakened!",
|
|
c.EFFECT_BURN => "Hit! Burning!",
|
|
c.EFFECT_POISON => "Hit! Poisoned!",
|
|
else => "You hit",
|
|
};
|
|
} else if (event.last.is_critical != 0) {
|
|
event.last.message = "Critical hit!";
|
|
} else {
|
|
event.last.message = "You hit";
|
|
}
|
|
}
|
|
|
|
pub fn enemyAttack(e: [*c]c.Enemy, p: [*c]c.Player) void {
|
|
if (e == null or p == null) return;
|
|
if (e[0].alive == 0) return;
|
|
|
|
event.reset();
|
|
event.last.is_player_damage = 1;
|
|
|
|
if (p[0].dodge > 0 and rng(0, 99) < p[0].dodge) {
|
|
event.last.was_dodged = 1;
|
|
event.last.message = "You dodged!";
|
|
return;
|
|
}
|
|
|
|
var base_damage = e[0].attack;
|
|
if (effects.has(&e[0].effects, e[0].effect_count, c.EFFECT_WEAKEN))
|
|
base_damage -= c.WEAKEN_ATTACK_REDUCTION;
|
|
base_damage -= p[0].defense;
|
|
if (base_damage < 1) base_damage = 1;
|
|
|
|
var damage = base_damage;
|
|
|
|
const e_crit_chance = if (e[0].crit_chance > 0) e[0].crit_chance else c.ENEMY_CRIT_CHANCE;
|
|
const e_crit_mult = if (e[0].crit_mult > 0) e[0].crit_mult else c.ENEMY_CRIT_MULT;
|
|
if (rng(0, 99) < e_crit_chance) {
|
|
damage = @divTrunc(damage * e_crit_mult, 100);
|
|
event.last.is_critical = 1;
|
|
}
|
|
|
|
const variance = rng(80, 120);
|
|
damage = @divTrunc(damage * variance, 100);
|
|
if (damage < 1) damage = 1;
|
|
|
|
if (p[0].block > 0 and rng(0, 99) < 30) {
|
|
var blocked = p[0].block;
|
|
if (blocked > damage) blocked = damage;
|
|
damage -= blocked;
|
|
if (damage < 1) damage = 1;
|
|
event.last.was_blocked = 1;
|
|
event.last.block_amount = blocked;
|
|
}
|
|
|
|
p[0].hp -= damage;
|
|
event.last.damage = damage;
|
|
|
|
const applied = rollProc(&p[0].effects, &p[0].effect_count, e[0].dmg_class, e[0].status_chance);
|
|
event.last.applied_effect = applied;
|
|
|
|
if (p[0].hp <= 0) {
|
|
p[0].hp = 0;
|
|
event.last.message = "You died!";
|
|
} else if (applied != c.EFFECT_NONE) {
|
|
event.last.message = switch (applied) {
|
|
c.EFFECT_POISON => "Hit! Poisoned!",
|
|
c.EFFECT_BLEED => "Hit! Bleeding!",
|
|
c.EFFECT_STUN => "Hit! Stunned!",
|
|
c.EFFECT_BURN => "Hit! Burning!",
|
|
c.EFFECT_WEAKEN => "Hit! Weakened!",
|
|
else => "Hit",
|
|
};
|
|
} else if (event.last.was_blocked != 0) {
|
|
event.last.message = "Blocked some damage";
|
|
} else if (event.last.is_critical != 0) {
|
|
event.last.message = "Critical!";
|
|
} else {
|
|
event.last.message = "Hit";
|
|
}
|
|
}
|