From 18c2378d06d5f9323e50eead92b3cf7dc61917b5 Mon Sep 17 00:00:00 2001 From: David Gay Date: Sun, 13 Jun 2021 21:15:47 -0400 Subject: Don't allow characters to look at another character's items --- app/controllers/characters/items_controller.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'app/controllers') 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 -- cgit v1.2.3 From e78a3513632954cb53fc8c806158c1fc98357173 Mon Sep 17 00:00:00 2001 From: David Gay Date: Sun, 13 Jun 2021 21:29:52 -0400 Subject: ItemInfixes --- app/controllers/characters/rankings_controller.rb | 2 +- app/models/character.rb | 6 ++++++ app/models/item_infix.rb | 13 +++++++++++++ db/migrate/20210614004827_create_item_infixes.rb | 11 +++++++++++ test/fixtures/item_infixes.yml | 11 +++++++++++ test/models/item_infix_test.rb | 7 +++++++ 6 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 app/models/item_infix.rb create mode 100644 db/migrate/20210614004827_create_item_infixes.rb create mode 100644 test/fixtures/item_infixes.yml create mode 100644 test/models/item_infix_test.rb (limited to 'app/controllers') 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/models/character.rb b/app/models/character.rb index d386002..03bd510 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -9,6 +9,7 @@ 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 @@ -182,6 +183,11 @@ 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 add_skill_xp(skill, amount) skill = Skill.find_by_gid(skill) if skill.is_a? String Character.transaction do diff --git a/app/models/item_infix.rb b/app/models/item_infix.rb new file mode 100644 index 0000000..cc99ee7 --- /dev/null +++ b/app/models/item_infix.rb @@ -0,0 +1,13 @@ +class ItemInfix < ApplicationRecord + belongs_to :character + belongs_to :item + belongs_to :skill + + before_create :check_max_infixes + + 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/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/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/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 -- cgit v1.2.3 From c7c70670f9cbedf6ef12acd06ed8388319590d1e Mon Sep 17 00:00:00 2001 From: David Gay Date: Sun, 13 Jun 2021 21:31:40 -0400 Subject: Only be able to view your own character --- app/controllers/characters_controller.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/characters_controller.rb b/app/controllers/characters_controller.rb index 77e1a94..1a91988 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[:character_id]) + unless current_char == @character + flash[:alert] = "You can only manage your own character." + redirect_to character_path(@character) + end + end end -- cgit v1.2.3 From 03b8bfa53b02754d7c375690d92ed83c1cd661da Mon Sep 17 00:00:00 2001 From: David Gay Date: Sun, 13 Jun 2021 21:40:42 -0400 Subject: Fix wrong param name in new CharacterController `#set_character` --- app/controllers/characters/skills_controller.rb | 15 ++ app/controllers/characters_controller.rb | 2 +- app/views/characters/skills/index.html.erb | 176 ++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 app/controllers/characters/skills_controller.rb create mode 100644 app/views/characters/skills/index.html.erb (limited to 'app/controllers') 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_controller.rb b/app/controllers/characters_controller.rb index 1a91988..2eb906b 100644 --- a/app/controllers/characters_controller.rb +++ b/app/controllers/characters_controller.rb @@ -37,7 +37,7 @@ class CharactersController < ApplicationController end def set_character - @character = Character.find(params[:character_id]) + @character = Character.find(params[:id]) unless current_char == @character flash[:alert] = "You can only manage your own character." redirect_to character_path(@character) diff --git a/app/views/characters/skills/index.html.erb b/app/views/characters/skills/index.html.erb new file mode 100644 index 0000000..f13e611 --- /dev/null +++ b/app/views/characters/skills/index.html.erb @@ -0,0 +1,176 @@ +

Skills

+
+
    +
  • <%= link_to "Rankings", character_rankings_path(@character) %>
  • +
  • <%= link_to "Titles", character_titles_path(@character) %>
  • +
