summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/bazaar_controller.rb91
-rw-r--r--app/models/bazaar_order.rb12
-rw-r--r--app/models/character.rb14
-rw-r--r--app/models/character_item.rb14
-rw-r--r--app/models/concerns/destroy_if_zero_quantity.rb18
-rw-r--r--app/views/application/_navbar.html.erb3
-rw-r--r--app/views/bazaar/index.html.erb128
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>