summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/game_controller.rb80
-rw-r--r--app/models/character.rb45
-rw-r--r--app/models/monster.rb31
-rw-r--r--app/views/activities/_results.html.erb2
-rw-r--r--app/views/activities/show.html.erb2
-rw-r--r--data/activities.yml21
-rw-r--r--data/monsters.yml48
-rw-r--r--db/migrate/20210522194937_add_wounds_to_characters.rb5
-rw-r--r--db/migrate/20210522201259_create_monsters.rb12
-rw-r--r--db/schema.rb12
-rw-r--r--db/seeds.rb5
-rw-r--r--test/fixtures/monsters.yml11
-rw-r--r--test/models/monster_test.rb7
14 files changed, 283 insertions, 2 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">
diff --git a/data/activities.yml b/data/activities.yml
index 26f4535..4975cf9 100644
--- a/data/activities.yml
+++ b/data/activities.yml
@@ -151,3 +151,24 @@ quarry_floret_mines:
xp:
- gid: "planequarry"
value: 50
+hunt_killing_fields:
+ name: "Hunt The Killing Fields"
+ description: "Hunt monsters in The Killing Fields."
+ location: "floret_region"
+ innate: true
+ whatnot:
+ duration:
+ base: 60
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "beastslay"
+ scale_value: 2
+ results:
+ - type: "monster"
+ chance: 1
+ table:
+ - gid: "pit_leech"
+ score: 0
+ - gid: "stalk_beast"
+ score: 0.70
diff --git a/data/monsters.yml b/data/monsters.yml
new file mode 100644
index 0000000..631c83c
--- /dev/null
+++ b/data/monsters.yml
@@ -0,0 +1,48 @@
+pit_leech:
+ name: "pit leech"
+ description: >-
+ A brown-black glistening thing the size of your arm and four times as wide. Featureless, except
+ for a ring of razor-sharp teeth at one end, encircling an inquisitive maw.
+ whatnot:
+ max_hp:
+ base: 5
+ speed:
+ base: 1
+ accuracy:
+ base: 1
+ power:
+ base: 1
+ evasion:
+ base: 1
+ block:
+ base: 1
+ block_value:
+ base: 1
+ awards:
+ - type: "xp"
+ skill: "beastslay"
+ base: 5
+stalk_beast:
+ name: "stalk beast"
+ description: >-
+ A walking tangle of long, sinewy eye stalks, each punctuated with a fist-sized eyeball. They were and
+ are the heralds of things to come.
+ whatnot:
+ max_hp:
+ base: 9
+ speed:
+ base: 3
+ accuracy:
+ base: 2
+ power:
+ base: 1
+ evasion:
+ base: 2
+ block:
+ base: 1
+ block_value:
+ base: 1
+ awards:
+ - type: "xp"
+ skill: "beastslay"
+ base: 9
diff --git a/db/migrate/20210522194937_add_wounds_to_characters.rb b/db/migrate/20210522194937_add_wounds_to_characters.rb
new file mode 100644
index 0000000..be56e29
--- /dev/null
+++ b/db/migrate/20210522194937_add_wounds_to_characters.rb
@@ -0,0 +1,5 @@
+class AddWoundsToCharacters < ActiveRecord::Migration[6.1]
+ def change
+ add_column :characters, :wounds, :integer
+ end
+end
diff --git a/db/migrate/20210522201259_create_monsters.rb b/db/migrate/20210522201259_create_monsters.rb
new file mode 100644
index 0000000..39f0a1a
--- /dev/null
+++ b/db/migrate/20210522201259_create_monsters.rb
@@ -0,0 +1,12 @@
+class CreateMonsters < ActiveRecord::Migration[6.1]
+ def change
+ create_table :monsters do |t|
+ t.string :gid
+ t.string :name
+ t.text :description
+ t.jsonb :whatnot
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5649a54..6dfbe87 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_05_22_184444) do
+ActiveRecord::Schema.define(version: 2021_05_22_201259) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -67,6 +67,7 @@ ActiveRecord::Schema.define(version: 2021_05_22_184444) do
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.bigint "active_title_id"
+ t.integer "wounds"
t.index ["active_title_id"], name: "index_characters_on_active_title_id"
t.index ["activity_id"], name: "index_characters_on_activity_id"
t.index ["user_id"], name: "index_characters_on_user_id"
@@ -149,6 +150,15 @@ ActiveRecord::Schema.define(version: 2021_05_22_184444) do
t.index ["gid"], name: "index_locations_on_gid"
end
+ create_table "monsters", force: :cascade do |t|
+ t.string "gid"
+ t.string "name"
+ t.text "description"
+ t.jsonb "whatnot"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ end
+
create_table "skills", force: :cascade do |t|
t.string "gid"
t.string "name"
diff --git a/db/seeds.rb b/db/seeds.rb
index ca72f1e..5a6c696 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -49,3 +49,8 @@ load_data_file("data/hearth_amenities.yml").map do |gid, hash|
hearth_amenity = HearthAmenity.find_or_create_by(gid: gid)
hearth_amenity.update(hash)
end
+
+load_data_file("data/monsters.yml").map do |gid, hash|
+ monster = Monster.find_or_create_by(gid: gid)
+ monster.update(hash)
+end
diff --git a/test/fixtures/monsters.yml b/test/fixtures/monsters.yml
new file mode 100644
index 0000000..ec30753
--- /dev/null
+++ b/test/fixtures/monsters.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ name: MyString
+ description: MyText
+ whatnot:
+
+two:
+ name: MyString
+ description: MyText
+ whatnot:
diff --git a/test/models/monster_test.rb b/test/models/monster_test.rb
new file mode 100644
index 0000000..f57dbbd
--- /dev/null
+++ b/test/models/monster_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class MonsterTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end