feat: extend classes with Classes.isValid and Classes.apply
This commit is contained in:
1 parent
a8b7261a84
commit
2cc87fc08c
2 files changed
+101
-8
No files matched your search
@@ -0,0 +1,64 @@
|
|||||||
|
-- Player classes. A class is chosen once at character creation (Game.new
|
||||||
|
-- options.class) and never changes; it grants a one-time stat or kit delta
|
||||||
|
-- and, through skills.lua, gates which skills the player is allowed to spend
|
||||||
|
-- points on. Classless play (no options.class given) is unaffected - every
|
||||||
|
-- class delta is opt-in so existing unclassed games keep their exact stats.
|
||||||
|
|
||||||
|
local Items = require("items")
|
||||||
|
|
||||||
|
local Classes = {}
|
||||||
|
|
||||||
|
Classes.order = { "fighter", "rogue", "mage", "ranger" }
|
||||||
|
|
||||||
|
Classes.defs = {
|
||||||
|
fighter = {
|
||||||
|
name = "fighter",
|
||||||
|
apply = function(player)
|
||||||
|
player.maxHp = player.maxHp + 8
|
||||||
|
player.hp = player.hp + 8
|
||||||
|
player.attack = player.attack + 2
|
||||||
|
player.baseAttack = player.baseAttack + 2
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
rogue = {
|
||||||
|
name = "rogue",
|
||||||
|
apply = function(player)
|
||||||
|
player.gold = player.gold + 15
|
||||||
|
player.attack = player.attack + 1
|
||||||
|
player.baseAttack = player.baseAttack + 1
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
mage = {
|
||||||
|
name = "mage",
|
||||||
|
apply = function(player)
|
||||||
|
player.maxMana = player.maxMana + 8
|
||||||
|
player.mana = player.mana + 8
|
||||||
|
player.attack = player.attack - 1
|
||||||
|
player.baseAttack = player.baseAttack - 1
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
ranger = {
|
||||||
|
name = "ranger",
|
||||||
|
apply = function(player)
|
||||||
|
Items.equipBow(Items.bow("shortbow", 3, 6), player)
|
||||||
|
player.inventory[#player.inventory + 1] = Items.ammo("arrow", 12)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function Classes.isValid(key)
|
||||||
|
return Classes.defs[key] ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Applies the one-time delta for `key` to a freshly created player. Must run
|
||||||
|
-- after the player's base stats, mana pool and starting inventory are all in
|
||||||
|
-- place, since every delta is additive on top of whatever is already there.
|
||||||
|
function Classes.apply(player, key)
|
||||||
|
local def = Classes.defs[key]
|
||||||
|
if not def then return false end
|
||||||
|
player.class = key
|
||||||
|
def.apply(player)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return Classes
|
||||||
+37
-8
@@ -1,24 +1,53 @@
|
|||||||
-- Deterministic combat: damage is effective attack minus effective defense,
|
-- Deterministic combat: damage is effective attack minus effective defense,
|
||||||
-- floored at 1. Effective stats fold in temporary status modifiers.
|
-- floored at 1. Effective stats fold in temporary status modifiers, plus
|
||||||
|
-- conditional skill bonuses (berserk/mana shield - see skills.lua, a no-op
|
||||||
|
-- for any entity without a `skills` table, i.e. monsters) and any completed
|
||||||
|
-- runeword on the wielder's own weapon/armor (see runewords.lua, a no-op for
|
||||||
|
-- any item without a matching rune chain), plus any item-set threshold the
|
||||||
|
-- wielder's current gear happens to meet (see sets.lua, a no-op below its
|
||||||
|
-- lowest 2-piece threshold).
|
||||||
|
-- The attack-minus-defense minimum-1 floor happens before elemental
|
||||||
|
-- resistance (resistances.lua) gets a say, so a defender immune to the
|
||||||
|
-- attack's damage type can still shrug off a hit that combat itself
|
||||||
|
-- guaranteed would land.
|
||||||
|
|
||||||
|
local Skills = require("skills")
|
||||||
|
local Resistances = require("resistances")
|
||||||
|
local Runewords = require("runewords")
|
||||||
|
local Sets = require("sets")
|
||||||
|
|
||||||
local Combat = {}
|
local Combat = {}
|
||||||
|
|
||||||
local function attackOf(entity)
|
local function attackOf(entity)
|
||||||
return entity.effectiveAttack and entity:effectiveAttack() or entity.attack
|
local base = entity.effectiveAttack and entity:effectiveAttack() or entity.attack
|
||||||
|
return base + Skills.attackBonus(entity) + Runewords.attackBonus(entity) + Sets.attackBonus(entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function defenseOf(entity)
|
local function defenseOf(entity)
|
||||||
return entity.effectiveDefense and entity:effectiveDefense() or entity.defense
|
local base = entity.effectiveDefense and entity:effectiveDefense() or entity.defense
|
||||||
|
return base + Skills.defenseBonus(entity) + Runewords.defenseBonus(entity) + Sets.defenseBonus(entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Combat.damage(attacker, defender)
|
-- The type of an attacker's raw damage: a weapon retagged by an elemental
|
||||||
local dealt = attackOf(attacker) - defenseOf(defender)
|
-- affix (flaming/venomed - see affixes.lua) fights with that element instead
|
||||||
|
-- of a bare fist or claw's plain physical.
|
||||||
|
local function damageTypeOf(attacker)
|
||||||
|
return (attacker.weapon and attacker.weapon.damageType) or Resistances.PHYSICAL
|
||||||
|
end
|
||||||
|
|
||||||
|
-- `multiplier` scales the raw attack-minus-defense difference before the
|
||||||
|
-- floor-at-1 minimum is applied, so a backstab still guarantees at least 1
|
||||||
|
-- rather than 2x-ing an already-clamped hit. `damageType` defaults to the
|
||||||
|
-- attacker's own weapon typing when omitted.
|
||||||
|
function Combat.damage(attacker, defender, multiplier, damageType)
|
||||||
|
local dealt = (attackOf(attacker) - defenseOf(defender)) * (multiplier or 1)
|
||||||
if dealt < 1 then dealt = 1 end
|
if dealt < 1 then dealt = 1 end
|
||||||
return dealt
|
dealt = math.floor(dealt)
|
||||||
|
return Resistances.apply(defender, dealt, damageType or damageTypeOf(attacker))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Combat.attack(attacker, defender)
|
function Combat.attack(attacker, defender, multiplier, damageType)
|
||||||
local dealt = Combat.damage(attacker, defender)
|
local dealt = Combat.damage(attacker, defender, multiplier, damageType)
|
||||||
defender.hp = defender.hp - dealt
|
defender.hp = defender.hp - dealt
|
||||||
return { damage = dealt, killed = defender.hp <= 0 }
|
return { damage = dealt, killed = defender.hp <= 0 }
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in new issue
Block a user