class GameController < ApplicationController def finish_activity @results = [] return unless current_char.activity_time_remaining <= 0 activity = current_char.activity return unless current_char.can_do_activity?(activity) # TODO: Add error message current_char.update(activity_started_at: Time.now) Character.transaction do current_char.pay_cost_for(activity) 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) if result[:table] table_roll = rand result[:table].sort_by { |t| -t[:score] }.each do |table_entry| quantity = table_entry[:quantity] || 1 score = table_entry[:score] table_scaling = result[:table_scaling] 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 give_item_with_xp(table_entry, quantity) break end end else quantity = result[:quantity] || 1 give_item_with_xp(result, quantity) end when "hearth_amenity" bhi = current_char.hearth.built_hearth_amenities .find_or_initialize_by(hearth_amenity: HearthAmenity.find_by_gid(result[:gid])) bhi.update(level: result[:level]) @results.push({ type: type, hearth_amenity: bhi.hearth_amenity }) when "activity" 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]) unless current_char.learned_activities.exists?(activity: activity) current_char.learned_activities.create(activity: activity) @results.push({ type: type, activity: activity }) end end end else raise "Invalid result type (#{type})" # TODO: Improve this. end end end rescue ItemQuantityError @results.replace({ type: "error", message: "You don't have enough items to complete this activity." }) end private def give_item_with_xp(data, quantity) item = Item.find_by_gid(data[:gid]) xp_awards = data[:xp]&.map { |xpe| { skill: Skill.find_by_gid(xpe[:gid]), amount: xpe[:value] } } xp_awards&.each do |award| current_char.add_skill_xp(award[:skill], (award[:amount] * quantity)) end 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