summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md79
-rw-r--r--app/controllers/characters/bestiary_controller.rb12
-rw-r--r--app/controllers/characters/hearth_controller.rb3
-rw-r--r--app/controllers/characters/item_infixes_controller.rb27
-rw-r--r--app/controllers/characters/items_controller.rb12
-rw-r--r--app/controllers/characters/rankings_controller.rb2
-rw-r--r--app/controllers/characters/skills_controller.rb15
-rw-r--r--app/controllers/characters/spells_controller.rb9
-rw-r--r--app/controllers/characters_controller.rb15
-rw-r--r--app/errors/item_infix_error.rb2
-rw-r--r--app/lib/activity_processor.rb57
-rw-r--r--app/models/character.rb74
-rw-r--r--app/models/character_skill.rb16
-rw-r--r--app/models/concerns/has_costs_and_requirements.rb8
-rw-r--r--app/models/item.rb7
-rw-r--r--app/models/item_infix.rb21
-rw-r--r--app/models/monster_kill.rb22
-rw-r--r--app/views/application/_navbar.html.erb20
-rw-r--r--app/views/application/_results.html.erb4
-rw-r--r--app/views/application/components/text/_title.html.erb14
-rw-r--r--app/views/characters/bestiary/index.html.erb22
-rw-r--r--app/views/characters/items/index.html.erb4
-rw-r--r--app/views/characters/show.html.erb26
-rw-r--r--app/views/characters/skills/_infix_slot.html.erb3
-rw-r--r--app/views/characters/skills/index.html.erb57
-rw-r--r--app/views/characters/spells/index.html.erb10
-rw-r--r--config/routes.rb4
-rw-r--r--data/activities.yml598
-rw-r--r--data/conditions.yml38
-rw-r--r--data/hearth_amenities.yml11
-rw-r--r--data/items.yml112
-rw-r--r--data/monsters.yml2
-rw-r--r--data/skills.yml2
-rw-r--r--data/titles.yml14
-rw-r--r--db/migrate/20210614004827_create_item_infixes.rb11
-rw-r--r--db/migrate/20210616014044_create_monster_kills.rb11
-rw-r--r--db/schema.rb28
-rw-r--r--test/fixtures/item_infixes.yml11
-rw-r--r--test/fixtures/monster_kills.yml11
-rw-r--r--test/models/item_infix_test.rb7
-rw-r--r--test/models/monster_kill_test.rb7
41 files changed, 1310 insertions, 98 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6e3d9d..0ea26fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,85 @@
# Changelog
All notable changes to this project will be documented in this file.
+## [0.1.11] - 2021-06-16
+
+### General
+- Any items equipped which provide a speed increase to a skill now have a chance to break when performing that skill.
+ The break chance is based on the equip slot of the item, and uses the same numbers as the combat equipment break
+ chance. The combat equipment break check process is unchanged.
+- Omenbind has been implemented.
+ - Omens can be bound once a character has constructed a binding array in their hearth.
+ - Omens are tradable items that can be infixed in a particular skill via the new skills UI (see the UI section of this
+ changelog).
+ - A single omen can be infixed in a skill at skill level 1 (the level of the skill you're infixing into, not the level
+ of your omenbind skill, unless of course you're infixing into your omenbind skill). At level 20 and every 20 levels
+ thereafter, another infix slot becomes available.
+ - Omens can be removed and re-infixed at will.
+ - All omens infixed in a skill have a chance to fade away and be permanently lost when performing that skill
+ (like equipment breakage). The chance of an infixed omen fading away is 1/500. You do not need to actively be
+ getting a bonus from an infixed omen for it to have a chance to fade away. When performing a skill, all omens
+ infixed in that skill have the same chance to fade away.
+ - Duplicate omens can be infixed, and their effects will stack if possible.
+- Havencast has been implemented.
+ - Spells can be cast from a new "Spells" view, accessible with the navigation.
+ - Spells typically require that a certain amount of mana is available. Mana can be made available by infixing certain
+ omens.
+ - Cantrips are spells which a character can cast by simply having the appropriate requirements and paying any cost.
+ Other spells must be learned from a spellpage by casting a decipher magicscript cantrip on the spellpage,
+ consuming it. Each spellpage can contain a different spell, and the one it provides isn't determined until
+ deciphering and consuming the spellpage. If the spell on the spellpage is one a character doesn't know yet, they
+ will learn the spell upon deciphering the spellpage.
+ - Non-cantrip spells also require that a spell focus is equipped.
+- Characters now each have a bestiary where monster kills are recorded. For leviathans, only the character who actually
+ slays the leviathan by dealing the final damage gets credit for the kill in their bestiary. The bestiary can be viewed
+ via a link on the Character view. (Hopefully) obviously, kills before this release were not counted.
+
+### Items
+- New items: faint mana, minor mana, faint forging haste, faint seething haste, faint working haste, simple spellpage,
+ apprentice wand
+- Iron lockpicks now increase synthsever speed by 10
+- Axes, pickaxes, and spades skill speed increase numbers all adjusted to compensate for base timer reductions
+
+### Activities
+- Planequarry activities now require a pickaxe, wealdreap activities now require an axe, magiculture activities now
+ require a spade, and base timers and item speed bonuses have been adjusted appropriately
+- Planequarry at the Brine Trench base duration changed from 60 to 50 (should have been like, 120 before but it was
+ mistakenly set to 60, and now it's being adjusted for the tool/timer changes in this release)
+- Planequarry XP award changes
+ - Pure iron ore 25 -> 18
+ - Gaian ore 50 -> 25
+- Otherforge
+ - Base duration changes
+ - Purify crude iron ingot 105 -> 60
+ - Purify iron ingot 115 -> 70
+ - Level requirements changes
+ - Arcanite longsword 15 -> 17 (setting it to 15 was a programming error)
+- Havencast
+ - New cantrips: Light, Dazzle, Decipher Simple Magicscript, Enchant Apprentice Wand
+ - New spells: Stinging Rays, Flame Whirl
+- Wildscour
+ - Crumbling ruins now has a chance to drop a simple spellpage
+
+### Monsters
+- Grinpads now have a chance to drop a simple spellpage
+
+### Hearth
+- New amenity: binding array (level 1, level 2)
+
+### Titles
+- 7 new titles
+ - Slayer: Kill 1,000 monsters
+ - Butcher: Kill 10,000 monsters
+ - Slaughterer: Kill 100,000 monsters
+ - Massacrer: Kill 1,000,000 monsters
+ - Spiteful: Kill 1,000 of one particular monster
+ - Hateful: Kill 10,000 of one particular monster
+ - Vicious: Kill 100,000 of one particular monster
+
+### UI
+- Character skills were moved from the Character view to their own view, which features a new interface with XP bars.
+- Reduced space between nav menu items
+
## [0.1.10.3] - 2021-06-14
### Fixed
diff --git a/app/controllers/characters/bestiary_controller.rb b/app/controllers/characters/bestiary_controller.rb
new file mode 100644
index 0000000..7e37d9c
--- /dev/null
+++ b/app/controllers/characters/bestiary_controller.rb
@@ -0,0 +1,12 @@
+class Characters::BestiaryController < ApplicationController
+ before_action :set_character
+
+ def index
+ @monster_kills = @character.monster_kills.all
+ end
+
+ private
+ def set_character
+ @character = Character.find(params[:character_id])
+ end
+end
diff --git a/app/controllers/characters/hearth_controller.rb b/app/controllers/characters/hearth_controller.rb
index 82f72d6..c525add 100644
--- a/app/controllers/characters/hearth_controller.rb
+++ b/app/controllers/characters/hearth_controller.rb
@@ -7,6 +7,7 @@ class Characters::HearthController < ApplicationController
forge: [],
laboratory: [],
spicebench: [],
+ binding_array: [],
}
Activity.where("gid like ?", "craft_%").each do |activity|
@@ -20,6 +21,8 @@ class Characters::HearthController < ApplicationController
@amenity_activities[:laboratory].push(activity) && next
when "spicebench"
@amenity_activities[:spicebench].push(activity) && next
+ when "binding_array"
+ @amenity_activities[:binding_array].push(activity) && next
else
raise "Invalid amenity gid (#{requirement_data[:gid]}"
end
diff --git a/app/controllers/characters/item_infixes_controller.rb b/app/controllers/characters/item_infixes_controller.rb
new file mode 100644
index 0000000..0b5f1c5
--- /dev/null
+++ b/app/controllers/characters/item_infixes_controller.rb
@@ -0,0 +1,27 @@
+class Characters::ItemInfixesController < ApplicationController
+ def create
+ # TODO: Can this find-by-id happen automagically?
+ @item_infix = current_char.infix(Item.find(params[:item_id]), Skill.find(params[:skill_id]))
+ if @item_infix
+ flash[:notice] = "Infixed #{@item_infix.item.name}."
+ else
+ flash[:alert] = "Failed to infix item."
+ end
+ redirect_to character_skills_path(current_char)
+ end
+
+ def destroy
+ @item_infix = ItemInfix.find(params[:id])
+ if current_char.remove_infix(@item_infix)
+ flash[:notice] = "Removed #{@item_infix.item.name}."
+ else
+ flash[:alert] = "Failed to remove #{@item_infix.item.name}."
+ end
+ redirect_to character_skills_path(current_char)
+ end
+
+ private
+ def item_infix_params
+ params.require(:item_infix).permit(:item_id, :skill_id)
+ end
+end
diff --git a/app/controllers/characters/items_controller.rb b/app/controllers/characters/items_controller.rb
index 470e21c..e38b69a 100644
--- a/app/controllers/characters/items_controller.rb
+++ b/app/controllers/characters/items_controller.rb
@@ -1,6 +1,7 @@
class Characters::ItemsController < ApplicationController
+ before_action :set_character, only: :index
+
def index
- @character = Character.find(params[:character_id])
end
def equip
@@ -66,4 +67,13 @@ class Characters::ItemsController < ApplicationController
redirect_to character_items_path(current_char)
end
end
+
+ private
+ def set_character
+ @character = Character.find(params[:character_id])
+ unless current_char == @character
+ flash[:alert] = "You can only look at your own items."
+ redirect_to character_path(@character)
+ end
+ end
end
diff --git a/app/controllers/characters/rankings_controller.rb b/app/controllers/characters/rankings_controller.rb
index bbae9fc..429b487 100644
--- a/app/controllers/characters/rankings_controller.rb
+++ b/app/controllers/characters/rankings_controller.rb
@@ -6,4 +6,4 @@ class Characters::RankingsController < ApplicationController
def index
@character = Character.find(params[:character_id])
end
-end \ No newline at end of file
+end
diff --git a/app/controllers/characters/skills_controller.rb b/app/controllers/characters/skills_controller.rb
new file mode 100644
index 0000000..6fcf417
--- /dev/null
+++ b/app/controllers/characters/skills_controller.rb
@@ -0,0 +1,15 @@
+class Characters::SkillsController < ApplicationController
+ before_action :set_character, only: :index
+
+ def index
+ end
+
+ private
+ def set_character
+ @character = Character.find(params[:character_id])
+ unless current_char == @character
+ flash[:alert] = "You can only look at your own skills."
+ redirect_to character_path(@character)
+ end
+ end
+end
diff --git a/app/controllers/characters/spells_controller.rb b/app/controllers/characters/spells_controller.rb
new file mode 100644
index 0000000..a0e6913
--- /dev/null
+++ b/app/controllers/characters/spells_controller.rb
@@ -0,0 +1,9 @@
+class Characters::SpellsController < ApplicationController
+ def index
+ @spell_activities = Activity.where("gid like ?", "havencast_%").where(innate: true).order(:name)
+ # TODO: Don't load into memory
+ @spell_activities = @spell_activities.to_a + current_char.learned_activities
+ .map { |la| la.activity }
+ .select { |a| a.gid.start_with?("havencast_") }
+ end
+end
diff --git a/app/controllers/characters_controller.rb b/app/controllers/characters_controller.rb
index 77e1a94..2eb906b 100644
--- a/app/controllers/characters_controller.rb
+++ b/app/controllers/characters_controller.rb
@@ -1,8 +1,8 @@
class CharactersController < ApplicationController
skip_before_action :redirect_if_no_active_character, only: [:new, :create]
+ before_action :set_character, only: [:show, :set_combat_styles]
def show
- @character = Character.find(params[:id])
end
def new
@@ -22,11 +22,6 @@ class CharactersController < ApplicationController
end
def set_combat_styles
- @character = Character.find(params[:character_id])
- unless @character == current_char
- flash[:alert] = "You can't set the combat styles of another character."
- redirect_to character_path(@character) and return
- end
if @character.update(offensive_style: params[:offensive_style],
defensive_style: params[:defensive_style])
flash[:notice] = "Changed combat styles to #{@character.offensive_style} and #{@character.defensive_style}."
@@ -40,4 +35,12 @@ class CharactersController < ApplicationController
def character_params
params.require(:character).permit(:name)
end
+
+ def set_character
+ @character = Character.find(params[:id])
+ unless current_char == @character
+ flash[:alert] = "You can only manage your own character."
+ redirect_to character_path(@character)
+ end
+ end
end
diff --git a/app/errors/item_infix_error.rb b/app/errors/item_infix_error.rb
new file mode 100644
index 0000000..bc6b251
--- /dev/null
+++ b/app/errors/item_infix_error.rb
@@ -0,0 +1,2 @@
+class ItemInfixError < StandardError
+end
diff --git a/app/lib/activity_processor.rb b/app/lib/activity_processor.rb
index 4a06a98..6509b44 100644
--- a/app/lib/activity_processor.rb
+++ b/app/lib/activity_processor.rb
@@ -90,6 +90,13 @@ class ActivityProcessor
item = Item.find_by_gid(result[:gid])
hp = @character.hearth.hearth_plantings.create(item: item)
@results.push({ type: type, hearth_planting: hp })
+ when "condition"
+ Character.transaction do
+ condition = Condition.find_by_gid(result[:gid])
+ @character.states.create!(condition: condition, expires_at: Time.now + result[:duration])
+ @results.push({ type: "message", body: result[:message] })
+ @results.push({ type: type, condition: condition })
+ end
when "activity"
next if rand > (result[:chance] || 1)
table_roll = rand
@@ -97,6 +104,7 @@ class ActivityProcessor
score = table_entry[:score]
if table_roll >= score
new_activity = Activity.find_by_gid(table_entry[:gid])
+ raise "Invalid activity gid (#{table_entry[:gid]})" unless new_activity
unless @character.learned_activities.exists?(activity: new_activity)
@character.learned_activities.create(activity: new_activity)
@results.push({ type: type, activity: new_activity })
@@ -108,6 +116,24 @@ class ActivityProcessor
end
end
+ # Note: This will result in equipment being checked for breakage twice (after combat, and now) if it provides the
+ # `beastslay_speed` stat. At the time of this writing, that stat doesn't exist.
+ # But just something to keep in mind.
+ # Note: This will result in equipment being checked twice if it provides two speed stats.
+ # Fine for now since no equipment gives two skill speed stats, but may want to refine in the future.
+ if @activity.whatnot[:requirements].any?
+ required_skill_gids = @activity.whatnot[:requirements].select { |r| r[:type] == "skill" }.map { |r| r[:gid] }.uniq
+ required_skill_gids.each do |required_skill_gid|
+ skill = Skill.find_by_gid(required_skill_gid)
+ @character.do_skill_based_equipment_break_checks(skill).each do |broken_item|
+ @results.push({ type: "warning", message: "Your #{broken_item.name} was damaged beyond repair!" })
+ end
+ @character.do_skill_based_item_infix_break_checks(skill).each do |broken_item|
+ @results.push({ type: "warning", message: "Your #{broken_item.name} omen faded away." })
+ end
+ end
+ end
+
if @character.activity && @character.queued_actions
if @character.queued_actions > 0
@character.queued_actions -= 1
@@ -231,8 +257,13 @@ class ActivityProcessor
turn_order = [[char, mon], [mon, char]].shuffle
end
turn_order.cycle do |actor, target|
+
base_accuracy_roll = roll(20)
accuracy_roll = base_accuracy_roll + actor.accuracy(with_combat_style: true)
+ if actor == mon
+ accuracy_roll += char.total_enemy_stat_change("accuracy")
+ end
+
evasion_roll = roll(20) + target.evasion(with_combat_style: true)
if accuracy_roll >= evasion_roll || base_accuracy_roll == 20
@@ -274,8 +305,21 @@ class ActivityProcessor
combat_message.call("#{target.name} evaded #{actor.name}'s attack.")
end
+ # HACK: DoT is char-only, and one-damage-type-only for now.
+ if actor == char
+ actor.dots.each do |data|
+ damage_roll = rand(data[:min]..data[:max])
+ effective_resistance = [target.resistance(data[:gid]), damage_roll].min
+ resolved_damage = damage_roll - (effective_resistance * rand(0.5..1)).round
+ mon_hp -= resolved_damage
+ combat_message.call("#{data[:message]} (#{resolved_damage} #{data[:gid]})")
+ end
+ end
+
if char_hp < 1 || mon_hp < 1
- break_check
+ @character.do_equipment_break_checks.each do |broken_item|
+ @results.push({ type: "warning", message: "Your #{broken_item.name} was damaged beyond repair!" })
+ end
if monster_spawn
hp_lost = monster_spawn.remaining_hp - mon_hp
@@ -296,6 +340,11 @@ class ActivityProcessor
end
else
@results.push({ type: "message", body: "You slew the #{mon.name}." })
+
+ monster_kills = character.monster_kills.find_or_initialize_by(monster: mon)
+ monster_kills.quantity ? monster_kills.quantity += 1 : monster_kills.quantity = 1
+ monster_kills.save
+
if monster_spawn
char.stop_activity
return
@@ -318,10 +367,4 @@ class ActivityProcessor
end
end
end
-
- def break_check
- @character.do_equipment_break_checks.each do |broken_item|
- @results.push({ type: "warning", message: "Your #{broken_item.name} was damaged beyond repair!" })
- end
- end
end
diff --git a/app/models/character.rb b/app/models/character.rb
index d386002..b7649c3 100644
--- a/app/models/character.rb
+++ b/app/models/character.rb
@@ -9,10 +9,12 @@ class Character < ApplicationRecord
has_many :character_items
has_many :learned_activities
has_many :items, through: :character_items
+ has_many :item_infixes
has_many :character_skills
has_many :conditions, through: :states
has_many :states
has_many :chat_messages
+ has_many :monster_kills
has_many :bazaar_orders
validates :name, presence: true
# TODO: Make defaults better. This has to allow nil so the `attribute` default works, and I don't like that.
@@ -115,6 +117,30 @@ class Character < ApplicationRecord
end
end
+ def do_skill_based_equipment_break_checks(skill)
+ skill = Skill.find_by_gid(skill) if skill.is_a? String
+ broken_items = []
+ # TODO: HACK: Should check other stats besides speed stat in the future.
+ # TODO: HACK: May not want a chance to break if speed is _reduced_. Though no equipment does this yet.
+ equipment.all.select { |eq| eq.effects.select { |ef| ef[:gid] == "#{skill.gid}_speed" }.any? }.each do |equipment|
+ if equipment.break_check
+ broken_items.push(equipment.item)
+ end
+ end
+ broken_items
+ end
+
+ def do_skill_based_item_infix_break_checks(skill)
+ skill = Skill.find_by_gid(skill) if skill.is_a? String
+ broken_items = []
+ item_infixes.where(skill: skill).each do |ii|
+ if ii.break_check
+ broken_items.push(ii.item)
+ end
+ end
+ broken_items
+ end
+
def do_equipment_break_checks(exclude_slots: [])
broken_items = []
equipment.where.not(slot: exclude_slots).each do |equipment|
@@ -136,6 +162,10 @@ class Character < ApplicationRecord
self.equipment.find_by(item: item)
end
+ def equipment_with_tag(tag)
+ self.equipment.all.find { |e| e.item.has_tag?(tag) }
+ end
+
def open_slots_for(item)
full_slots = self.equipment.map { |e| e.slot }
item.equip_slots.reject { |slot| full_slots.include?(slot) }
@@ -182,6 +212,30 @@ class Character < ApplicationRecord
end
end
+ def max_infixes(skill)
+ skill = Skill.find_by_gid(skill) if skill.is_a? String
+ 1 + (skill_level(skill) / 20).floor
+ end
+
+ def available_infixes(skill)
+ skill = Skill.find_by_gid(skill) if skill.is_a? String
+ max_infixes(skill) - item_infixes.where(skill: skill).count
+ end
+
+ def infix(item, skill)
+ Character.transaction do
+ shift_item(item, -1)
+ item_infixes.create(item: item, skill: skill)
+ end
+ end
+
+ def remove_infix(item_infix)
+ Character.transaction do
+ shift_item(item_infix.item, 1)
+ item_infix.destroy
+ end
+ end
+
def add_skill_xp(skill, amount)
skill = Skill.find_by_gid(skill) if skill.is_a? String
Character.transaction do
@@ -247,7 +301,14 @@ class Character < ApplicationRecord
activity.whatnot[:requirements]&.each do |requirement|
case requirement[:type]
when "equipment"
- return false unless self.equipment_with_gid(requirement[:gid])
+ if requirement[:tag]
+ return false unless self.equipment_with_tag(requirement[:tag])
+ else
+ return false unless self.equipment_with_gid(requirement[:gid])
+ end
+ when "stat"
+ # TODO: HACK: This won't work with built-in stats! Need to change this to work with power and whatnot.
+ return false unless self.total_stat_change(requirement[:gid]) >= requirement[:value]
when "skill"
return false unless self.skill_level(requirement[:gid]) >= requirement[:level]
when "hearth_amenity"
@@ -306,17 +367,26 @@ class Character < ApplicationRecord
hearth_amenity_effects = self.hearth.built_hearth_amenities.filter_map { |a| a.effects if a.effects }
equipment_effects = self.equipment.filter_map { |a| a.effects if a.effects }
state_effects = self.states.filter_map { |a| a.effects if a.effects && !a.expired? }
- (hearth_amenity_effects + equipment_effects + state_effects).flatten
+ item_infix_effects = self.item_infixes.filter_map { |a| a.effects if a.effects }
+ (hearth_amenity_effects + equipment_effects + state_effects + item_infix_effects).flatten
end
def total_stat_change(gid)
effects.filter_map { |e| e[:modifier] if e[:type] == "stat_change" && e[:gid] == gid }.sum
end
+ def total_enemy_stat_change(gid)
+ effects.filter_map { |e| e[:modifier] if e[:type] == "enemy_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 dots
+ effects.filter_map { |e| { gid: e[:gid], min: e[:min], max: e[:max], message: e[:message] } if e[:type] == "dot" }
+ end
+
def planting_spots
[total_stat_change("planting_spots"), 0].max
end
diff --git a/app/models/character_skill.rb b/app/models/character_skill.rb
index 368cc28..406cade 100644
--- a/app/models/character_skill.rb
+++ b/app/models/character_skill.rb
@@ -39,6 +39,10 @@ class CharacterSkill < ApplicationRecord
end
end
+ def self.xp_required_for_level(level)
+ level <= 120 ? XP_TOTALS_PER_LEVEL[level - 1] : nil
+ end
+
def level
XP_TOTALS_PER_LEVEL.each_with_index do |total, index|
return index if total > self.xp
@@ -46,7 +50,7 @@ class CharacterSkill < ApplicationRecord
end
def total_xp_for_next_level
- xp_required_for_level(level + 1)
+ CharacterSkill.xp_required_for_level(level + 1)
end
def xp_to_next_level
@@ -57,11 +61,13 @@ class CharacterSkill < ApplicationRecord
CharacterSkill.top_xp_for(self.skill, limit: nil).map(&:character).map(&:id).index(self.character.id) + 1
end
- private
- def xp_required_for_level(level)
- level <= 120 ? XP_TOTALS_PER_LEVEL[level - 1] : nil
- end
+ def percentage_of_skill_level_completed
+ xp_gained_this_level = xp - CharacterSkill.xp_required_for_level(level)
+ total_xp_gain_neeeded_for_entire_level = total_xp_for_next_level - CharacterSkill.xp_required_for_level(level)
+ (xp_gained_this_level.to_f / total_xp_gain_neeeded_for_entire_level) * 100
+ end
+ private
def send_chat_message_if_leveled_up
if CharacterSkill.level_for_xp(self.xp_was) < CharacterSkill.level_for_xp(self.xp)
chat_message = ChatMessage.new(body: "reached #{self.skill.name} level #{self.level}!",
diff --git a/app/models/concerns/has_costs_and_requirements.rb b/app/models/concerns/has_costs_and_requirements.rb
index 34ff0f3..9c4abf8 100644
--- a/app/models/concerns/has_costs_and_requirements.rb
+++ b/app/models/concerns/has_costs_and_requirements.rb
@@ -19,8 +19,14 @@ module HasCostsAndRequirements
case req[:type]
when "skill"
requirements.push "level #{req[:level]} #{Skill.find_by_gid(req[:gid]).name}"
+ when "stat"
+ requirements.push "#{req[:value]} #{req[:gid]}"
when "equipment"
- requirements.push "equipped #{Item.find_by_gid(req[:gid]).name}"
+ if req[:tag]
+ requirements.push "equipped #{req[:tag]}"
+ else
+ requirements.push "equipped #{Item.find_by_gid(req[:gid]).name}"
+ end
when "hearth_amenity"
requirements.push "level #{req[:level]} #{HearthAmenity.find_by_gid(req[:gid]).name}"
else
diff --git a/app/models/item.rb b/app/models/item.rb
index 8b80788..0e25b2f 100644
--- a/app/models/item.rb
+++ b/app/models/item.rb
@@ -13,6 +13,13 @@ class Item < ApplicationRecord
self.whatnot && self.whatnot[:use_effects]&.any?
end
+ def infixable?(skill = nil)
+ skill = Skill.find_by_gid(skill) if skill.is_a? String
+ return false unless self.whatnot && self.whatnot[:infix_skills]&.any?
+ return true unless skill
+ self.whatnot[:infix_skills].select { |data| data[:gid] == skill.gid }.any?
+ end
+
def equip_slots
return [] unless self.equipment?
self.whatnot[:equip_slots].map { |data| data.to_sym }
diff --git a/app/models/item_infix.rb b/app/models/item_infix.rb
new file mode 100644
index 0000000..b59ccdb
--- /dev/null
+++ b/app/models/item_infix.rb
@@ -0,0 +1,21 @@
+class ItemInfix < ApplicationRecord
+ belongs_to :character
+ belongs_to :item
+ belongs_to :skill
+
+ before_create :check_max_infixes
+
+ def effects
+ self.item.whatnot[:infix_effects]
+ end
+
+ def break_check
+ rand > 0.998 ? destroy : false
+ end
+
+ private
+ def check_max_infixes
+ current_infixes = character.item_infixes.where(skill: skill)
+ raise :abort if current_infixes.count >= character.max_infixes(skill)
+ end
+end
diff --git a/app/models/monster_kill.rb b/app/models/monster_kill.rb
new file mode 100644
index 0000000..24519b1
--- /dev/null
+++ b/app/models/monster_kill.rb
@@ -0,0 +1,22 @@
+class MonsterKill < ApplicationRecord
+ belongs_to :monster
+ belongs_to :character
+
+ validates :quantity, numericality: { greater_than_or_equal_to: 0, only_integer: true }
+ scope :ordered_by_monster_name, -> { includes(:monster).order("monsters.name") }
+
+ after_save :award_titles
+
+ private
+ def award_titles
+ character.award_title("spiteful") if quantity >= 1000
+ character.award_title("hateful") if quantity >= 10_000
+ character.award_title("vicious") if quantity >= 100_000
+
+ all_kills_quantity = character.monster_kills.sum(:quantity)
+ character.award_title("slayer") if all_kills_quantity >= 1_000
+ character.award_title("butcher") if all_kills_quantity >= 10_000
+ character.award_title("slaughterer") if all_kills_quantity >= 100_000
+ character.award_title("massacrer") if all_kills_quantity >= 1_000_000
+ end
+end
diff --git a/app/views/application/_navbar.html.erb b/app/views/application/_navbar.html.erb
index be2d9f2..e315b42 100644
--- a/app/views/application/_navbar.html.erb
+++ b/app/views/application/_navbar.html.erb
@@ -1,21 +1,27 @@
-<ul class="py-2 px-2 col-span-12 text-display">
+<ul class="py-2 px-2 col-span-12 text-display space-x-2.5">
<% if current_char %>
- <li class="mr-6 inline">
+ <li class="inline">
<%= link_to "Locations", locations_path %>
</li>
- <li class="mr-6 inline">
+ <li class="inline">
<%= link_to "Character", character_path(current_char) %>
</li>
- <li class="mr-6 inline">
+ <li class="inline">
+ <%= link_to "Skills", character_skills_path(current_char) %>
+ </li>
+ <li class="inline">
<%= link_to "Inventory", character_items_path(current_char) %>
</li>
- <li class="mr-6 inline">
+ <li class="inline">
+ <%= link_to "Spells", character_spells_path(current_char) %>
+ </li>
+ <li class="inline">
<%= link_to "Hearth", character_hearth_path(current_char) %>
</li>
- <li class="mr-6 inline">
+ <li class="inline">
<%= link_to "Bazaar", bazaar_path %>
</li>
- <li class="mr-6 inline">
+ <li class="inline">
<%= link_to "Messages", messages_path %>
</li>
<% end %>
diff --git a/app/views/application/_results.html.erb b/app/views/application/_results.html.erb
index ec62991..a7dc002 100644
--- a/app/views/application/_results.html.erb
+++ b/app/views/application/_results.html.erb
@@ -13,7 +13,7 @@
<p>You planted <%= link_to result[:hearth_planting].item.name,
item_path(result[:hearth_planting].item) %> in the loam.</p>
<% when "activity" %>
- <p>You realized how to <%= result[:activity].name %>!</p>
+ <p>You learned how to <%= result[:activity].name %>!</p>
<% when "monster" %>
<p>You encountered a <%= result[:monster].name %>.</p>
<p class="text-xs italic"><%= result[:monster].description %></p>
@@ -24,6 +24,8 @@
<p class="text-xs">You gained <%= result[:xp] %> <%= result[:skill].name %> XP.</p>
<% when "title" %>
<p>You earned the title <%= render "application/components/text/title", title: result[:title] %>!</p>
+ <% when "condition" %>
+ <p>You gained the <%= result[:condition].name %> condition.</p>
<% when "message" %>
<p><%= result[:body] %></p>
<% when "warning" %>
diff --git a/app/views/application/components/text/_title.html.erb b/app/views/application/components/text/_title.html.erb
index a7c2a7e..5e1d5a2 100644
--- a/app/views/application/components/text/_title.html.erb
+++ b/app/views/application/components/text/_title.html.erb
@@ -10,6 +10,20 @@
<span class="text-purple-200">Aspirant</span>
<% when "sentinel" %>
<span class="text-gray-500">S</span><span class="text-gray-400">e</span><span class="text-gray-300">n</span><span class="text-gray-200">ti</span><span class="text-gray-300">n</span><span class="text-gray-400">e</span><span class="text-gray-500">l</span>
+ <% when "spiteful" %>
+ <span class="text-transparent bg-clip-text bg-gradient-to-b from-gray-500 to-red-900">Spiteful</span>
+ <% when "hateful" %>
+ <span class="text-transparent bg-clip-text bg-gradient-to-b from-gray-600 to-red-800">Hateful</span>
+ <% when "vicious" %>
+ <span class="text-transparent bg-clip-text bg-gradient-to-b from-gray-700 to-red-700">Vicious</span>
+ <% when "slayer" %>
+ <span class="text-red-400">Slayer</span>
+ <% when "butcher" %>
+ <span class="text-red-500">Butcher</span>
+ <% when "slaughterer" %>
+ <span class="text-red-600">Slaughterer</span>
+ <% when "massacrer" %>
+ <span class="text-red-700">Massacrer</span>
<% else %>
<%= title.name %>
<% end %>
diff --git a/app/views/characters/bestiary/index.html.erb b/app/views/characters/bestiary/index.html.erb
new file mode 100644
index 0000000..f7376e9
--- /dev/null
+++ b/app/views/characters/bestiary/index.html.erb
@@ -0,0 +1,22 @@
+<h1 class="text-3xl mb-4">Bestiary</h1>
+
+<% if @monster_kills.any? %>
+ <table class="table-auto">
+ <thead>
+ <tr>
+ <th class="table-header-padded">Monster</th>
+ <th class="table-header-padded">Kills</th>
+ </tr>
+ </thead>
+ <tbody>
+ <% @monster_kills.ordered_by_monster_name.each do |mk| %>
+ <tr>
+ <td class="table-cell-padded"><%= mk.monster.name %></td>
+ <td class="table-cell-padded"><%= mk.quantity %></td>
+ </tr>
+ <% end %>
+ </tbody>
+ </table>
+<% else %>
+ <p>You haven't killed any monsters yet.</p>
+<% end %>
diff --git a/app/views/characters/items/index.html.erb b/app/views/characters/items/index.html.erb
index 671e68f..57e8531 100644
--- a/app/views/characters/items/index.html.erb
+++ b/app/views/characters/items/index.html.erb
@@ -35,6 +35,10 @@
character_items: @character.character_items.ordered_by_item_name.select { |ci|
ci.item.has_tag?("currency") } %>
+<%= render "characters/items/inventory_section", heading: "Omens",
+ character_items: @character.character_items.ordered_by_item_name.select { |ci|
+ ci.item.has_tag?("omen") } %>
+
<%= render "characters/items/inventory_section", heading: "Seeds",
character_items: @character.character_items.ordered_by_item_name.select { |ci|
ci.item.has_tag?("seed") } %>
diff --git a/app/views/characters/show.html.erb b/app/views/characters/show.html.erb
index a7b7c0e..96a87f8 100644
--- a/app/views/characters/show.html.erb
+++ b/app/views/characters/show.html.erb
@@ -5,6 +5,7 @@
<div class="text-lg text-display mb-4">
<ul class="flex flex-row">
<li class="mr-2"><%= link_to "Titles", character_titles_path(@character) %></li>
+ <li class="mr-2"><%= link_to "Bestiary", character_bestiary_path(@character) %></li>
<li class="mr-2"><%= link_to "Rankings", character_rankings_path(@character) %></li>
</ul>
</div>
@@ -149,31 +150,6 @@
</div>
</div>
-<div class="my-6">
- <h2 class="text-xl mb-4">Skills</h2>
-
- <table class="table-auto mb-8">
- <thead>
- <tr>
- <th class="table-header-padded">Skill</th>
- <th class="table-header-padded">Level</th>
- <th class="table-header-padded">XPTNL</th>
- <th class="table-header-padded">Total XP</th>
- </tr>
- </thead>
- <tbody>
- <% @character.character_skills.ordered_by_skill_name.each do |cs| %>
- <tr>
- <td class="table-cell-padded"><%= cs.skill.name %></td>
- <td class="table-cell-padded"><%= cs.level %></td>
- <td class="table-cell-padded"><%= cs.xp_to_next_level %></td>
- <td class="table-cell-padded"><%= cs.xp %></td>
- </tr>
- <% end %>
- </tbody>
- </table>
-</div>
-
<% if @character == current_char %>
<%= link_to "Manage account", edit_user_registration_path, class: "text-sm" %>
<% end %>
diff --git a/app/views/characters/skills/_infix_slot.html.erb b/app/views/characters/skills/_infix_slot.html.erb
new file mode 100644
index 0000000..0113596
--- /dev/null
+++ b/app/views/characters/skills/_infix_slot.html.erb
@@ -0,0 +1,3 @@
+<div class="flex justify-between items-center border-t border-gray-700 p-2">
+ <%= yield %>
+</div>
diff --git a/app/views/characters/skills/index.html.erb b/app/views/characters/skills/index.html.erb
new file mode 100644
index 0000000..d804384
--- /dev/null
+++ b/app/views/characters/skills/index.html.erb
@@ -0,0 +1,57 @@
+<h2 class="text-3xl mb-2">Skills</h2>
+<div class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
+ <% @character.character_skills.ordered_by_skill_name.each do |cs| %>
+ <div>
+ <div class="rounded border border-gray-700">
+ <div class="flex p-1">
+ <div class="flex-grow">
+ <div class="text-xl text-display mb-1">
+ <%= cs.skill.name %>
+ </div>
+ <div class="flex items-center text-xs">
+ <span class="bg-gray-700 px-1 py-0.5 rounded mr-1">XP</span><%= cs.xp %>
+ </div>
+ </div>
+ <div class="text-xl m-2 text-display">
+ <%= cs.level %>
+ </div>
+ </div>
+ <div class="border border-gray-700 h-2 mt-1 -mb-px -mx-px">
+ <div class="bg-gray-600 h-full" style="width: <%= cs.percentage_of_skill_level_completed %>%">
+ </div>
+ </div>
+ <% @character.item_infixes.where(skill: cs.skill).each do |ii| %>
+ <%= render "characters/skills/infix_slot" do %>
+ <div>
+ <%= ii.item.name %>
+ </div>
+ <div>
+ <%= button_to "Remove", character_item_infix_path(id: ii.id), method: :delete %>
+ </div>
+ <% end %>
+ <% end %>
+ <% @character.available_infixes(cs.skill).times do %>
+ <%= render "characters/skills/infix_slot" do %>
+ <%# TODO: Don't load all into memory %>
+ <% infixable_items = @character.items.select {|i| i.infixable?(cs.skill)} %>
+ <% if infixable_items.any? %>
+ <%= form_with url: character_item_infixes_path, class: "w-full" do |f| %>
+ <div class="flex space-x-1">
+ <div class="flex-grow">
+ <%= f.select :item_id, infixable_items.map { |i| [i.name, i.id]}, {}, class: "w-full" %>
+ <%= f.hidden_field :skill_id, value: cs.skill.id %>
+ </div>
+ <div>
+ <%= f.submit "Infix" %>
+ </div>
+ </div>
+ <% end %>
+ <% else %>
+ <div class="text-gray-500">No omens to infix.</div>
+ <% end %>
+ <% end %>
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+</div>
diff --git a/app/views/characters/spells/index.html.erb b/app/views/characters/spells/index.html.erb
new file mode 100644
index 0000000..5f415a3
--- /dev/null
+++ b/app/views/characters/spells/index.html.erb
@@ -0,0 +1,10 @@
+<h2 class="text-3xl mb-2">Spells</h2>
+<div data-controller="activity-select">
+ <%= form_with url: start_activity_path, method: :post do |f| %>
+ <%= f.select :id, @spell_activities.map { |a| [a.name, a.id] }, {},
+ { data: { activity_select_target: "select", action: "activity-select#load" } } %>
+ <%= f.number_field :actions, value: 1, size: 5, min: 1, max: 2_000_000_000 %>
+ <%= f.submit "Cast" %>
+ <% end %>
+ <div data-activity-select-target="output" class="my-1"></div>
+</div>
diff --git a/config/routes.rb b/config/routes.rb
index fa6d748..fefd12f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -27,11 +27,15 @@ Rails.application.routes.draw do
post "/combat_styles", to: "characters#set_combat_styles"
scope module: :characters do
get "/rankings", to: "rankings#index"
+ get :bestiary, to: "bestiary#index"
post "/items/unequip/:slot", to: "items#unequip", as: :item_unequip
+ resources :item_infixes, only: [:create, :destroy]
resources :items, only: [:index] do
post "/equip", to: "items#equip"
post "/use", to: "items#use"
end
+ resources :skills, only: [:index]
+ resources :spells, only: [:index]
resources :titles, only: [:index] do
post "/activate", to: "titles#activate"
end
diff --git a/data/activities.yml b/data/activities.yml
index 3d3dc7b..8f2231a 100644
--- a/data/activities.yml
+++ b/data/activities.yml
@@ -251,6 +251,69 @@ construct_spicebench_level2:
- type: "hearth_amenity"
gid: "spicebench"
level: 2
+construct_binding_array_level1:
+ name: "Construct Binding Array Level 1"
+ description: "Build a level 1 binding array."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "foundation"
+ level: 1
+ duration:
+ base: 1000
+ cost:
+ - type: "item"
+ gid: "stone"
+ quantity: 200
+ - type: "item"
+ gid: "wood"
+ quantity: 400
+ - type: "item"
+ gid: "iron_ingot"
+ quantity: 4
+ - type: "item"
+ gid: "red_beryl"
+ quantity: 2
+ - type: "item"
+ gid: "tourmaline"
+ quantity: 2
+ results:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 1
+construct_binding_array_level2:
+ name: "Construct Binding Array Level 2"
+ description: "Upgrade your binding array to level 2."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 1
+ duration:
+ base: 3600
+ cost:
+ - type: "item"
+ gid: "stone"
+ quantity: 200
+ - type: "item"
+ gid: "wood"
+ quantity: 300
+ - type: "item"
+ gid: "red_beryl"
+ quantity: 2
+ - type: "item"
+ gid: "tourmaline"
+ quantity: 2
+ - type: "item"
+ gid: "paraiba_tourmaline"
+ quantity: 1
+ - type: "item"
+ gid: "yellow_beryl"
+ quantity: 1
+ results:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 2
plant_mudtub_seed:
name: "Plant Mudtub Seed"
description: "Plant a mudtub seed."
@@ -259,8 +322,13 @@ plant_mudtub_seed:
- type: "hearth_amenity"
gid: "loamspire"
level: 1
+ - type: "skill"
+ gid: "magiculture"
+ level: 1
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 120
+ base: 60
scaling:
- type: "skill"
gid: "magiculture"
@@ -282,8 +350,13 @@ harvest_mudtub:
- type: "hearth_amenity"
gid: "loamspire"
level: 1
+ - type: "skill"
+ gid: "magiculture"
+ level: 1
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 120
+ base: 60
scaling:
- type: "skill"
gid: "magiculture"
@@ -311,8 +384,10 @@ plant_midoras_seed:
- type: "skill"
gid: "magiculture"
level: 2
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 130
+ base: 70
scaling:
- type: "skill"
gid: "magiculture"
@@ -337,8 +412,10 @@ harvest_midoras:
- type: "skill"
gid: "magiculture"
level: 2
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 130
+ base: 70
scaling:
- type: "skill"
gid: "magiculture"
@@ -366,8 +443,10 @@ plant_templis_seed:
- type: "skill"
gid: "magiculture"
level: 4
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 140
+ base: 80
scaling:
- type: "skill"
gid: "magiculture"
@@ -392,8 +471,10 @@ harvest_templis:
- type: "skill"
gid: "magiculture"
level: 4
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 140
+ base: 80
scaling:
- type: "skill"
gid: "magiculture"
@@ -421,8 +502,10 @@ plant_enzon_seed:
- type: "skill"
gid: "magiculture"
level: 7
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 150
+ base: 90
scaling:
- type: "skill"
gid: "magiculture"
@@ -447,8 +530,10 @@ harvest_enzon:
- type: "skill"
gid: "magiculture"
level: 7
+ - type: "equipment"
+ tag: "spade"
duration:
- base: 150
+ base: 90
scaling:
- type: "skill"
gid: "magiculture"
@@ -473,6 +558,9 @@ craft_crude_iron_ingot:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 50
minimum: 35
@@ -501,6 +589,9 @@ craft_iron_ingot:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 60
minimum: 35
@@ -595,7 +686,7 @@ craft_purify_crude_iron_ingot:
gid: "forge"
level: 1
duration:
- base: 105
+ base: 60
minimum: 35
scaling:
- type: "skill"
@@ -626,7 +717,7 @@ craft_purify_iron_ingot:
gid: "otherforge"
level: 5
duration:
- base: 115
+ base: 70
minimum: 35
scaling:
- type: "skill"
@@ -653,6 +744,9 @@ craft_iron_lockpicks:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 50
minimum: 35
@@ -681,6 +775,9 @@ craft_iron_dagger:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 50
minimum: 35
@@ -771,6 +868,9 @@ craft_iron_buckler:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 100
minimum: 35
@@ -1049,7 +1149,7 @@ craft_arcanite_longsword:
level: 2
- type: "skill"
gid: "otherforge"
- level: 15
+ level: 17
duration:
base: 215
minimum: 35
@@ -1329,6 +1429,9 @@ craft_iron_pickaxe:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 75
minimum: 35
@@ -1360,6 +1463,9 @@ craft_iron_axe:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 75
minimum: 35
@@ -1391,6 +1497,9 @@ craft_iron_spade:
- type: "hearth_amenity"
gid: "forge"
level: 1
+ - type: "skill"
+ gid: "otherforge"
+ level: 1
duration:
base: 75
minimum: 35
@@ -1567,6 +1676,9 @@ craft_mending_salve:
- type: "hearth_amenity"
gid: "laboratory"
level: 1
+ - type: "skill"
+ gid: "fluxseethe"
+ level: 1
duration:
base: 90
minimum: 35
@@ -1879,8 +1991,14 @@ planequarry_floret_mines:
description: "Planequarry at the Floret Mines."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "planequarry"
+ level: 1
+ - type: "equipment"
+ tag: "pickaxe"
duration:
- base: 130
+ base: 70
minimum: 35
scaling:
- type: "skill"
@@ -1912,7 +2030,7 @@ planequarry_floret_mines:
score: 0.996
xp:
- gid: "planequarry"
- value: 25
+ value: 18
- type: "item"
chance: 0.02
table:
@@ -1947,8 +2065,10 @@ planequarry_deepshaft:
- type: "skill"
gid: "planequarry"
level: 10
+ - type: "equipment"
+ tag: "pickaxe"
duration:
- base: 160
+ base: 80
minimum: 35
scaling:
- type: "skill"
@@ -1980,12 +2100,12 @@ planequarry_deepshaft:
score: 0.90
xp:
- gid: "planequarry"
- value: 25
+ value: 18
- gid: "gaian_ore"
score: 0.995
xp:
- gid: "planequarry"
- value: 50
+ value: 25
- type: "item"
chance: 0.03
table:
@@ -2016,8 +2136,14 @@ planequarry_brine_trench:
description: "Planequarry in the south Floret brine trench."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "planequarry"
+ level: 1
+ - type: "equipment"
+ tag: "pickaxe"
duration:
- base: 60
+ base: 50
minimum: 25
scaling:
- type: "skill"
@@ -2045,6 +2171,10 @@ beastslay_killing_fields:
description: "Hunt monsters in the Killing Fields."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "beastslay"
+ level: 1
duration:
base: 60
minimum: 35
@@ -2069,6 +2199,10 @@ beastslay_hopegraves:
description: "Hunt monsters in the Hopegraves."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "beastslay"
+ level: 1
duration:
base: 70
minimum: 35
@@ -2091,8 +2225,14 @@ wealdreap_twil_woods:
description: "Wealdreap within Twil Woods."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "wealdreap"
+ level: 1
+ - type: "equipment"
+ tag: "axe"
duration:
- base: 120
+ base: 60
minimum: 35
scaling:
- type: "skill"
@@ -2160,8 +2300,10 @@ wealdreap_twil_grove:
- type: "skill"
gid: "wealdreap"
level: 10
+ - type: "equipment"
+ tag: "axe"
duration:
- base: 150
+ base: 90
minimum: 35
scaling:
- type: "skill"
@@ -2226,6 +2368,12 @@ manatrawl_sor_well:
description: "Manatrawl within Sor Well."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "manatrawl"
+ level: 1
+ - type: "equipment"
+ tag: "aethermesh"
duration:
base: 60
minimum: 35
@@ -2236,9 +2384,6 @@ manatrawl_sor_well:
- type: "stat"
gid: "manatrawl_speed"
scale_value: 1
- requirements:
- - type: "equipment"
- gid: "aethermesh"
results:
- type: "item"
chance: 1
@@ -2267,6 +2412,12 @@ manatrawl_sor_well_depths:
description: "Manatrawl deep within Sor Well."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "manatrawl"
+ level: 7
+ - type: "equipment"
+ tag: "aethermesh"
duration:
base: 70
minimum: 35
@@ -2277,12 +2428,6 @@ manatrawl_sor_well_depths:
- type: "stat"
gid: "manatrawl_speed"
scale_value: 1
- requirements:
- - type: "skill"
- gid: "manatrawl"
- level: 7
- - type: "equipment"
- gid: "aethermesh"
results:
- type: "item"
chance: 1
@@ -2311,6 +2456,12 @@ synthsever_rusted_lockbox:
name: "Unlock Rusted Lockbox"
description: "Unseal the lock on a rusted lockbox."
whatnot:
+ requirements:
+ - type: "equipment"
+ gid: "lockpicks"
+ - type: "skill"
+ gid: "synthsever"
+ level: 1
duration:
base: 180
minimum: 35
@@ -2318,9 +2469,6 @@ synthsever_rusted_lockbox:
- type: "skill"
gid: "synthsever"
scale_value: 1
- requirements:
- - type: "equipment"
- gid: "iron_lockpicks"
cost:
- type: "item"
gid: "rusted_lockbox"
@@ -2370,6 +2518,10 @@ wildscour_crumbling_ruins:
description: "Wildscour within the crumbling ruins."
location: "floret_region"
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "wildscour"
+ level: 1
duration:
base: 60
minimum: 35
@@ -2407,6 +2559,11 @@ wildscour_crumbling_ruins:
xp:
- gid: "wildscour"
value: 4
+ - gid: "simple_spellpage"
+ score: 0.99
+ xp:
+ - gid: "wildscour"
+ value: 15
- gid: "disturbing_doodad"
score: 0.998
xp:
@@ -2420,6 +2577,9 @@ craft_gem_dust_from_red_beryl:
- type: "hearth_amenity"
gid: "laboratory"
level: 1
+ - type: "skill"
+ gid: "fluxseethe"
+ level: 1
duration:
base: 60
minimum: 15
@@ -2448,6 +2608,9 @@ craft_gem_dust_from_tourmaline:
- type: "hearth_amenity"
gid: "laboratory"
level: 1
+ - type: "skill"
+ gid: "fluxseethe"
+ level: 1
duration:
base: 60
minimum: 15
@@ -2510,6 +2673,9 @@ craft_mudtub_mash:
- type: "hearth_amenity"
gid: "spicebench"
level: 1
+ - type: "skill"
+ gid: "spicework"
+ level: 1
duration:
base: 60
minimum: 35
@@ -2633,6 +2799,10 @@ beastslay_leviathan_floret_region:
name: "Hunt a Leviathan in the Floret Region"
description: "You are hunting down a leviathan ravaging the Floret Region."
whatnot:
+ requirements:
+ - type: "skill"
+ gid: "beastslay"
+ level: 1
duration:
base: 80
minimum: 40
@@ -2647,3 +2817,369 @@ beastslay_leviathan_floret_region:
- type: "monster_spawn"
location: "floret_region"
chance: 1
+craft_faint_mana:
+ name: "Bind faint mana"
+ description: "Bind an omen of faint mana."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 1
+ - type: "skill"
+ gid: "omenbind"
+ level: 1
+ duration:
+ base: 60
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "omenbind"
+ scale_value: 1
+ - type: "stat"
+ gid: "omenbind_speed"
+ scale_value: 1
+ cost:
+ - type: "item"
+ gid: "vestige"
+ quantity: 5
+ - type: "item"
+ gid: "arcane_dust"
+ quantity: 10
+ results:
+ - type: "item"
+ gid: "faint_mana"
+ xp:
+ - gid: "omenbind"
+ value: 10
+craft_faint_forging_haste:
+ name: "Bind faint forging haste"
+ description: "Bind an omen of faint forging haste."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 1
+ - type: "skill"
+ gid: "omenbind"
+ level: 5
+ duration:
+ base: 75
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "omenbind"
+ scale_value: 1
+ - type: "stat"
+ gid: "omenbind_speed"
+ scale_value: 1
+ cost:
+ - type: "item"
+ gid: "vestige"
+ quantity: 10
+ - type: "item"
+ gid: "iron_ingot"
+ quantity: 1
+ results:
+ - type: "item"
+ gid: "faint_forging_haste"
+ xp:
+ - gid: "omenbind"
+ value: 20
+craft_faint_seething_haste:
+ name: "Bind faint seething haste"
+ description: "Bind an omen of faint seething haste."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 1
+ - type: "skill"
+ gid: "omenbind"
+ level: 5
+ duration:
+ base: 75
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "omenbind"
+ scale_value: 1
+ - type: "stat"
+ gid: "omenbind_speed"
+ scale_value: 1
+ cost:
+ - type: "item"
+ gid: "vestige"
+ quantity: 10
+ - type: "item"
+ gid: "burstshroom"
+ quantity: 10
+ results:
+ - type: "item"
+ gid: "faint_seething_haste"
+ xp:
+ - gid: "omenbind"
+ value: 20
+craft_faint_working_haste:
+ name: "Bind faint working haste"
+ description: "Bind an omen of faint working haste."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 1
+ - type: "skill"
+ gid: "omenbind"
+ level: 5
+ duration:
+ base: 75
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "omenbind"
+ scale_value: 1
+ - type: "stat"
+ gid: "omenbind_speed"
+ scale_value: 1
+ cost:
+ - type: "item"
+ gid: "vestige"
+ quantity: 10
+ - type: "item"
+ gid: "salt"
+ quantity: 20
+ results:
+ - type: "item"
+ gid: "faint_working_haste"
+ xp:
+ - gid: "omenbind"
+ value: 20
+craft_minor_mana:
+ name: "Bind minor mana"
+ description: "Bind an omen of minor mana."
+ whatnot:
+ requirements:
+ - type: "hearth_amenity"
+ gid: "binding_array"
+ level: 2
+ - type: "skill"
+ gid: "omenbind"
+ level: 10
+ duration:
+ base: 90
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "omenbind"
+ scale_value: 1
+ - type: "stat"
+ gid: "omenbind_speed"
+ scale_value: 1
+ cost:
+ - type: "item"
+ gid: "faint_mana"
+ quantity: 2
+ - type: "item"
+ gid: "vestige"
+ quantity: 10
+ - type: "item"
+ gid: "shimmering_essence"
+ quantity: 1
+ results:
+ - type: "item"
+ gid: "minor_mana"
+ xp:
+ - gid: "omenbind"
+ value: 30
+havencast_light:
+ name: "Cast Light"
+ description: "Risk a little light."
+ whatnot:
+ tags:
+ - "spell"
+ - "cantrip"
+ requirements:
+ - type: "skill"
+ gid: "havencast"
+ level: 1
+ - type: "stat"
+ gid: "mana"
+ value: 1
+ duration:
+ base: 62
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "havencast"
+ scale_value: 1
+ results:
+ - type: "condition"
+ gid: "light"
+ duration: 3600 # 1 Hour
+ message: "A ball of light glows before you."
+ - type: "xp"
+ gid: "havencast"
+ base: 3
+havencast_dazzle:
+ name: "Cast Dazzle"
+ description: "Cast the Dazzle cantrip."
+ whatnot:
+ tags:
+ - "spell"
+ - "cantrip"
+ duration:
+ base: 62
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "havencast"
+ scale_value: 1
+ requirements:
+ - type: "skill"
+ gid: "havencast"
+ level: 2
+ - type: "stat"
+ gid: "mana"
+ value: 1
+ results:
+ - type: "condition"
+ gid: "dazzle"
+ duration: 600 # 10 minutes
+ message: "Sparkling lights distract your enemies."
+ - type: "xp"
+ gid: "havencast"
+ base: 5
+havencast_decipher_simple_magicscript:
+ name: "Cast Decipher Simple Magicscript"
+ description: "Cast the Decipher Simple Magicscript cantrip."
+ whatnot:
+ tags:
+ - "spell"
+ - "cantrip"
+ duration:
+ base: 64
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "havencast"
+ scale_value: 1
+ requirements:
+ - type: "skill"
+ gid: "havencast"
+ level: 3
+ - type: "stat"
+ gid: "mana"
+ value: 1
+ cost:
+ - type: "item"
+ gid: "simple_spellpage"
+ quantity: 1
+ results:
+ - type: "xp"
+ gid: "havencast"
+ base: 10
+ - type: "activity"
+ chance: 1
+ table:
+ - gid: "havencast_stinging_rays"
+ score: 0
+ - gid: "havencast_flame_whirl"
+ score: .50
+havencast_enchant_apprentice_wand:
+ name: "Cast Enchant Apprentice Wand"
+ description: "Cast the Enchant Apprentice Wand spell."
+ whatnot:
+ tags:
+ - "spell"
+ - "cantrip"
+ duration:
+ base: 70
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "havencast"
+ scale_value: 1
+ requirements:
+ - type: "skill"
+ gid: "havencast"
+ level: 5
+ - type: "stat"
+ gid: "mana"
+ value: 1
+ cost:
+ - type: "item"
+ gid: "wood"
+ quantity: 1
+ - type: "item"
+ gid: "arcane_dust"
+ quantity: 10
+ - type: "item"
+ gid: "shimmering_essence"
+ quantity: 1
+ results:
+ - type: "item"
+ gid: "apprentice_wand"
+ - type: "xp"
+ gid: "havencast"
+ base: 22
+havencast_stinging_rays:
+ name: "Cast Stinging Rays"
+ description: "Cast the Stinging Rays spell."
+ innate: false
+ whatnot:
+ tags:
+ - "spell"
+ duration:
+ base: 70
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "havencast"
+ scale_value: 1
+ requirements:
+ - type: "skill"
+ gid: "havencast"
+ level: 5
+ - type: "stat"
+ gid: "mana"
+ value: 1
+ - type: "equipment"
+ tag: "focus"
+ results:
+ - type: "condition"
+ gid: "stinging_rays"
+ duration: 600 # 10 minutes
+ message: "Beams of arcane energy appear in the air around you."
+ - type: "xp"
+ gid: "havencast"
+ base: 11
+havencast_flame_whirl:
+ name: "Cast Flame Whirl"
+ description: "Cast the Flame Whirl spell."
+ innate: false
+ whatnot:
+ tags:
+ - "spell"
+ duration:
+ base: 80
+ minimum: 35
+ scaling:
+ - type: "skill"
+ gid: "havencast"
+ scale_value: 1
+ requirements:
+ - type: "skill"
+ gid: "havencast"
+ level: 10
+ - type: "stat"
+ gid: "mana"
+ value: 2
+ - type: "equipment"
+ tag: "focus"
+ results:
+ - type: "condition"
+ gid: "flame_whirl"
+ duration: 600 # 10 minutes
+ message: "A thin disc of fire encircles you."
+ - type: "xp"
+ gid: "havencast"
+ base: 15
diff --git a/data/conditions.yml b/data/conditions.yml
index b691254..4cb71b0 100644
--- a/data/conditions.yml
+++ b/data/conditions.yml
@@ -96,3 +96,41 @@ mercuria:
- type: "stat_change"
gid: "evasion"
modifier: 1
+light:
+ name: "light"
+ description: "Allows seeing in dark areas."
+ whatnot:
+ effects:
+ - type: "special"
+ gid: "darkvision"
+ - type: "stat_change"
+ gid: "necrotic_resistance"
+ modifier: 1
+dazzle:
+ name: "dazzle"
+ description: "Reduces enemies' accuracy."
+ whatnot:
+ effects:
+ - type: "enemy_stat_change"
+ gid: "accuracy"
+ modifier: -1
+stinging_rays:
+ name: "stinging rays"
+ description: "Does arcane damage over time."
+ whatnot:
+ effects:
+ - type: "dot"
+ gid: "arcane"
+ min: 1
+ max: 3
+ message: "Stinging rays spring from the air around you and hit your enemy."
+flame_whirl:
+ name: "flame whirl"
+ description: "Does fire damage over time."
+ whatnot:
+ effects:
+ - type: "dot"
+ gid: "fire"
+ min: 1
+ max: 8
+ message: "A ring of flame spins about you, licking at your enemy."
diff --git a/data/hearth_amenities.yml b/data/hearth_amenities.yml
index 6ab8021..e58aa1c 100644
--- a/data/hearth_amenities.yml
+++ b/data/hearth_amenities.yml
@@ -85,3 +85,14 @@ spicebench:
level: 2
gid: "spicework_speed"
modifier: 4
+binding_array:
+ name: "Binding Array"
+ description: >-
+ A apparatus for binding magical energy into a physical form, creating an omen.
+ whatnot:
+ construct_activities:
+ - level: 1
+ gid: "construct_binding_array_level1"
+ - level: 2
+ gid: "construct_binding_array_level2"
+
diff --git a/data/items.yml b/data/items.yml
index 0aac500..3c07863 100644
--- a/data/items.yml
+++ b/data/items.yml
@@ -720,6 +720,11 @@ iron_lockpicks:
whatnot:
tags:
- "tool"
+ - "lockpicks"
+ equip_effects:
+ - type: "stat_change"
+ gid: "synthsever_speed"
+ modifier: 10
equip_slots:
- "mainhand"
stone_pickaxe:
@@ -728,12 +733,13 @@ stone_pickaxe:
whatnot:
tags:
- "tool"
+ - "pickaxe"
equip_slots:
- "mainhand"
equip_effects:
- type: "stat_change"
gid: "planequarry_speed"
- modifier: 60
+ modifier: 2
- type: "stat_change"
gid: "accuracy"
modifier: 0
@@ -746,12 +752,13 @@ stone_axe:
whatnot:
tags:
- "tool"
+ - "axe"
equip_slots:
- "mainhand"
equip_effects:
- type: "stat_change"
gid: "wealdreap_speed"
- modifier: 60
+ modifier: 2
- type: "stat_change"
gid: "accuracy"
modifier: 0
@@ -764,12 +771,13 @@ stone_spade:
whatnot:
tags:
- "tool"
+ - "spade"
equip_slots:
- "mainhand"
equip_effects:
- type: "stat_change"
gid: "magiculture_speed"
- modifier: 60
+ modifier: 2
- type: "stat_change"
gid: "accuracy"
modifier: 0
@@ -782,12 +790,13 @@ iron_pickaxe:
whatnot:
tags:
- "tool"
+ - "pickaxe"
equip_slots:
- "mainhand"
equip_effects:
- type: "stat_change"
gid: "planequarry_speed"
- modifier: 80
+ modifier: 10
- type: "stat_change"
gid: "accuracy"
modifier: 1
@@ -800,12 +809,13 @@ iron_axe:
whatnot:
tags:
- "tool"
+ - "axe"
equip_slots:
- "mainhand"
equip_effects:
- type: "stat_change"
gid: "wealdreap_speed"
- modifier: 80
+ modifier: 10
- type: "stat_change"
gid: "accuracy"
modifier: 1
@@ -818,12 +828,13 @@ iron_spade:
whatnot:
tags:
- "tool"
+ - "spade"
equip_slots:
- "mainhand"
equip_effects:
- type: "stat_change"
gid: "magiculture_speed"
- modifier: 80
+ modifier: 10
- type: "stat_change"
gid: "accuracy"
modifier: 1
@@ -836,6 +847,7 @@ arcanite_pickaxe:
whatnot:
tags:
- "tool"
+ - "pickaxe"
equip_slots:
- "mainhand"
equip_requirements:
@@ -845,7 +857,7 @@ arcanite_pickaxe:
equip_effects:
- type: "stat_change"
gid: "planequarry_speed"
- modifier: 90
+ modifier: 20
- type: "stat_change"
gid: "accuracy"
modifier: 2
@@ -858,6 +870,7 @@ arcanite_axe:
whatnot:
tags:
- "tool"
+ - "axe"
equip_slots:
- "mainhand"
equip_requirements:
@@ -867,7 +880,7 @@ arcanite_axe:
equip_effects:
- type: "stat_change"
gid: "wealdreap_speed"
- modifier: 90
+ modifier: 20
- type: "stat_change"
gid: "accuracy"
modifier: 2
@@ -880,6 +893,7 @@ arcanite_spade:
whatnot:
tags:
- "tool"
+ - "spade"
equip_slots:
- "mainhand"
equip_requirements:
@@ -889,19 +903,33 @@ arcanite_spade:
equip_effects:
- type: "stat_change"
gid: "magiculture_speed"
- modifier: 90
+ modifier: 20
- type: "stat_change"
gid: "accuracy"
modifier: 2
- type: "stat_change"
gid: "power"
modifier: 3
+apprentice_wand:
+ name: "apprentice wand"
+ description: "A simple magic wand."
+ whatnot:
+ tags:
+ - "tool"
+ - "focus"
+ equip_slots:
+ - "mainhand"
+ equip_effects:
+ - type: "stat_change"
+ gid: "havencast_speed"
+ modifier: 2
aethermesh:
name: "aethermesh"
description: "A tool for manatrawl."
whatnot:
tags:
- "tool"
+ - "aethermesh"
equip_slots:
- "mainhand"
quarrying_draught:
@@ -1094,3 +1122,69 @@ balgoloth_claw:
gid: "pierce"
min: 1
max: 6
+faint_mana:
+ name: "faint mana"
+ description: "A weak omen that provides a very small amount of magical energy."
+ whatnot:
+ tags:
+ - "omen"
+ infix_skills:
+ - gid: "havencast"
+ infix_effects:
+ - type: "stat_change"
+ gid: "mana"
+ modifier: 1
+minor_mana:
+ name: "minor mana"
+ description: "A weak omen that provides a small amount of magical energy."
+ whatnot:
+ tags:
+ - "omen"
+ infix_skills:
+ - gid: "havencast"
+ infix_effects:
+ - type: "stat_change"
+ gid: "mana"
+ modifier: 2
+faint_forging_haste:
+ name: "faint forging haste"
+ description: "A very weak omen that grants a very small increase to otherforge speed."
+ whatnot:
+ tags:
+ - "omen"
+ infix_skills:
+ - gid: "otherforge"
+ infix_effects:
+ - type: "stat_change"
+ gid: "otherforge_speed"
+ modifier: 2
+faint_seething_haste:
+ name: "faint seething haste"
+ description: "A very weak omen that grants a very small increase to fluxseethe speed."
+ whatnot:
+ tags:
+ - "omen"
+ infix_skills:
+ - gid: "fluxseethe"
+ infix_effects:
+ - type: "stat_change"
+ gid: "fluxseethe_speed"
+ modifier: 2
+faint_working_haste:
+ name: "faint working haste"
+ description: "A very weak omen that grants a very small increase to spicework speed."
+ whatnot:
+ tags:
+ - "omen"
+ infix_skills:
+ - gid: "spicework"
+ infix_effects:
+ - type: "stat_change"
+ gid: "spicework_speed"
+ modifier: 2
+simple_spellpage:
+ name: "simple spellpage"
+ description: "An old piece of parchment covered in magical script. Deciphering it can yield knowledge of a spell."
+ whatnot:
+ tags:
+ - "material"
diff --git a/data/monsters.yml b/data/monsters.yml
index 26a6f90..4ce9434 100644
--- a/data/monsters.yml
+++ b/data/monsters.yml
@@ -119,6 +119,8 @@ grinpad:
- gid: "vestige"
score: 0.85
max_quantity: 10
+ - gid: "simple_spellpage"
+ score: 0.99
- gid: "rusted_lockbox"
score: 0.995
- gid: "warm_diadem"
diff --git a/data/skills.yml b/data/skills.yml
index 68de599..0bbd63a 100644
--- a/data/skills.yml
+++ b/data/skills.yml
@@ -52,7 +52,7 @@ omenbind:
ago developed a technique for harnessing magical power and spells within physical objects like crystalline
batteries and gem-laden crowns. These power-imbued objects are called omens. The practice of omenbinding flourishes
to this day, and indeed the world would not be the same without its providing for saving and storing up power to be
- used at a later time, or in greater mass. The most powerful omenbinders have been known to bind energy even within
+ used at a later time, or in greater mass. The most powerful omenbinding have been known to bind energy even within
flesh, though this practice is as risky as it is dangerous.
otherforge:
name: "Otherforge"
diff --git a/data/titles.yml b/data/titles.yml
index a8c228a..23ff009 100644
--- a/data/titles.yml
+++ b/data/titles.yml
@@ -8,3 +8,17 @@ aspirant:
name: "Aspirant"
sentinel:
name: "Sentinel"
+spiteful:
+ name: "Spiteful"
+hateful:
+ name: "Hateful"
+vicious:
+ name: "Vicious"
+slayer:
+ name: "Slayer"
+butcher:
+ name: "Butcher"
+slaughterer:
+ name: "Slaughterer"
+massacrer:
+ name: "Massacrer"
diff --git a/db/migrate/20210614004827_create_item_infixes.rb b/db/migrate/20210614004827_create_item_infixes.rb
new file mode 100644
index 0000000..5b49892
--- /dev/null
+++ b/db/migrate/20210614004827_create_item_infixes.rb
@@ -0,0 +1,11 @@
+class CreateItemInfixes < ActiveRecord::Migration[6.1]
+ def change
+ create_table :item_infixes do |t|
+ t.references :character, null: false, foreign_key: true
+ t.references :item, null: false, foreign_key: true
+ t.references :skill, null: false, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20210616014044_create_monster_kills.rb b/db/migrate/20210616014044_create_monster_kills.rb
new file mode 100644
index 0000000..b61908d
--- /dev/null
+++ b/db/migrate/20210616014044_create_monster_kills.rb
@@ -0,0 +1,11 @@
+class CreateMonsterKills < ActiveRecord::Migration[6.1]
+ def change
+ create_table :monster_kills do |t|
+ t.references :monster, null: false, foreign_key: true
+ t.references :character, null: false, foreign_key: true
+ t.bigint :quantity
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 75ebad0..6c503fd 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_06_10_010413) do
+ActiveRecord::Schema.define(version: 2021_06_16_014044) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -156,6 +156,17 @@ ActiveRecord::Schema.define(version: 2021_06_10_010413) do
t.index ["character_id"], name: "index_hearths_on_character_id"
end
+ create_table "item_infixes", force: :cascade do |t|
+ t.bigint "character_id", null: false
+ t.bigint "item_id", null: false
+ t.bigint "skill_id", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["character_id"], name: "index_item_infixes_on_character_id"
+ t.index ["item_id"], name: "index_item_infixes_on_item_id"
+ t.index ["skill_id"], name: "index_item_infixes_on_skill_id"
+ end
+
create_table "items", force: :cascade do |t|
t.string "gid"
t.string "name"
@@ -196,6 +207,16 @@ ActiveRecord::Schema.define(version: 2021_06_10_010413) do
t.index ["sender_id"], name: "index_messages_on_sender_id"
end
+ create_table "monster_kills", force: :cascade do |t|
+ t.bigint "monster_id", null: false
+ t.bigint "character_id", null: false
+ t.bigint "quantity"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["character_id"], name: "index_monster_kills_on_character_id"
+ t.index ["monster_id"], name: "index_monster_kills_on_monster_id"
+ end
+
create_table "monster_spawn_combats", force: :cascade do |t|
t.bigint "monster_spawn_id", null: false
t.bigint "character_id", null: false
@@ -308,10 +329,15 @@ ActiveRecord::Schema.define(version: 2021_06_10_010413) do
add_foreign_key "hearth_plantings", "hearths"
add_foreign_key "hearth_plantings", "items"
add_foreign_key "hearths", "characters"
+ add_foreign_key "item_infixes", "characters"
+ add_foreign_key "item_infixes", "items"
+ add_foreign_key "item_infixes", "skills"
add_foreign_key "learned_activities", "activities"
add_foreign_key "learned_activities", "characters"
add_foreign_key "messages", "characters", column: "recipient_id"
add_foreign_key "messages", "characters", column: "sender_id"
+ add_foreign_key "monster_kills", "characters"
+ add_foreign_key "monster_kills", "monsters"
add_foreign_key "monster_spawn_combats", "characters"
add_foreign_key "monster_spawn_combats", "monster_spawns"
add_foreign_key "monster_spawns", "locations"
diff --git a/test/fixtures/item_infixes.yml b/test/fixtures/item_infixes.yml
new file mode 100644
index 0000000..535831c
--- /dev/null
+++ b/test/fixtures/item_infixes.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ character: one
+ item: one
+ skill: one
+
+two:
+ character: two
+ item: two
+ skill: two
diff --git a/test/fixtures/monster_kills.yml b/test/fixtures/monster_kills.yml
new file mode 100644
index 0000000..776b079
--- /dev/null
+++ b/test/fixtures/monster_kills.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ monster: one
+ character: one
+ quantity:
+
+two:
+ monster: two
+ character: two
+ quantity:
diff --git a/test/models/item_infix_test.rb b/test/models/item_infix_test.rb
new file mode 100644
index 0000000..a6f63a3
--- /dev/null
+++ b/test/models/item_infix_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class ItemInfixTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/models/monster_kill_test.rb b/test/models/monster_kill_test.rb
new file mode 100644
index 0000000..aef80ac
--- /dev/null
+++ b/test/models/monster_kill_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class MonsterKillTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end