+
+ +

First entered the planes + <%= pluralize((Date.current - @character.created_at.to_date).to_i, "day") %> ago.

+ +

Learned <%= @character.learned_activities.count %> recipe(s) or technique(s).

+ +
+
+

Boons & Banes

+ <% if @character.active_states.any? %> +
    + <% @character.active_states.each do |state| %> +
      <%= state.condition.name %> (expires in <%= distance_of_time_in_words_to_now(state.expires_at)%>)
    + <% end %> +
+ <% else %> +

No boons or banes affect you.

+ <% end %> +
+
+ <% if @character == current_char %> +

Combat Styles

+ <%= form_with url: character_combat_styles_path(character_id: @character) do |f| %> + <%= f.label :offensive_style, "Offensive" %> + <%= f.select :offensive_style, Character.offensive_styles.keys.to_a, selected: @character.offensive_style %> + + <%= f.label :defensive_style, "Defensive" %> + <%= f.select :defensive_style, Character.defensive_styles.keys.to_a, selected: @character.defensive_style %> + + <%= f.submit "Set" %> + <% end %> + <% end %> +
+
+ + +
+
+
+

Combat Statistics

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Wounds<%= @character.wounds %> / <%= @character.max_wounds %>
Max HP<%= @character.max_hp %>
Speed<%= @character.speed %>
Accuracy<%= @character.accuracy(with_combat_style: true) %>
Power<%= @character.power(with_combat_style: true) %>
Evasion<%= @character.evasion(with_combat_style: true) %>
+
+
+

Resistances

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Slash<%= @character.resistance("slash")%>
Pierce<%= @character.resistance("pierce") %>
Bash<%= @character.resistance("bash") %>
Arcane<%= @character.resistance("arcane") %>
Fire<%= @character.resistance("fire") %>
Frost<%= @character.resistance("frost") %>
Lightning<%= @character.resistance("lightning") %>
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Acid<%= @character.resistance("acid") %>
Thunder<%= @character.resistance("thunder")%>
Radiant<%= @character.resistance("radiant") %>
Necrotic<%= @character.resistance("necrotic") %>
Poison<%= @character.resistance("poison") %>
Bleed<%= @character.resistance("bleed") %>
+
+
+
+
+
+ +
+

Skills

+ + + + + + + + + + + + <% @character.character_skills.ordered_by_skill_name.each do |cs| %> + + + + + + + <% end %> + +
SkillLevelXPTNLTotal XP
<%= cs.skill.name %><%= cs.level %><%= cs.xp_to_next_level %><%= cs.xp %>
+
+ +<% if @character == current_char %> + <%= link_to "Manage account", edit_user_registration_path, class: "text-sm" %> +<% end %> -- cgit v1.2.3 From 53c9df5feb8ad4ebef8f40840d29255a81edb6df Mon Sep 17 00:00:00 2001 From: David Gay Date: Tue, 15 Jun 2021 19:13:55 -0400 Subject: Reboot omens and allow infixing items (like omens) into skills --- app/controllers/characters/hearth_controller.rb | 3 + .../characters/item_infixes_controller.rb | 27 ++++ app/errors/item_infix_error.rb | 2 + app/models/character.rb | 19 +++ app/models/item.rb | 7 + app/views/characters/items/index.html.erb | 4 + app/views/characters/skills/_infix_slot.html.erb | 3 + app/views/characters/skills/index.html.erb | 59 ++++++-- config/routes.rb | 1 + data/activities.yml | 166 +-------------------- data/conditions.yml | 1 + data/items.yml | 30 ++-- 12 files changed, 127 insertions(+), 195 deletions(-) create mode 100644 app/controllers/characters/item_infixes_controller.rb create mode 100644 app/errors/item_infix_error.rb create mode 100644 app/views/characters/skills/_infix_slot.html.erb (limited to 'app/controllers') 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/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/models/character.rb b/app/models/character.rb index 03bd510..4ee1f71 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -188,6 +188,25 @@ class Character < ApplicationRecord 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 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/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/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 @@ +
+ <%= yield %> +
diff --git a/app/views/characters/skills/index.html.erb b/app/views/characters/skills/index.html.erb index fd53870..f79b06e 100644 --- a/app/views/characters/skills/index.html.erb +++ b/app/views/characters/skills/index.html.erb @@ -1,23 +1,56 @@

