diff options
author | David Gay <david@davidgay.org> | 2021-05-22 18:10:19 -0400 |
---|---|---|
committer | David Gay <david@davidgay.org> | 2021-05-22 18:10:19 -0400 |
commit | 44facc2e567eb3c045ce082428f42276e45b0202 (patch) | |
tree | 0b302bfd60a6faae698a63e68e3e1a5bdb9e16c2 /app | |
parent | 38f3a39221869483e3468e9f4d8cab5450a70f89 (diff) |
Monsters and basic combat
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/application_controller.rb | 4 | ||||
-rw-r--r-- | app/controllers/game_controller.rb | 80 | ||||
-rw-r--r-- | app/models/character.rb | 45 | ||||
-rw-r--r-- | app/models/monster.rb | 31 | ||||
-rw-r--r-- | app/views/activities/_results.html.erb | 2 | ||||
-rw-r--r-- | app/views/activities/show.html.erb | 2 |
6 files changed, 163 insertions, 1 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index efd8427..81e07da 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,4 +10,8 @@ class ApplicationController < ActionController::Base def redirect_if_no_active_character redirect_to new_character_path unless current_char end + + def roll(sides) + rand(sides) + 1 + end end diff --git a/app/controllers/game_controller.rb b/app/controllers/game_controller.rb index 58644a7..c63fbc5 100644 --- a/app/controllers/game_controller.rb +++ b/app/controllers/game_controller.rb @@ -13,6 +13,23 @@ class GameController < ApplicationController activity.whatnot[:results].each do |result| type = result[:type] case type + when "monster" + next if rand > (result[:chance] || 1) + table_roll = rand + result[:table].sort_by { |t| -t[:score] }.each do |table_entry| + score = table_entry[:score] + result[:table_scaling]&.each do |scale_entry| + case scale_entry[:type] + when "skill" + score = score**(1 + (scale_entry[:scale_value] * current_char.skill_level(scale_entry[:gid]))) + end + end + if table_roll >= score + activity = Activity.find_by_gid(table_entry[:gid]) + monster = Monster.find_by_gid(table_entry[:gid]) + resolve_combat_with(monster) + end + end when "item" next if rand > (result[:chance] || 1) @@ -83,4 +100,67 @@ class GameController < ApplicationController current_char.shift_item(item, quantity) @results.push({ type: "item", item: item, quantity: quantity, xp: xp_awards }) end + + def resolve_combat_with(mon) + char = current_char + char_hp = current_char.max_hp + mon_hp = mon.max_hp + combat_message = ->(msg) { @results.push({ type: "message", body: "[#{char_hp}/#{char.max_hp}] #{msg}" }) } + combat_message.call("You encountered a #{mon.name}.") + char_initiative = roll(10) + char.speed + mon_initative = roll(10) + mon.speed + if char_initiative > mon_initative + turn_order = [char, mon] + elsif mon_initative > char_initiative + turn_order = [mon, char] + else + turn_order = [char, mon].shuffle + end + turn_order.cycle do |actor| + case actor + when char + accuracy_roll = roll(20) + char.accuracy + evasion_roll = roll(20) + mon.evasion + if accuracy_roll >= evasion_roll + dealt_damage = roll(4) + char.power # TODO: Replace d4 with weapon damage + if accuracy_roll >= (evasion_roll + 10) + combat_message.call("You landed a critical hit!") + dealt_damage = dealt_damage * 2 + end + blocked_damage = (accuracy_roll >= (roll(20) + mon.block)) ? 0 : mon.block_value + resolved_damage = dealt_damage - blocked_damage + mon_hp -= resolved_damage + combat_message.call("You hit for #{resolved_damage} (#{dealt_damage} - #{blocked_damage} blocked)") + elsif evasion_roll > accuracy_roll + combat_message.call("The #{mon.name} evaded your attack.") + end + when mon + combat_message.call("Monsters don't get turns yet.") + else + raise "Invalid combatant (class is #{actor.class})" + end + if char_hp < 1 || mon_hp < 1 + if char_hp < 1 + combat_message.call("You were defeated! You retreat, wounded.") + char.increment(:wounds) + else + combat_message.call("You defeated the #{mon.name}.") + mon.whatnot[:awards]&.each do |award_data| + case award_data[:type] + when "xp" + skill = Skill.find_by_gid(award_data[:skill]) + amount = award_data[:base] + char.add_skill_xp(skill, amount) + combat_message.call("You gained #{amount} #{skill.name} XP.") + else + raise "Invalid award type string (#{award_data[:type]})" + end + end + end + break + else + combat_message.call("-" * 20) + end + end + end end diff --git a/app/models/character.rb b/app/models/character.rb index 59ed5ae..bad2bb2 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -19,6 +19,22 @@ class Character < ApplicationRecord after_create :create_skills after_create { Hearth.create(character: self) } + def beastslay_level; skill_level("beastslay"); end + def fluxseethe_level; skill_level("fluxseethe"); end + def havencast_level; skill_level("havencast"); end + def hexcarve_level; skill_level("hexcarve"); end + def magiculture_level; skill_level("magiculture"); end + def manatrawl_level; skill_level("manatrawl"); end + def omenbind_level; skill_level("omenbind"); end + def otherforge_level; skill_level("otherforge"); end + def planequarry_level; skill_level("planequarry"); end + def spicework_level; skill_level("spicework"); end + def synthsever_level; skill_level("synthsever"); end + def veilreach_level; skill_level("veilreach"); end + def wealdreap_level; skill_level("wealdreap"); end + def wildscour_level; skill_level("wildscour"); end + def worldcall_level; skill_level("worldcall"); end + def shift_item(item, amount) item = Item.find_by_gid(item) if item.is_a? String CharacterItem.transaction do @@ -70,6 +86,7 @@ class Character < ApplicationRecord end def add_skill_xp(skill, amount) + skill = Skill.find_by_gid(skill) if skill.is_a? String CharacterSkill.find_by(skill: skill).increment!(:xp, amount) end @@ -115,6 +132,34 @@ class Character < ApplicationRecord self.update(activity: activity, activity_started_at: Time.now) if self.can_do_activity?(activity) end + def can_fight? + self.wounds < 1 + end + + def max_hp + 10 + self.beastslay_level + end + + def speed + self.beastslay_level + end + + def accuracy + self.beastslay_level + end + + def power + self.beastslay_level + end + + def evasion + self.beastslay_level + end + + def block + self.beastslay_level + 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 new file mode 100644 index 0000000..1b521aa --- /dev/null +++ b/app/models/monster.rb @@ -0,0 +1,31 @@ +class Monster < ApplicationRecord + include HasWhatnot + + def max_hp + self.whatnot[:max_hp][:base] + end + + def speed + self.whatnot[:speed][:base] + end + + def accuracy + self.whatnot[:accuracy][:base] + end + + def power + self.whatnot[:power][:base] + end + + def evasion + self.whatnot[:evasion][:base] + end + + def block + self.whatnot[:block][:base] + end + + def block_value + self.whatnot[:block_value][:base] + end +end diff --git a/app/views/activities/_results.html.erb b/app/views/activities/_results.html.erb index 0fef8fb..5fde590 100644 --- a/app/views/activities/_results.html.erb +++ b/app/views/activities/_results.html.erb @@ -11,6 +11,8 @@ <p>You constructed <%= result[:hearth_amenity].name %>.</p> <% when "activity" %> <p>You realized how to <%= result[:activity].name %>!</p> + <% when "message" %> + <p><%= result[:body] %></p> <% when "error" %> <p><%= result[:message] %></p> <% end %> diff --git a/app/views/activities/show.html.erb b/app/views/activities/show.html.erb index 84c19a4..c9c6d25 100644 --- a/app/views/activities/show.html.erb +++ b/app/views/activities/show.html.erb @@ -2,7 +2,7 @@ <p><%= @activity.description %></p> <div class="min-w-full my-2 px-1 overflow-auto text-sm border-2 border-gray-800 rounded" - style="height: 75px" id="result_output"> + style="height: 30rem;" id="result_output"> </div> <div id="result_controls"> |