combat: rewrite in Zig; add basic damage types and weapon archetypes
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ic8055a1cf6bdad1aca13673ea171b4b46a6a6964
This commit is contained in:
parent
7af642612b
commit
22ab6fc6eb
15 changed files with 802 additions and 158 deletions
185
libs/combat/attack.zig
Normal file
185
libs/combat/attack.zig
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
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 {
|
||||
if (resistance >= 100) return 0;
|
||||
const factor = 100 - resistance;
|
||||
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;
|
||||
|
||||
const variance = rng(80, 120);
|
||||
var damage = @divTrunc(base_attack * variance, 100);
|
||||
if (damage < 1) damage = 1;
|
||||
|
||||
if (rng(0, 99) < crit_chance) {
|
||||
damage = @divTrunc(damage * crit_mult, 100);
|
||||
event.last.is_critical = 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;
|
||||
|
||||
const variance = rng(80, 120);
|
||||
var damage = @divTrunc(base_damage * variance, 100);
|
||||
if (damage < 1) damage = 1;
|
||||
|
||||
if (rng(0, 99) < c.ENEMY_CRIT_CHANCE) {
|
||||
damage = @divTrunc(damage * c.ENEMY_CRIT_MULT, 100);
|
||||
event.last.is_critical = 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";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue