From d2afb75b2cf512158d82ea2513ee4fb6662362a7 Mon Sep 17 00:00:00 2001 From: David Gay Date: Sun, 6 Jun 2021 14:15:59 -0400 Subject: Switch from power-based damage to damage types, replace block with resistance, and make natural 20 accuracy rolls always hit (and crit) --- CHANGELOG.md | 8 ++++ app/lib/activity_processor.rb | 30 ++++++++---- app/models/character.rb | 22 ++++----- app/models/monster.rb | 8 +--- app/views/characters/show.html.erb | 8 ---- data/items.yml | 98 +++++++++++++++++--------------------- data/monsters.yml | 44 ++++++++--------- 7 files changed, 103 insertions(+), 115 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffb0af2..d7674b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ All notable changes to this project will be documented in this file. ### General - Implemented monitoring (error tracking, uptime, server health, etc.) via Honeybadger. +### Combat +- A natural 20 on an accuracy roll now always results in a (critical) hit. Previously, if a 20 was rolled, it + could still miss. Now it's always a hit and always a critical hit. +- The block and block value stats have been removed. + +### Items +- Granite ring now increases physical resistance by 2, since block has been removed. + ### UI - New inventory view, with items sorted into categories, and a (hopefully) better section for equipped items. diff --git a/app/lib/activity_processor.rb b/app/lib/activity_processor.rb index 73f296d..5e6acaf 100644 --- a/app/lib/activity_processor.rb +++ b/app/lib/activity_processor.rb @@ -210,18 +210,30 @@ class ActivityProcessor base_accuracy_roll = roll(20) accuracy_roll = base_accuracy_roll + actor.accuracy(with_combat_style: true) evasion_roll = roll(20) + target.evasion(with_combat_style: true) - if accuracy_roll >= evasion_roll - dealt_damage = roll(4) + actor.power(with_combat_style: true) # TODO: Replace d4 with weapon damage + if accuracy_roll >= evasion_roll || base_accuracy_roll == 20 + + dealt_damage = {} + actor.damage_ranges.each do |data| + damage_roll = rand(data[:min]..data[:max]) + dealt_damage[data[:gid]] = damage_roll + end + if base_accuracy_roll == 20 combat_message.call("#{actor.name} landed a critical hit!") - dealt_damage = dealt_damage * 2 + dealt_damage.each do |gid, amount| + dealt_damage[gid] = amount * 2 + end end - blocked_damage = (accuracy_roll >= (roll(20) + target.block(with_combat_style: true))) ? 0 : target.block_value - blocked_damage = [blocked_damage, (dealt_damage - 1)].min - resolved_damage = dealt_damage - blocked_damage - actor == char ? mon_hp -= resolved_damage : char_hp -= resolved_damage - damage_text = "#{resolved_damage} damage." - damage_text += " (#{dealt_damage} - #{blocked_damage} blocked)" if blocked_damage > 0 + + resolved_damage = {} + dealt_damage.each do |gid, amount| + effective_resistance = [target.resistance(gid), amount].min + resolved_damage[gid] = amount - (effective_resistance * rand(0.5..1)).round + end + + total_damage = resolved_damage.values.sum + actor == char ? mon_hp -= total_damage : char_hp -= total_damage + damage_text = "#{total_damage} damage (#{resolved_damage.to_a.map { |gid, amount| "#{amount} #{gid}" }.join(", ")})" combat_message.call("#{actor.name} hit for #{damage_text}") elsif evasion_roll > accuracy_roll combat_message.call("#{target.name} evaded #{actor.name}'s attack.") diff --git a/app/models/character.rb b/app/models/character.rb index 0f434cf..15169a4 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -312,6 +312,10 @@ class Character < ApplicationRecord effects.filter_map { |e| e[:modifier] if e[:type] == "stat_change" && e[:gid] == gid }.sum end + def damage_ranges + effects.filter_map { |e| { gid: e[:gid], min: e[:min], max: e[:max] } if e[:type] == "damage" } + end + def planting_spots [total_stat_change("planting_spots"), 0].max end @@ -325,12 +329,16 @@ class Character < ApplicationRecord bleed physical energy].include?(damage_type) raise "Invalid damage type" end + res = total_stat_change("#{damage_type}_resistance") if %w[slash pierce bash].include?(damage_type) res += resistance("physical") elsif %w[arcane fire frost lightning acid thunder radiant necrotic].include?(damage_type) res += resistance("energy") end + + res = (res * 0.75).ceil if elusive? + res = (res * 1.25).floor if protective? [res, 0].max end @@ -376,20 +384,6 @@ class Character < ApplicationRecord base end - def block(with_combat_style: false) - base = [self.beastslay_level + total_stat_change("block"), 0].max - if with_combat_style && self.elusive? - base = (base * 0.75).ceil - elsif with_combat_style && self.protective? - base = (base * 1.25).floor - end - base - end - - def block_value - [total_stat_change("block_value"), 0].max - end - private def create_skills Skill.all.each { |skill| self.character_skills.create(skill: skill, xp: 0) } diff --git a/app/models/monster.rb b/app/models/monster.rb index f4ae7a7..40018fd 100644 --- a/app/models/monster.rb +++ b/app/models/monster.rb @@ -21,12 +21,8 @@ class Monster < ApplicationRecord self.whatnot[:evasion][:base] end - def block(with_combat_style: false) - self.whatnot[:block][:base] - end - - def block_value - self.whatnot[:block_value][:base] + def damage_ranges + self.whatnot[:hit_effects].filter_map { |e| { gid: e[:gid], min: e[:min], max: e[:max] } if e[:type] == "damage" } end def resistance(damage_type) diff --git a/app/views/characters/show.html.erb b/app/views/characters/show.html.erb index 75143ec..896280d 100644 --- a/app/views/characters/show.html.erb +++ b/app/views/characters/show.html.erb @@ -74,14 +74,6 @@ Evasion <%= @character.evasion(with_combat_style: true) %> - - Block - <%= @character.block(with_combat_style: true) %> - - - Block Value - <%= @character.block_value %> - diff --git a/data/items.yml b/data/items.yml index 0fdc49c..760a7b6 100644 --- a/data/items.yml +++ b/data/items.yml @@ -83,12 +83,10 @@ iron_dagger: equip_slots: - "mainhand" equip_effects: - - type: "stat_change" - gid: "accuracy" - modifier: 2 - - type: "stat_change" - gid: "power" - modifier: 2 + - type: "damage" + gid: "pierce" + min: 1 + max: 4 iron_short_sword: name: "iron short sword" description: "A short sword made of iron." @@ -100,12 +98,14 @@ iron_short_sword: gid: "beastslay" level: 2 equip_effects: - - type: "stat_change" - gid: "accuracy" - modifier: 3 - - type: "stat_change" - gid: "power" - modifier: 3 + - type: "damage" + gid: "slash" + min: 1 + max: 3 + - type: "damage" + gid: "pierce" + min: 1 + max: 3 iron_longsword: name: "iron longsword" description: "A longsword made of iron." @@ -117,12 +117,10 @@ iron_longsword: gid: "beastslay" level: 5 equip_effects: - - type: "stat_change" - gid: "accuracy" - modifier: 4 - - type: "stat_change" - gid: "power" - modifier: 4 + - type: "damage" + gid: "slash" + min: 1 + max: 8 iron_buckler: name: "iron buckler" description: "A buckler made of iron." @@ -131,10 +129,7 @@ iron_buckler: - "offhand" equip_effects: - type: "stat_change" - gid: "block" - modifier: 1 - - type: "stat_change" - gid: "block_value" + gid: "physical_resistance" modifier: 1 iron_shield: name: "iron shield" @@ -148,11 +143,11 @@ iron_shield: level: 3 equip_effects: - type: "stat_change" - gid: "block" + gid: "physical_resistance" modifier: 2 - type: "stat_change" - gid: "block_value" - modifier: 2 + gid: "energy_resistance" + modifier: 1 arcanite_dagger: name: "arcanite dagger" description: "A dagger made of arcanite." @@ -164,12 +159,10 @@ arcanite_dagger: gid: "beastslay" level: 10 equip_effects: - - type: "stat_change" - gid: "accuracy" - modifier: 6 - - type: "stat_change" - gid: "power" - modifier: 6 + - type: "damage" + gid: "pierce" + min: 2 + max: 6 arcanite_short_sword: name: "arcanite short sword" description: "A short sword made of iron." @@ -181,12 +174,14 @@ arcanite_short_sword: gid: "beastslay" level: 12 equip_effects: - - type: "stat_change" - gid: "accuracy" - modifier: 7 - - type: "stat_change" - gid: "power" - modifier: 7 + - type: "damage" + gid: "slash" + min: 2 + max: 5 + - type: "damage" + gid: "pierce" + min: 2 + max: 5 arcanite_longsword: name: "arcanite longsword" description: "A longsword made of arcanite." @@ -198,12 +193,10 @@ arcanite_longsword: gid: "beastslay" level: 15 equip_effects: - - type: "stat_change" - gid: "accuracy" - modifier: 8 - - type: "stat_change" - gid: "power" - modifier: 8 + - type: "damage" + gid: "slash" + min: 3 + max: 12 arcanite_buckler: name: "arcanite buckler" description: "A buckler made of arcanite." @@ -216,11 +209,8 @@ arcanite_buckler: - "offhand" equip_effects: - type: "stat_change" - gid: "block" - modifier: 4 - - type: "stat_change" - gid: "block_value" - modifier: 4 + gid: "physical_resistance" + modifier: 3 arcanite_shield: name: "arcanite shield" description: "A shield made of arcanite." @@ -233,11 +223,11 @@ arcanite_shield: level: 13 equip_effects: - type: "stat_change" - gid: "block" - modifier: 5 + gid: "physical_resistance" + modifier: 4 - type: "stat_change" - gid: "block_value" - modifier: 5 + gid: "energy_resistance" + modifier: 2 mending_salve: name: "mending salve" description: "A healing mixture capable of closing wounds." @@ -478,8 +468,8 @@ granite_ring: - "right_ring" equip_effects: - type: "stat_change" - gid: "block" - modifier: 1 + gid: "physical_resistance" + modifier: 2 mudtub_seed: name: "mudtub seed" description: "The seed of a mudtub plant." diff --git a/data/monsters.yml b/data/monsters.yml index 4a8dc40..ca55a64 100644 --- a/data/monsters.yml +++ b/data/monsters.yml @@ -14,10 +14,11 @@ pit_leech: base: 1 evasion: base: 1 - block: - base: 1 - block_value: - base: 1 + hit_effects: + - type: "damage" + gid: "pierce" + min: 1 + max: 5 awards: - type: "xp" gid: "beastslay" @@ -47,10 +48,11 @@ stalk_beast: base: 2 evasion: base: 2 - block: - base: 1 - block_value: - base: 1 + hit_effects: + - type: "damage" + gid: "bash" + min: 3 + max: 8 awards: - type: "xp" gid: "beastslay" @@ -82,10 +84,11 @@ grinpad: base: 2 evasion: base: 5 - block: - base: 1 - block_value: - base: 1 + hit_effects: + - type: "damage" + gid: "slash" + min: 5 + max: 12 awards: - type: "xp" gid: "beastslay" @@ -122,10 +125,11 @@ lesser_trodgeathomp: base: 10 evasion: base: 5 - block: - base: 10 - block_value: - base: 3 + hit_effects: + - type: "damage" + gid: "bash" + min: 12 + max: 20 awards: - type: "xp" gid: "beastslay" @@ -160,10 +164,6 @@ bollyrot: base: 7 evasion: base: 7 - block: - base: 6 - block_value: - base: 2 awards: - type: "xp" gid: "beastslay" @@ -195,10 +195,6 @@ crypt_writhe: base: 12 evasion: base: 10 - block: - base: 8 - block_value: - base: 4 awards: - type: "xp" gid: "beastslay" -- cgit v1.2.3