Skills

<% @character.character_skills.ordered_by_skill_name.each do |cs| %> -
-
-
-
- <%= cs.skill.name %> +
+
+
+
+
+ <%= cs.skill.name %> +
+
+ XP<%= cs.xp %> +
-
- XP<%= cs.xp %> +
+ <%= cs.level %>
-
- <%= cs.level %> -
-
-
-
+
+
+
+ <% @character.item_infixes.where(skill: cs.skill).each do |ii| %> + <%= render "characters/skills/infix_slot" do %> +
+ <%= ii.item.name %> +
+
+ <%= button_to "Remove", character_item_infix_path(id: ii.id), method: :delete %> +
+ <% 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| %> +
+
+ <%= f.select :item_id, infixable_items.map { |i| [i.name, i.id]}, {}, class: "w-full" %> + <%= f.hidden_field :skill_id, value: cs.skill.id %> +
+
+ <%= f.submit "Infix" %> +
+
+ <% end %> + <% else %> +
No items to infix.
+ <% end %> + <% end %> + <% end %>
<% end %> diff --git a/config/routes.rb b/config/routes.rb index 22b8299..51d9e6c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,6 +28,7 @@ Rails.application.routes.draw do scope module: :characters do get "/rankings", to: "rankings#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" diff --git a/data/activities.yml b/data/activities.yml index 525905a..a508781 100644 --- a/data/activities.yml +++ b/data/activities.yml @@ -305,7 +305,7 @@ construct_binding_array_level2: gid: "tourmaline" quantity: 2 - type: "item" - gid: "paraiba tourmaline" + gid: "paraiba_tourmaline" quantity: 1 - type: "item" gid: "yellow_beryl" @@ -2710,9 +2710,9 @@ beastslay_leviathan_floret_region: - type: "monster_spawn" location: "floret_region" chance: 1 -craft_faint_hope: - name: "Bind faint hope" - description: "Bind an omen of faint hope." +craft_faint_mana: + name: "Bind faint mana" + description: "Bind an omen of faint mana." whatnot: requirements: - type: "hearth_amenity" @@ -2737,163 +2737,7 @@ craft_faint_hope: quantity: 10 results: - type: "item" - gid: "faint_hope" + gid: "faint_mana" xp: - gid: "omenbind" value: 6 -craft_fleeting_glimpse: - name: "Bind fleeting glimpse" - description: "Bind a fleeting glimpse omen." - whatnot: - requirements: - - type: "hearth_amenity" - gid: "binding_array" - level: 1 - - type: "skill" - gid: "omenbind" - level: 3 - duration: - base: 65 - minimum: 35 - scaling: - - type: "skill" - gid: "omenbind" - scale_value: 1 - - type: "stat" - gid: "omenbind_speed" - scale_value: 1 - cost: - - type: "item" - gid: "vestige" - quantity: 8 - - type: "item" - gid: "shimmering_essence" - quantity: 1 - results: - - type: "item" - gid: "fleeting_glimpse" - xp: - - gid: "omenbind" - value: 12 -craft_bright_token: - name: "Bind bright token" - description: "Bind a bright token omen." - whatnot: - requirements: - - type: "hearth_amenity" - gid: "binding_array" - level: 1 - - type: "skill" - gid: "omenbind" - level: 6 - duration: - base: 70 - minimum: 35 - scaling: - - type: "skill" - gid: "omenbind" - scale_value: 1 - - type: "stat" - gid: "omenbind_speed" - scale_value: 1 - cost: - - type: "item" - gid: "vestige" - quantity: 12 - - type: "item" - gid: "gem_dust" - quantity: 1 - results: - - type: "item" - gid: "bright_token" - xp: - - gid: "omenbind" - value: 16 -craft_sign_of_solace: - name: "Bind sign of solace" - description: "Bind a sign of solace." - whatnot: - requirements: - - type: "hearth_amenity" - gid: "binding_array" - level: 2 - - type: "skill" - gid: "omenbind" - level: 10 - duration: - base: 80 - minimum: 35 - scaling: - - type: "skill" - gid: "omenbind" - scale_value: 1 - - type: "stat" - gid: "omenbind_speed" - scale_value: 1 - cost: - - type: "item" - gid: "vestige" - quantity: 25 - - type: "item" - gid: "claritas_flower" - quantity: 1 - results: - - type: "item" - gid: "sign_of_solace" - xp: - - gid: "omenbind" - value: 22 -havencast_light: - name: "Cast Light" - description: "Cast the Light cantrip." - whatnot: - tags: - - "cantrip" - duration: - base: 30 - minimum: 10 - scaling: - - type: "skill" - gid: "havencast" - scale_value: 1 - requirements: - - type: "skill" - gid: "havencast" - level: 1 - cost: - - type: "mana" - gid: "clear" - quantity: 1 - results: - - type: "condition" - gid: "light" - - type: "xp" - gid: "havencast" - base: 3 -havencast_dazzle: - name: "Cast Dazzle" - description: "Cast the Dazzle cantrip." - whatnot: - tags: - - "cantrip" - duration: - base: 32 - minimum: 10 - scaling: - - type: "skill" - gid: "havencast" - scale_value: 1 - requirements: - - type: "skill" - gid: "havencast" - level: 2 - cost: - - type: "mana" - gid: "clear" - quantity: 2 - results: - - type: "condition" - gid: "dazzle" - - type: "xp" - gid: "havencast" - base: 6 diff --git a/data/conditions.yml b/data/conditions.yml index 11dcede..3badf5b 100644 --- a/data/conditions.yml +++ b/data/conditions.yml @@ -106,6 +106,7 @@ light: dazzle: name: "dazzle" description: "Reduces enemies' accuracy." + whatnot: effects: - type: "enemy_stat_change" gid: "accuracy" diff --git a/data/items.yml b/data/items.yml index 1f0be2c..224aac6 100644 --- a/data/items.yml +++ b/data/items.yml @@ -1094,27 +1094,15 @@ balgoloth_claw: gid: "pierce" min: 1 max: 6 -faint_hope: - name: "faint hope" - description: "A weak omen made from compressed arcane energy." - whatnot: - tags: - - "omen" -fleeting_glimpse: - name: "fleeting glimpse" - description: "A weak omen that shows vague images in its ethereal surface." - whatnot: - tags: - - "omen" -bright_token: - name: "bright token" - description: "An omen with promising magical potential." - whatnot: - tags: - - "omen" -sign_of_solace: - name: "sign of solace" - description: "A sign that shines radiantly from a pinpoint at its center." +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 -- cgit v1.2.3 From d2a31e04d19796a600a932a3491e056fe2c89af2 Mon Sep 17 00:00:00 2001 From: David Gay Date: Tue, 15 Jun 2021 19:54:29 -0400 Subject: Basic spellcasting --- app/controllers/characters/spells_controller.rb | 5 ++++ app/lib/activity_processor.rb | 7 ++++++ app/models/character.rb | 6 ++++- app/models/concerns/has_costs_and_requirements.rb | 2 ++ app/models/item_infix.rb | 4 ++++ app/views/application/_navbar.html.erb | 3 +++ app/views/application/_results.html.erb | 2 ++ app/views/characters/skills/index.html.erb | 2 +- app/views/characters/spells/index.html.erb | 10 ++++++++ config/routes.rb | 1 + data/activities.yml | 29 +++++++++++++++++++++++ 11 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 app/controllers/characters/spells_controller.rb create mode 100644 app/views/characters/spells/index.html.erb (limited to 'app/controllers') diff --git a/app/controllers/characters/spells_controller.rb b/app/controllers/characters/spells_controller.rb new file mode 100644 index 0000000..9694856 --- /dev/null +++ b/app/controllers/characters/spells_controller.rb @@ -0,0 +1,5 @@ +class Characters::SpellsController < ApplicationController + def index + @spell_activities = Activity.where("gid like ?", "havencast_%").order(:name) + end +end diff --git a/app/lib/activity_processor.rb b/app/lib/activity_processor.rb index 4a06a98..9b03f55 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 diff --git a/app/models/character.rb b/app/models/character.rb index 4ee1f71..5ff067c 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -273,6 +273,9 @@ class Character < ApplicationRecord case requirement[:type] when "equipment" return false unless self.equipment_with_gid(requirement[:gid]) + 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" @@ -331,7 +334,8 @@ 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) diff --git a/app/models/concerns/has_costs_and_requirements.rb b/app/models/concerns/has_costs_and_requirements.rb index 34ff0f3..9f859f5 100644 --- a/app/models/concerns/has_costs_and_requirements.rb +++ b/app/models/concerns/has_costs_and_requirements.rb @@ -19,6 +19,8 @@ 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}" when "hearth_amenity" diff --git a/app/models/item_infix.rb b/app/models/item_infix.rb index cc99ee7..3167320 100644 --- a/app/models/item_infix.rb +++ b/app/models/item_infix.rb @@ -5,6 +5,10 @@ class ItemInfix < ApplicationRecord before_create :check_max_infixes + def effects + self.item.whatnot[:infix_effects] + end + private def check_max_infixes current_infixes = character.item_infixes.where(skill: skill) diff --git a/app/views/application/_navbar.html.erb b/app/views/application/_navbar.html.erb index 3436619..aaec030 100644 --- a/app/views/application/_navbar.html.erb +++ b/app/views/application/_navbar.html.erb @@ -12,6 +12,9 @@
  • <%= link_to "Inventory", character_items_path(current_char) %>
  • +
  • + <%= link_to "Spells", character_spells_path(current_char) %> +
  • <%= link_to "Hearth", character_hearth_path(current_char) %>
  • diff --git a/app/views/application/_results.html.erb b/app/views/application/_results.html.erb index ec62991..691f1d5 100644 --- a/app/views/application/_results.html.erb +++ b/app/views/application/_results.html.erb @@ -24,6 +24,8 @@

    You gained <%= result[:xp] %> <%= result[:skill].name %> XP.

    <% when "title" %>

    You earned the title <%= render "application/components/text/title", title: result[:title] %>!

    + <% when "condition" %> +

    You gained the <%= result[:condition].name %> condition.

    <% when "message" %>

    <%= result[:body] %>

    <% when "warning" %> diff --git a/app/views/characters/skills/index.html.erb b/app/views/characters/skills/index.html.erb index f79b06e..d804384 100644 --- a/app/views/characters/skills/index.html.erb +++ b/app/views/characters/skills/index.html.erb @@ -47,7 +47,7 @@
    <% end %> <% else %> -
    No items to infix.
    +
    No omens to infix.
    <% end %> <% end %> <% end %> 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 @@ +

    Spells

    +
    + <%= 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 %> +
    +
    diff --git a/config/routes.rb b/config/routes.rb index 51d9e6c..af608e5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -34,6 +34,7 @@ Rails.application.routes.draw do 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 a508781..ec82b38 100644 --- a/data/activities.yml +++ b/data/activities.yml @@ -2741,3 +2741,32 @@ craft_faint_mana: xp: - gid: "omenbind" value: 6 +havencast_light: + name: "Cast Light" + description: "Risk a little light." + whatnot: + tags: + - "spell" + - "cantrip" + duration: + base: 30 + minimum: 10 + scaling: + - type: "skill" + gid: "havencast" + scale_value: 1 + requirements: + - type: "skill" + gid: "havencast" + level: 1 + - type: "stat" + gid: "mana" + 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 -- cgit v1.2.3 From 637adf2963f174c3e3e3d6cf9efbff314e306f3e Mon Sep 17 00:00:00 2001 From: David Gay Date: Tue, 15 Jun 2021 21:52:49 -0400 Subject: MonsterKills and bestiary view --- CHANGELOG.md | 5 ++++- app/controllers/characters/bestiary_controller.rb | 12 ++++++++++++ app/lib/activity_processor.rb | 5 +++++ app/models/character.rb | 1 + app/models/monster_kill.rb | 7 +++++++ app/views/characters/bestiary/index.html.erb | 22 ++++++++++++++++++++++ app/views/characters/show.html.erb | 1 + config/routes.rb | 1 + db/migrate/20210616014044_create_monster_kills.rb | 11 +++++++++++ db/schema.rb | 14 +++++++++++++- test/fixtures/monster_kills.yml | 11 +++++++++++ test/models/monster_kill_test.rb | 7 +++++++ 12 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 app/controllers/characters/bestiary_controller.rb create mode 100644 app/models/monster_kill.rb create mode 100644 app/views/characters/bestiary/index.html.erb create mode 100644 db/migrate/20210616014044_create_monster_kills.rb create mode 100644 test/fixtures/monster_kills.yml create mode 100644 test/models/monster_kill_test.rb (limited to 'app/controllers') diff --git a/CHANGELOG.md b/CHANGELOG.md index acc3268..22ebaf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ All notable changes to this project will be documented in this file. When performing a skill, all omens infixed in that skill have the same chance to fade away. - 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. +- 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. ### Activities - Planequarry XP award changes @@ -34,7 +37,7 @@ All notable changes to this project will be documented in this file. - New amenity: binding array (level 1, level 2) ### UI -- Character skills were moved to their own view, which features a new interface with XP bars. +- Character skills were moved from the Character view to their own view, which features a new interface with XP bars. ## [0.1.10.3] - 2021-06-14 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/lib/activity_processor.rb b/app/lib/activity_processor.rb index e211db4..79c8f3e 100644 --- a/app/lib/activity_processor.rb +++ b/app/lib/activity_processor.rb @@ -323,6 +323,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 diff --git a/app/models/character.rb b/app/models/character.rb index 0f2c5b3..7f26ad4 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -14,6 +14,7 @@ class Character < ApplicationRecord 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. diff --git a/app/models/monster_kill.rb b/app/models/monster_kill.rb new file mode 100644 index 0000000..4096e8e --- /dev/null +++ b/app/models/monster_kill.rb @@ -0,0 +1,7 @@ +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") } +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 @@ +

    Bestiary

    + +<% if @monster_kills.any? %> + + + + + + + + + <% @monster_kills.ordered_by_monster_name.each do |mk| %> + + + + + <% end %> + +
    MonsterKills
    <%= mk.monster.name %><%= mk.quantity %>
    +<% else %> +

    You haven't killed any monsters yet.

    +<% end %> diff --git a/app/views/characters/show.html.erb b/app/views/characters/show.html.erb index 02f2f55..96a87f8 100644 --- a/app/views/characters/show.html.erb +++ b/app/views/characters/show.html.erb @@ -5,6 +5,7 @@
    • <%= link_to "Titles", character_titles_path(@character) %>
    • +
    • <%= link_to "Bestiary", character_bestiary_path(@character) %>
    • <%= link_to "Rankings", character_rankings_path(@character) %>
    diff --git a/config/routes.rb b/config/routes.rb index af608e5..fefd12f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ 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 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 f3992ee..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_14_004827) 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" @@ -207,6 +207,16 @@ ActiveRecord::Schema.define(version: 2021_06_14_004827) 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 @@ -326,6 +336,8 @@ ActiveRecord::Schema.define(version: 2021_06_14_004827) do 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/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/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 -- cgit v1.2.3 From 361acfad2162e08a962c63c4443641fbbd812089 Mon Sep 17 00:00:00 2001 From: David Gay Date: Wed, 16 Jun 2021 19:20:17 -0400 Subject: Learn spells from spellpages --- app/controllers/characters/spells_controller.rb | 6 +++++- app/lib/activity_processor.rb | 5 +---- app/views/application/_results.html.erb | 2 +- data/activities.yml | 16 ++++++++++------ data/items.yml | 8 ++++---- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/characters/spells_controller.rb b/app/controllers/characters/spells_controller.rb index 9694856..a0e6913 100644 --- a/app/controllers/characters/spells_controller.rb +++ b/app/controllers/characters/spells_controller.rb @@ -1,5 +1,9 @@ class Characters::SpellsController < ApplicationController def index - @spell_activities = Activity.where("gid like ?", "havencast_%").order(:name) + @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/lib/activity_processor.rb b/app/lib/activity_processor.rb index fb0b13d..18fdece 100644 --- a/app/lib/activity_processor.rb +++ b/app/lib/activity_processor.rb @@ -97,10 +97,6 @@ class ActivityProcessor @results.push({ type: "message", body: result[:message] }) @results.push({ type: type, condition: condition }) end - when "decipher_magicscript" - Character.transaction do - # TODO: Add functionality - end when "activity" next if rand > (result[:chance] || 1) table_roll = rand @@ -108,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 }) diff --git a/app/views/application/_results.html.erb b/app/views/application/_results.html.erb index 691f1d5..a7dc002 100644 --- a/app/views/application/_results.html.erb +++ b/app/views/application/_results.html.erb @@ -13,7 +13,7 @@

    You planted <%= link_to result[:hearth_planting].item.name, item_path(result[:hearth_planting].item) %> in the loam.

    <% when "activity" %> -

    You realized how to <%= result[:activity].name %>!

    +

    You learned how to <%= result[:activity].name %>!

    <% when "monster" %>

    You encountered a <%= result[:monster].name %>.

    <%= result[:monster].description %>

    diff --git a/data/activities.yml b/data/activities.yml index 66b7765..3202e7f 100644 --- a/data/activities.yml +++ b/data/activities.yml @@ -2904,9 +2904,9 @@ havencast_dazzle: - type: "xp" gid: "havencast" base: 5 -havencast_decipher_magicscript: - name: "Cast Decipher Magicscript" - description: "Cast the Decipher Magicscript cantrip." +havencast_decipher_simple_magicscript: + name: "Cast Decipher Simple Magicscript" + description: "Cast the Decipher Simple Magicscript cantrip." whatnot: tags: - "spell" @@ -2927,13 +2927,17 @@ havencast_decipher_magicscript: value: 1 cost: - type: "item" - gid: "spellpage" + gid: "simple_spellpage" quantity: 1 results: - type: "xp" gid: "havencast" - base: 7 - - type: "decipher_magicscript" + base: 10 + - type: "activity" + chance: 1 + table: + - gid: "havencast_stinging_rays" + score: 0 havencast_enchant_apprentice_wand: name: "Cast Enchant Apprentice Wand" description: "Cast the Enchant Apprentice Wand spell." diff --git a/data/items.yml b/data/items.yml index b20ab34..a89591e 100644 --- a/data/items.yml +++ b/data/items.yml @@ -1134,9 +1134,9 @@ faint_mana: - type: "stat_change" gid: "mana" modifier: 1 -spellpage: - name: "spellpage" - description: "A piece of parchment covered in magical script. Deciphering it can yield knowledge of a spell." +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: - - "materials" + - "material" -- cgit v1.2.3