diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/bazaar_controller.rb | 91 | ||||
-rw-r--r-- | app/models/bazaar_order.rb | 12 | ||||
-rw-r--r-- | app/models/character.rb | 14 | ||||
-rw-r--r-- | app/models/character_item.rb | 14 | ||||
-rw-r--r-- | app/models/concerns/destroy_if_zero_quantity.rb | 18 | ||||
-rw-r--r-- | app/views/application/_navbar.html.erb | 3 | ||||
-rw-r--r-- | app/views/bazaar/index.html.erb | 128 |
7 files changed, 264 insertions, 16 deletions
diff --git a/app/controllers/bazaar_controller.rb b/app/controllers/bazaar_controller.rb new file mode 100644 index 0000000..b31e8fb --- /dev/null +++ b/app/controllers/bazaar_controller.rb @@ -0,0 +1,91 @@ +class BazaarController < ApplicationController + def index + @bazaar_orders = BazaarOrder.all + @items = Item.all + end + + def create_order + item = Item.find(params[:item_id]) + quantity = params[:quantity].to_i + price_each = params[:price_each].to_i + if params[:buy_order] == "1" + BazaarOrder.transaction do + current_char.shift_item("vestige", (-price_each * quantity)) + current_char.bazaar_orders.create!(item: item, quantity: quantity, + price_each: price_each, buy_order: true) + flash[:notice] = "Posted buy order for #{item.name} at #{price_each} vg each." + end + else + BazaarOrder.transaction do + current_char.shift_item(item, -quantity) + current_char.bazaar_orders.create!(item: item, quantity: quantity, + price_each: price_each, buy_order: false) + flash[:notice] = "Posted sell order for #{item.name} at #{price_each} vg each." + end + end + rescue ItemQuantityError + flash[:alert] = "Failed to post order. Make sure you have vestige or items." + ensure + redirect_to bazaar_path + end + + def accept_offer + @bazaar_order = BazaarOrder.find(params[:id]) + if @bazaar_order.buy_order? + BazaarOrder.transaction do + quantity = [params[:quantity].to_i, @bazaar_order.quantity].min + profit = quantity * @bazaar_order.price_each + current_char.shift_item(@bazaar_order.item, -quantity) + current_char.shift_item("vestige", profit) + @bazaar_order.character.shift_item(@bazaar_order.item, quantity) + @bazaar_order.decrement!(:quantity, quantity) + @bazaar_order.save! + flash[:notice] = "You sold #{quantity} #{@bazaar_order.item.name} and got #{profit} vg." + end + else + BazaarOrder.transaction do + quantity = [params[:quantity].to_i, @bazaar_order.quantity].min + cost = quantity * @bazaar_order.price_each + current_char.shift_item("vestige", -cost) + current_char.shift_item(@bazaar_order.item, quantity) + @bazaar_order.character.shift_item("vestige", cost) + @bazaar_order.decrement!(:quantity, quantity) + @bazaar_order.save! + flash[:notice] = "You bought #{quantity} #{@bazaar_order.item.name} for #{cost} vg." + end + end + rescue ItemQuantityError + flash[:alert] = "Failed to accept order. Make sure you have vestige or items." + ensure + redirect_to bazaar_path + end + + def cancel_offer + @bazaar_order = BazaarOrder.find(params[:id]) + unless @bazaar_order.character == current_char + flash[:alert] = "You can't cancel someone else's order." + redirect_to bazaar_path and return + end + if @bazaar_order.buy_order? + BazaarOrder.transaction do + vestige_to_return = @bazaar_order.quantity * @bazaar_order.price_each + @bazaar_order.character.shift_item("vestige", vestige_to_return) + @bazaar_order.destroy! + flash[:notice] = "You canceled your buy order for #{@bazaar_order.item.name} and got" \ + " #{vestige_to_return} vg back." + end + else + BazaarOrder.transaction do + items_to_return = @bazaar_order.quantity + @bazaar_order.character.shift_item(@bazaar_order.item, items_to_return) + @bazaar_order.destroy! + flash[:notice] = "You canceled your sell order for #{@bazaar_order.item.name} and got" \ + " #{items_to_return} of them back." + end + end + rescue ItemQuantityError + flash[:alert] = "Failed to cancel order." + ensure + redirect_to bazaar_path + end +end diff --git a/app/models/bazaar_order.rb b/app/models/bazaar_order.rb new file mode 100644 index 0000000..d36d520 --- /dev/null +++ b/app/models/bazaar_order.rb @@ -0,0 +1,12 @@ +class BazaarOrder < ApplicationRecord + include DestroyIfZeroQuantity + + belongs_to :character + belongs_to :item + + validates :price_each, :quantity, + numericality: { greater_than_or_equal_to: 0, + less_than_or_equal_to: 2_000_000_000, only_integer: true } + validates :buy_order, inclusion: { in: [true, false] } + +end diff --git a/app/models/character.rb b/app/models/character.rb index bad2bb2..003f8af 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -11,6 +11,7 @@ class Character < ApplicationRecord has_many :items, through: :character_items has_many :character_skills has_many :chat_messages + has_many :bazaar_orders validates :name, presence: true validates_length_of :name, maximum: 15, message: "can't be longer than 15 characters" validates_uniqueness_of :name, message: "is already being used" @@ -35,12 +36,17 @@ class Character < ApplicationRecord def wildscour_level; skill_level("wildscour"); end def worldcall_level; skill_level("worldcall"); end + def vestige + vestige = self.character_items.find_by(item: Item.find_by_gid("vestige")) + vestige ? vestige.quantity : 0 + end + def shift_item(item, amount) item = Item.find_by_gid(item) if item.is_a? String CharacterItem.transaction do ci = self.character_items.find_or_initialize_by(item: item) - ci.increment(:quantity, amount) - ci.save + ci.increment!(:quantity, amount) + ci.save! end end @@ -71,7 +77,7 @@ class Character < ApplicationRecord open_slots = self.open_slots_for(item) raise EquipmentError unless open_slots.any? self.shift_item(item, -1) - self.equipment.create(item: item, slot: open_slots.first) + self.equipment.create!(item: item, slot: open_slots.first) end end @@ -80,7 +86,7 @@ class Character < ApplicationRecord equipment = self.equipment.find_by(slot: slot) raise EquipmentError unless equipment item = equipment.item - equipment.destroy + equipment.destroy! self.shift_item(item, 1) end end diff --git a/app/models/character_item.rb b/app/models/character_item.rb index b54f799..6ee13ab 100644 --- a/app/models/character_item.rb +++ b/app/models/character_item.rb @@ -1,4 +1,6 @@ class CharacterItem < ApplicationRecord + include DestroyIfZeroQuantity + belongs_to :character belongs_to :item validates :quantity, presence: true @@ -9,17 +11,5 @@ class CharacterItem < ApplicationRecord end end - after_save :destroy_if_zero_quantity - scope :ordered_by_item_name, -> { includes(:item).order("items.name") } - - private - def destroy_if_zero_quantity - if self.quantity == 0 - destroy - elsif self.quantity < 0 - # TODO: Can improve this (at the least, with reporting, later). - raise ItemQuantityError - end - end end diff --git a/app/models/concerns/destroy_if_zero_quantity.rb b/app/models/concerns/destroy_if_zero_quantity.rb new file mode 100644 index 0000000..6fdfb3e --- /dev/null +++ b/app/models/concerns/destroy_if_zero_quantity.rb @@ -0,0 +1,18 @@ +module DestroyIfZeroQuantity + extend ActiveSupport::Concern + + included do + after_save :destroy_if_zero_quantity + end + + private + def destroy_if_zero_quantity + byebug + if self.quantity == 0 + destroy + elsif self.quantity < 0 + # TODO: Can improve this (at the least, with reporting, later). + raise ItemQuantityError + end + end +end diff --git a/app/views/application/_navbar.html.erb b/app/views/application/_navbar.html.erb index ef81d6c..c0e2761 100644 --- a/app/views/application/_navbar.html.erb +++ b/app/views/application/_navbar.html.erb @@ -12,5 +12,8 @@ <li class="mr-6 inline"> <%= link_to "Hearth", character_hearth_path(current_char) %> </li> + <li class="mr-6 inline"> + <%= link_to "Bazaar", bazaar_path %> + </li> <% end %> </ul> diff --git a/app/views/bazaar/index.html.erb b/app/views/bazaar/index.html.erb new file mode 100644 index 0000000..63d708f --- /dev/null +++ b/app/views/bazaar/index.html.erb @@ -0,0 +1,128 @@ +<h1 class="text-3xl mb-4">Numedite Bazaar</h1> + +<p class="mb-2">The small, cloaked people known as the Numedites travel this way and that across the planes. + Because their ways are spiritual in nature, they use their innate magic to facilitate + distant trade at no cost to their clientele.</p> + +<p class="mb-2">You have <span class="font-bold"><%= current_char.vestige %></span> vestige.</p> + +<div class="my-8"> + <h2 class="text-xl mb-4">Post Order</h2> + + <p class="mb-4">Sell orders can be posted with a caravan of numedites, offering items for vestige. + Buy orders can also be posted, offering vestige for items.</p> + + <%= form_with url: bazaar_order_path, class: "my-4" do |f| %> + <div> + <%= f.label :item_id, "Item" %> + <%= f.select :item_id, @items.map { |i| [i.name, i.id] } %> + </div> + <div> + <%= f.label :quantity, "Quantity" %> + <%= f.number_field :quantity, required: true, min: 1, max: 2_000_000_000 %> + </div> + <div> + <%= f.label :price_each, "Price Each" %> + <%= f.number_field :price_each, required: true, min: 1, max: 2_000_000_000 %> + </div> + <div> + <%= f.label :buy_order, "Post as buy order" %> + <%= f.check_box :buy_order %> + </div> + <div> + <%= f.submit "Post Order" %> + </div> + <% end %> +</div> + +<div class="my-8"> + <h2 class="text-xl mb-4">Your Orders</h2> + <table class="table-auto mb-8"> + <thead> + <tr> + <th class="table-header-padded">Item</th> + <th class="table-header-padded">Quantity</th> + <th class="table-header-padded">Type</th> + <th class="table-header-padded">Price Each</th> + <th class="table-header-padded">Cancel</th> + </tr> + </thead> + <tbody> + <% @bazaar_orders.where(character: current_char).each do |bo| %> + <tr> + <td class="table-cell-padded"><%= bo.item.name %></td> + <td class="table-cell-padded"><%= bo.quantity %></td> + <td class="table-cell-padded"><%= bo.buy_order? ? "Buy" : "Sell" %></td> + <td class="table-cell-padded"><%= bo.price_each %></td> + <td class="table-cell-padded"> + <%= button_to "Cancel", bazzar_cancel_offer_path(id: bo.id), method: :delete %> + </td> + </tr> + <% end %> + </tbody> + </table> +</div> + +<div class="my-8"> + <h2 class="text-xl mb-4">Sell Orders</h2> + + <table class="table-auto mb-8"> + <thead> + <tr> + <th class="table-header-padded">Item</th> + <th class="table-header-padded">Quantity</th> + <th class="table-header-padded">From</th> + <th class="table-header-padded">Price Each</th> + <th class="table-header-padded">Buy</th> + </tr> + </thead> + <tbody> + <% @bazaar_orders.where(buy_order: false).each do |bo| %> + <tr> + <td class="table-cell-padded"><%= bo.item.name %></td> + <td class="table-cell-padded"><%= bo.quantity %></td> + <td class="table-cell-padded"><%= bo.character.name %></td> + <td class="table-cell-padded"><%= bo.price_each %></td> + <td class="table-cell-padded"> + <%= form_with url: bazzar_accept_offer_path(id: bo.id) do |f| %> + <%= f.number_field :quantity, value: 1, min: 1, max: 2_000_000_000, required: true %> + <%= f.submit "Buy" %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> +</div> + +<div class="my-8"> + <h2 class="text-xl mb-4">Buy Orders</h2> + + <table class="table-auto mb-8"> + <thead> + <tr> + <th class="table-header-padded">Item</th> + <th class="table-header-padded">Quantity</th> + <th class="table-header-padded">From</th> + <th class="table-header-padded">Price Each</th> + <th class="table-header-padded">Sell</th> + </tr> + </thead> + <tbody> + <% @bazaar_orders.where(buy_order: true).each do |bo| %> + <tr> + <td class="table-cell-padded"><%= bo.item.name %></td> + <td class="table-cell-padded"><%= bo.quantity %></td> + <td class="table-cell-padded"><%= bo.character.name %></td> + <td class="table-cell-padded"><%= bo.price_each %></td> + <td class="table-cell-padded"> + <%= form_with url: bazzar_accept_offer_path(id: bo.id) do |f| %> + <%= f.number_field :quantity, value: 1, min: 1, max: 2_000_000_000, required: true %> + <%= f.submit "Sell" %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> +</div> |