[XP] RMX-OS Player Shops

Started by Wizered67, December 03, 2014, 12:04:53 am

Previous topic - Next topic

Wizered67

December 03, 2014, 12:04:53 am Last Edit: December 03, 2014, 12:06:51 am by Wizered67
RMX-OS Player Shops
Authors: Wizered67
Version: 0.89
Type: Player Shop System
Key Term: RMX-OS Plugin



Introduction

A few weeks ago I worked on this script for Player Shops for Whitespirits. Unfortunately I don't currently have RMXP so I can't put the finishing touches on this script, but it should be complete enough to be functional and can be edited as necessary. This script allows players to create their own shops where they can place items, weapons, and armor for sale at a price of their choice for other players to buy. There is a configurable fee for creating a shop and players will only be able to access shops on the map they were created in. Players can also name their shops for extra fun. With this script even games without hundreds of players online at a time can have a thriving economy and community.


Features


  • Lets players make shops to make money.

  • Gives players an alternate way to buy goods.

  • Adds new commands for making, editing, buying from, and listing shops.

  • Nearly completely plug and play.





Screenshots
Selling items in a shop: ShowHide

Buying from a shop: ShowHide



Demo

N/A


Script
Client Side (RMX-OS 2.0 Compatible): ShowHide


#------------------------------------------------------------------------------#
#                  RMX-OS Player Shops                                       #
#                     by Wizered67                                             #
#                          V 0.89                                              #
# All errors should be reported at www.chaos-project.com.                      #
#------------------------------------------------------------------------------#
module PlayerShopConfig
 SHOP_COST = 500
end

#==============================================================================
# module RMXOS
#==============================================================================

module RMXOS
 
 #============================================================================
 # module Documentation
 #============================================================================
 
 module Documentation
   
   PARAMETERS['shops']   = 'none'
   PARAMETERS['makeshop']    = 'SHOPNAME'
   PARAMETERS['editshop']    = 'SHOPID'
   PARAMETERS['buy']    = 'SHOPID'
   DESCRIPTIONS['shops'] = 'Displays a list of all shops on the same map.'
   DESCRIPTIONS['makeshop']  = "Creates a new shop on the current map. Costs #{PlayerShopConfig::SHOP_COST} gold."
   DESCRIPTIONS['editshop']  = 'Edits the shop with the specified id.'
   DESCRIPTIONS['buy']  = 'Opens the shop with the specified id. Only 1 player can be using a shop at a time.'
 end
 
 #============================================================================
 # module Data
 #============================================================================
 
 module Data
   PlayerShopsReceivedMoney     = 'Received AMOUNT Gold for purchases made in your shop.'
   NewShopCreated               = "Spent #{PlayerShopConfig::SHOP_COST} gold and created a new shop with id: ID. Add items with /editshop SHOPID"
   InvalidItemShop              = 'Invalid item or shop has been selected.'
   NotEnoughMoney               = "Not enough money to make a shop. It costs #{PlayerShopConfig::SHOP_COST} gold."
   
 end
 
 #============================================================================
 # Network
 #============================================================================
 
 class Network
   
   def sell_item(price)
     if $game_temp.player_shop_sell_item == nil || $game_temp.player_shop_sell_selected == 0
       self.add_error(RMXOS::Data::InvalidItemShop)
       return
     end
     type = 0
     case $game_temp.player_shop_sell_item
     when RPG::Item
       $game_party.lose_item($game_temp.player_shop_sell_item.id, $game_temp.player_shop_sell_amount)
       type = 0
     when RPG::Weapon
       $game_party.lose_weapon($game_temp.player_shop_sell_item.id, $game_temp.player_shop_sell_amount)
       type = 1
     when RPG::Armor
       $game_party.lose_armor($game_temp.player_shop_sell_item.id, $game_temp.player_shop_sell_amount)
       type = 2
     end
     shop = $game_temp.player_shop_sell_selected
     item = $game_temp.player_shop_sell_item.id
     amount = $game_temp.player_shop_sell_amount
     self.send('SCD', shop, item, amount, price, type)
   end
   
   alias check_game_player_shops_alias check_game
   def check_game(message)
     
       case message
       
       when /\ARMPS\t(.+)/
         amount = $1
         self.add_message(0, RMXOS::Data::PlayerShopsReceivedMoney.sub('AMOUNT',
           amount), RMXOS::Data::ColorOk)
           $game_party.gain_gold(amount.to_i)
       return true
       
       when /\ANSI\t(.+)/
         id = $1.to_i
         self.add_message(0, RMXOS::Data::NewShopCreated.sub('ID',
           id.to_s), RMXOS::Data::ColorOk)
       return true
       
       when /\ALSSD\t(.+)/
         $game_temp.player_shop_buy_items = eval($1)
         $game_temp.player_shop_owner = $game_temp.player_shop_buy_items[0]
         $game_temp.player_shop_name = $game_temp.player_shop_buy_items[4]
         $scene = Scene_PlayerShopBuy.new
       return true
       
       when /\AEIS\Z/ #invalid shop selected (sell)
         $game_temp.player_shop_sell_selected = 0
       return true
       
       when /\AEISB\Z/ #invalid shop selected (buy)
         $game_temp.player_shop_buy_selected = 0
       return true
         
       when /\AOESC\t(.+)\t(.+)/ #open edit shop screen
         $game_temp.player_shop_owner = $1
         $game_temp.player_shop_name = $2
         $scene = Scene_PlayerShopSell.new
       return true
     end
     
     return check_game_player_shops_alias(message)
   end
   
   alias check_normal_commands_player_shops_alias check_normal_commands
   def check_normal_commands(message)
     case message
       when /\A\/shops\Z/
         self.send('SMSM', $game_map.map_id)
       return true
       
       when /\A\/editshop (\S+)\Z/
         self.send('TSES', $1)
         $game_temp.player_shop_sell_selected = $1.to_i
       return true
     
       when /\A\/buy (\S+)\Z/
         self.send('SSD', $1, $game_map.map_id)
         $game_temp.player_shop_buy_selected = $1.to_i
       return true
       
       when /\A\/makeshop (\S{1}.*)/
           if $game_party.gold >= PlayerShopConfig::SHOP_COST
             $game_party.lose_gold(PlayerShopConfig::SHOP_COST)
             self.send('CNPS', @username, @user_id, $game_map.map_id, $1)
           
         else
           self.add_error(RMXOS::Data::NotEnoughMoney)
         end
       return true
     end
     return check_normal_commands_player_shops_alias(message)
   end
 
 end
 
end


#==============================================================================
# ** Scene_Shop
#------------------------------------------------------------------------------
#  This class performs shop screen processing.
#==============================================================================

class Scene_PlayerShopSell
 #--------------------------------------------------------------------------
 # * Main Processing
 #--------------------------------------------------------------------------
 def main
   # Make help window
   @help_window = Window_Help.new
   # Make gold window
   @gold_window = Window_Gold.new
   @gold_window.x = 480
   @gold_window.y = 64
   # Make dummy window
   @dummy_window = Window_Base.new(0, 128, 640, 352)
   @dummy_window2 = Window_Base.new(0, 128, 368, 352)
   @dummy_window2.visible = false
   @shop_name_window = Window_PlayerShopName.new
   # Make sell window
   @sell_window = Window_ShopSell.new
   @sell_window.active = true
   @sell_window.visible = true
   @sell_window.help_window = @help_window
   # Make quantity input window
   @number_window = Window_PlayerShopSellNumber.new
   @number_window.active = false
   @number_window.visible = false
   @input_number_window = Window_PlayerShopInputNumber.new(6)
   @input_number_window.number = 0
   #@input_number_window.x = 128
   #@input_number_window.y = 224
   @input_number_window.visible = false
   @input_number_window.active = false
   # Make status window
   @status_window = Window_ShopStatus.new
   @status_window.visible = false
   # Execute transition
   Graphics.transition
   # Main loop
   loop do
     # Update game screen
     Graphics.update
     # Update input information
     Input.update
     # Frame update
     update
     # Abort loop if screen is changed
     if $scene != self
       break
     end
   end
   # Prepare for transition
   Graphics.freeze
   # Dispose of windows
   @help_window.dispose
   @gold_window.dispose
   @dummy_window.dispose
   @dummy_window2.dispose
   @sell_window.dispose
   @number_window.dispose
   @status_window.dispose
   @shop_name_window.dispose
   @input_number_window.dispose
 end
 #--------------------------------------------------------------------------
 # * Frame Update
 #--------------------------------------------------------------------------
 def update
   # Update windows
   @help_window.update
   @gold_window.update
   @dummy_window.update
   @sell_window.update
   @number_window.update
   @status_window.update
   @shop_name_window.update
   
   $network.listen
   
   if @input_number_window.active
     @input_number_window.update
     update_price_input
     return
   end
   # If sell window is active: call update_sell
   if @sell_window.active
     update_sell
     return
   end
   # If quantity input window is active: call update_number
   if @number_window.active
     update_number
     return
   end
 end
 #--------------------------------------------------------------------------
 # * Frame Update (when price window is active)
 #--------------------------------------------------------------------------
 def update_price_input
   if Input.trigger?(Input::B)
     # Play cancel SE
     $game_system.se_play($data_system.cancel_se)
     @input_number_window.active = false
     @input_number_window.visible = false
     @number_window.active = true
     @number_window.visible = true
     #@dummy_window.visible = true
     @dummy_window2.visible = false
     return
   end
   
   if Input.trigger?(Input::C)
     if @input_number_window.number == 0
       $game_system.se_play($data_system.buzzer_se)
       return
     end
     $game_system.se_play($data_system.shop_se)
     @dummy_window.visible = true
     $network.sell_item(@input_number_window.number)
     @gold_window.refresh
     @sell_window.refresh
     @status_window.refresh
     # Change windows to sell mode
     @sell_window.active = true
     @sell_window.visible = true
     @status_window.visible = false
     @input_number_window.visible = false
     @input_number_window.active = false
     @dummy_window2.visible = false
     return
   end
 end
 #--------------------------------------------------------------------------
 # * Frame Update (when sell window is active)
 #--------------------------------------------------------------------------
 def update_sell
   # If B button was pressed
   if Input.trigger?(Input::B)
     # Play cancel SE
     $game_system.se_play($data_system.cancel_se)
     # Change windows to initial mode
     $scene = Scene_Map.new
     $network.send('SIU', $game_temp.player_shop_sell_selected, 0)
     return
   end
   # If C button was pressed
   if Input.trigger?(Input::C)
     # Get item
     @item = @sell_window.item
     # Set status window item
     @status_window.item = @item
     # If item is invalid, or item price is 0 (unable to sell)
     if @item == nil or @item.price == 0
       # Play buzzer SE
       $game_system.se_play($data_system.buzzer_se)
       return
     end
     # Play decision SE
     $game_system.se_play($data_system.decision_se)
     # Get items in possession count
     case @item
     when RPG::Item
       number = $game_party.item_number(@item.id)
     when RPG::Weapon
       number = $game_party.weapon_number(@item.id)
     when RPG::Armor
       number = $game_party.armor_number(@item.id)
     end
     # Maximum quanitity to sell = number of items in possession
     max = number
     # Change windows to quantity input mode
     @sell_window.active = false
     @sell_window.visible = false
     @number_window.set(@item, max)
     @number_window.active = true
     @number_window.visible = true
     @status_window.visible = true
     @dummy_window.visible = false
   end
 end
 #--------------------------------------------------------------------------
 # * Frame Update (when quantity input window is active)
 #--------------------------------------------------------------------------
 def update_number
   # If B button was pressed
   if Input.trigger?(Input::B)
     # Play cancel SE
     $game_system.se_play($data_system.cancel_se)
     # Set quantity input window to inactive / invisible
     @number_window.active = false
     @number_window.visible = false
     # Branch by command window cursor position
     
       @sell_window.active = true
       @sell_window.visible = true
       @status_window.visible = false
       @dummy_window.visible = true
     return
   end
   # If C button was pressed
   if Input.trigger?(Input::C)
     # Play shop SE
     $game_system.se_play($data_system.decision_se)
     $game_temp.player_shop_sell_item = @item
     $game_temp.player_shop_sell_amount = @number_window.number
     @number_window.active = false
     @number_window.visible = false
     @input_number_window.number = 0
     @input_number_window.active = true
     @input_number_window.visible = true
     @input_number_window.set_item(@item)
     @input_number_window.refresh
     @dummy_window.visible = false
     @dummy_window2.visible = true
     return
   end
   return
 end
end

#==============================================================================
# ** Window_PlayerShopName
#------------------------------------------------------------------------------
#  Displays owner and shop name in shop screen
#==============================================================================

class Window_PlayerShopName < Window_Base
 #--------------------------------------------------------------------------
 # * Object Initialization
 #--------------------------------------------------------------------------
 def initialize
   super(0, 64, 480, 64)
   self.contents = Bitmap.new(width - 32, height - 32)
   update
 end
 #--------------------------------------------------------------------------
 # * Refresh
 #--------------------------------------------------------------------------
 def update
   self.contents.clear
   self.contents.font.color = system_color
   self.contents.draw_text(4, 0, 480, 32, $game_temp.player_shop_owner + "'s " + $game_temp.player_shop_name)
 end
end


#==============================================================================
# ** Window_PlayerShopSellNumber
#------------------------------------------------------------------------------
#  Modified version that doesn't show how much it will sell for
#==============================================================================

class Window_PlayerShopSellNumber < Window_Base
 #--------------------------------------------------------------------------
 # * Object Initialization
 #--------------------------------------------------------------------------
 def initialize
   super(0, 128, 368, 352)
   self.contents = Bitmap.new(width - 32, height - 32)
   @item = nil
   @max = 1
   @number = 1
 end
 #--------------------------------------------------------------------------
 # * Set Items, Max Quantity, and Price
 #--------------------------------------------------------------------------
 def set(item, max)
   @item = item
   @max = max
   @number = 1
   refresh
 end
 #--------------------------------------------------------------------------
 # * Set Inputted Quantity
 #--------------------------------------------------------------------------
 def number
   return @number
 end
 #--------------------------------------------------------------------------
 # * Refresh
 #--------------------------------------------------------------------------
 def refresh
   self.contents.clear
   draw_item_name(@item, 4, 96)
   self.contents.font.color = normal_color
   self.contents.draw_text(272, 96, 32, 32, "×")
   self.contents.draw_text(308, 96, 24, 32, @number.to_s, 2)
   self.cursor_rect.set(304, 96, 32, 32)
 end
 #--------------------------------------------------------------------------
 # * Frame Update
 #--------------------------------------------------------------------------
 def update
   super
   if self.active
     # Cursor right (+1)
     if Input.repeat?(Input::RIGHT) and @number < @max
       $game_system.se_play($data_system.cursor_se)
       @number += 1
       refresh
     end
     # Cursor left (-1)
     if Input.repeat?(Input::LEFT) and @number > 1
       $game_system.se_play($data_system.cursor_se)
       @number -= 1
       refresh
     end
     # Cursdr up (+10)
     if Input.repeat?(Input::UP) and @number < @max
       $game_system.se_play($data_system.cursor_se)
       @number = [@number + 10, @max].min
       refresh
     end
     # Cursor down (-10)
     if Input.repeat?(Input::DOWN) and @number > 1
       $game_system.se_play($data_system.cursor_se)
       @number = [@number - 10, 1].max
       refresh
     end
   end
 end
end

#==============================================================================
# ** Window_PlayerShopInputNumber
#------------------------------------------------------------------------------
#  This window is for inputting numbers, and is used within the
#  message window.
#==============================================================================

class Window_PlayerShopInputNumber < Window_Base
 #--------------------------------------------------------------------------
 # * Object Initialization
 #     digits_max : digit count
 #--------------------------------------------------------------------------
 def initialize(digits_max)
   @digits_max = digits_max
   @number = 0
   @item = nil
   # Calculate cursor width from number width (0-9 equal width and postulate)
   dummy_bitmap = Bitmap.new(32, 32)
   @cursor_width = dummy_bitmap.text_size("0").width + 8
   dummy_bitmap.dispose
   super(0, 128, 368, 352)
   self.contents = Bitmap.new(width - 32, height - 32)
   self.z += 9999
   self.opacity = 0
   @index = 0
   refresh
   update_cursor_rect
 end
 #--------------------------------------------------------------------------
 # * Get Number
 #--------------------------------------------------------------------------
 def number
   return @number
 end
 #--------------------------------------------------------------------------
 # * Set Number
 #     number : new number
 #--------------------------------------------------------------------------
 def number=(number)
   @number = [[number, 0].max, 10 ** @digits_max - 1].min
   refresh
 end
 #--------------------------------------------------------------------------
 # * Cursor Rectangle Update
 #--------------------------------------------------------------------------
 def update_cursor_rect
   self.cursor_rect.set(128 + @index * @cursor_width, 96, @cursor_width, 32)
 end
 #--------------------------------------------------------------------------
 # * Frame Update
 #--------------------------------------------------------------------------
 def update
   super
   # If up or down directional button was pressed
   if Input.repeat?(Input::UP) or Input.repeat?(Input::DOWN)
     $game_system.se_play($data_system.cursor_se)
     # Get current place number and change it to 0
     place = 10 ** (@digits_max - 1 - @index)
     n = @number / place % 10
     @number -= n * place
     # If up add 1, if down substract 1
     n = (n + 1) % 10 if Input.repeat?(Input::UP)
     n = (n + 9) % 10 if Input.repeat?(Input::DOWN)
     # Reset current place number
     @number += n * place
     refresh
   end
   # Cursor right
   if Input.repeat?(Input::RIGHT)
     if @digits_max >= 2
       $game_system.se_play($data_system.cursor_se)
       @index = (@index + 1) % @digits_max
     end
   end
   # Cursor left
   if Input.repeat?(Input::LEFT)
     if @digits_max >= 2
       $game_system.se_play($data_system.cursor_se)
       @index = (@index + @digits_max - 1) % @digits_max
     end
   end
   update_cursor_rect
 end
 def set_item(item)
     @item = item
 end
 #--------------------------------------------------------------------------
 # * Refresh
 #--------------------------------------------------------------------------
 def refresh
   self.contents.clear
   self.contents.font.color = normal_color
   draw_item_name(@item, 4, 0)
   self.contents.draw_text(128, 64, 96,32, "Price: ")
   s = sprintf("%0*d", @digits_max, @number)
   for i in 0...@digits_max
     self.contents.draw_text(128 + i * @cursor_width + 4, 96, 32, 32, s[i,1])
   end
 end
end


#==============================================================================
# ** Scene_PlayerShopBuy
#------------------------------------------------------------------------------
#  This class performs shop screen processing.
#==============================================================================

class Scene_PlayerShopBuy
 #--------------------------------------------------------------------------
 # * Main Processing
 #--------------------------------------------------------------------------
 def main
   # Make help window
   @help_window = Window_Help.new
   # Make command window
   # Make gold window
   @gold_window = Window_Gold.new
   @gold_window.x = 480
   @gold_window.y = 64
   # Make dummy window
   @dummy_window = Window_Base.new(0, 128, 640, 352)
   @dummy_window.visible = false
   @shop_name_window = Window_PlayerShopName.new
   # Make buy window
   @buy_window = Window_PlayerShopBuy.new #shop goods here
   @buy_window.active = true
   @buy_window.visible = true
   @buy_window.help_window = @help_window
   # Make quantity input window
   @number_window = Window_ShopNumber.new
   @number_window.active = false
   @number_window.visible = false
   # Make status window
   @status_window = Window_ShopStatus.new
   @status_window.visible = true
   # Execute transition
   Graphics.transition
   # Main loop
   loop do
     # Update game screen
     Graphics.update
     # Update input information
     Input.update
     # Frame update
     update
     # Abort loop if screen is changed
     if $scene != self
       break
     end
   end
   # Prepare for transition
   Graphics.freeze
   # Dispose of windows
   @help_window.dispose
   @gold_window.dispose
   @dummy_window.dispose
   @buy_window.dispose
   @number_window.dispose
   @status_window.dispose
   @shop_name_window.dispose
 end
 
 #--------------------------------------------------------------------------
 # * Frame Update
 #--------------------------------------------------------------------------
 def update
   # Update windows
   @help_window.update
   @gold_window.update
   @dummy_window.update
   @buy_window.update
   @number_window.update
   @status_window.update
   @shop_name_window.update
   $network.listen
   # If buy window is active: call update_buy
   if @buy_window.active
     update_buy
     return
   end
   # If quantity input window is active: call update_number
   if @number_window.active
     update_number
     return
   end
 end
 #--------------------------------------------------------------------------
 # * Frame Update (when buy window is active)
 #--------------------------------------------------------------------------
 def update_buy
   # Set status window item
   @status_window.item = @buy_window.item
   # If B button was pressed
   if Input.trigger?(Input::B)
     # Play cancel SE
     $game_system.se_play($data_system.cancel_se)
     $network.send('SIU', $game_temp.player_shop_buy_selected, 0)
     $scene = Scene_Map.new
     return
   end
   # If C button was pressed
   if Input.trigger?(Input::C)
     # Get item
     @item = @buy_window.item
     # If item is invalid, or price is higher than money possessed
     if @item == nil or @buy_window.get_price > $game_party.gold
       # Play buzzer SE
       $game_system.se_play($data_system.buzzer_se)
       return
     end
     # Get items in possession count
     case @item
     when RPG::Item
       number = $game_party.item_number(@item.id)
     when RPG::Weapon
       number = $game_party.weapon_number(@item.id)
     when RPG::Armor
       number = $game_party.armor_number(@item.id)
     end
     # If 99 items are already in possession
     if number == 99
       # Play buzzer SE
       $game_system.se_play($data_system.buzzer_se)
       return
     end
     # Play decision SE
     $game_system.se_play($data_system.decision_se)
     # Calculate maximum amount possible to buy
     max = @buy_window.get_price == 0 ? 99 : $game_party.gold / @buy_window.get_price
     max = [max, 99 - number, @buy_window.get_number].min
     # Change windows to quantity input mode
     @buy_window.active = false
     @buy_window.visible = false
     @number_window.set(@item, max, @buy_window.get_price)
     @number_window.active = true
     @number_window.visible = true
   end
 end
 #--------------------------------------------------------------------------
 # * Frame Update (when quantity input window is active)
 #--------------------------------------------------------------------------
 def update_number
   # If B button was pressed
   if Input.trigger?(Input::B)
     # Play cancel SE
     $game_system.se_play($data_system.cancel_se)
     # Set quantity input window to inactive / invisible
     @number_window.active = false
     @number_window.visible = false
     # Branch by command window cursor position
     @buy_window.active = true
     @buy_window.visible = true
     return
   end
   # If C button was pressed
   if Input.trigger?(Input::C)
     # Play shop SE
     $game_system.se_play($data_system.shop_se)
     # Set quantity input window to inactive / invisible
     @number_window.active = false
     @number_window.visible = false
     if $game_temp.player_shop_buy_items[1] != $network.user_id
       $game_party.lose_gold(@number_window.number * @buy_window.get_price)
     end
       
       $network.send('SMP',$game_temp.player_shop_buy_items[1] ,@number_window.number * @buy_window.get_price)
       case @item
       when RPG::Item
         type = 0
         $game_party.gain_item(@item.id, @number_window.number)
       when RPG::Weapon
         type = 1
         $game_party.gain_weapon(@item.id, @number_window.number)
       when RPG::Armor
         type = 2
         $game_party.gain_armor(@item.id, @number_window.number)
       end
       shop = $game_temp.player_shop_buy_selected
       id = @item.id
       amount = @number_window.number
       price = @buy_window.get_price
       $network.send('SCD',shop, id, -amount, price, type)
       @buy_window.reduce_quantity(@number_window.number)
       # Refresh each window
       @gold_window.refresh
       @buy_window.refresh
       @status_window.refresh
       # Change windows to buy mode
       @buy_window.active = true
       @buy_window.visible = true
     return
   end
 end
end

#==============================================================================
# ** Window_PlayerShopBuy
#------------------------------------------------------------------------------
#  This window displays buyable goods on the shop screen.
#==============================================================================

class Window_PlayerShopBuy < Window_Selectable
 #--------------------------------------------------------------------------
 # * Object Initialization
 #     shop_goods : goods
 #--------------------------------------------------------------------------
 def initialize
   super(0, 128, 368, 352)
   refresh
   self.index = 0
 end
 #--------------------------------------------------------------------------
 # * Item Acquisition
 #--------------------------------------------------------------------------
 def item
   return nil if @data == []
   return @data[self.index][0]
 end
 #--------------------------------------------------------------------------
 # * Refresh
 #--------------------------------------------------------------------------
 def refresh
   if self.contents != nil
     self.contents.dispose
     self.contents = nil
   end
   @data = []
   goods = $game_temp.player_shop_buy_items
   for item_data in goods[3].values
     next if item_data[1] <= 0
     case item_data[2]
     when 0
     item = $data_items[item_data[3]]
     when 1
       item = $data_weapons[item_data[3]]
     when 2
       item = $data_armors[item_data[3]]
     end
     if item != nil
       @data.push([item, item_data[0], item_data[1]]) #[item, price, amount]
     end
   end
   if self.index >= @data.size
       self.index = @data.size - 1
       self.index = 0 if self.index < 0
   end
   # If item count is not 0, make a bit map and draw all items
   @item_max = @data.size
   if @item_max > 0
     self.contents = Bitmap.new(width - 32, row_max * 32)
     for i in 0...@item_max
       draw_item(i)
     end
   end
 end
 
 def reduce_quantity(amount)
   for item_data in $game_temp.player_shop_buy_items[3].values
    case item_data[2]
     when 0
     item = $data_items[item_data[3]]
     when 1
       item = $data_weapons[item_data[3]]
     when 2
       item = $data_armors[item_data[3]]
     end
     if item == self.item
         item_data[1] -= amount
     end
   end
 end
   
 def get_price
     return @data[self.index][1]
 end
 
 def get_number
     return @data[self.index][2]
 end
 #--------------------------------------------------------------------------
 # * Draw Item
 #     index : item number
 #--------------------------------------------------------------------------
 def draw_item(index)
   item = @data[index][0]
   # Get items in possession
   case item
   when RPG::Item
     number = $game_party.item_number(item.id)
   when RPG::Weapon
     number = $game_party.weapon_number(item.id)
   when RPG::Armor
     number = $game_party.armor_number(item.id)
   end
   # If price is less than money in possession, and amount in possession is
   # not 99, then set to normal text color. Otherwise set to disabled color
   if @data[index][1] <= $game_party.gold and number < 99
     self.contents.font.color = normal_color
   else
     self.contents.font.color = disabled_color
   end
   x = 4
   y = index * 32
   rect = Rect.new(x, y, self.width - 32, 32)
   self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
   bitmap = RPG::Cache.icon(item.icon_name)
   opacity = self.contents.font.color == normal_color ? 255 : 128
   self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
   self.contents.draw_text(x + 28, y, 212, 32, item.name + "  [QTY: " + @data[index][2].to_s + "]", 0)
   self.contents.draw_text(x + 240, y, 88, 32, @data[index][1].to_s, 2)
 end
 #--------------------------------------------------------------------------
 # * Help Text Update
 #--------------------------------------------------------------------------
 def update_help
   @help_window.set_text(self.item == nil ? "" : self.item.description)
 end
end



class Game_Temp
   attr_accessor :player_shop_sell_item
   attr_accessor :player_shop_sell_amount
   attr_accessor :player_shop_sell_selected
   attr_accessor :player_shop_owner
   attr_accessor :player_shop_name
   attr_accessor :player_shop_buy_items
   attr_accessor :player_shop_buy_selected
   alias initialize_player_shops initialize
   def initialize
       @player_shop_sell_item = nil
       @player_shop_sell_amount = 0
       @player_shop_sell_selected = 0
       @player_shop_owner = ""
       @player_shop_name = ""
       @player_shop_buy_items = {}
       @player_shop_buy_selected = 0
       initialize_player_shops
   end
end


.rb extension: ShowHide

#------------------------------------------------------------------------------
#                  RMX-OS Player Shops                                            
#                     by Wizered67                                                          
#                          V 0.89                                                              
# All errors should be reported at www.chaos-project.com.                  
#------------------------------------------------------------------------------
module RMXOS

#------------------------------------------------------------------
# Passes the extension's main module to RMX-OS on the top
# level so it can handle this extension.
# Returns: Module of this extension for update.
#------------------------------------------------------------------
def self.load_current_extension
return PlayerShops
end

end

#======================================================================
# module PlayerShops
#======================================================================

module PlayerShops

# extension version
VERSION = 1.0
# required RMX-OS version
RMXOS_VERSION = 2.0
# whether the server should update this extension in an idividual thread or not
SERVER_THREAD = true
# the extension's name/identifier
IDENTIFIER = 'Player Shops'

SHOPS_CREATED_FILENAME = './createdshops.dat'
SHOPS_DATA_FILENAME = './shopsdata.dat'
MONEY_OWED_FILENAME = './moneyowed.dat'
# :::: START Configuration
# - YOUR CONFIGURATION HERE
# :::: END Configuration

#------------------------------------------------------------------
# Initializes the extension (i.e. instantiation of classes).
#------------------------------------------------------------------
def self.initialize
# create mutex
@mutex = Mutex.new
@shops_created = {}
if FileTest.exist?(SHOPS_CREATED_FILENAME)
file = File.open(SHOPS_CREATED_FILENAME, 'r')
created = Marshal.load(file)
file.close
created.each_key {|map| @shops_created[map] = created[map]}
end
@max_shop_id = 1
if @shops_created != {}
for map in @shops_created.keys
for id in @shops_created[map]
@max_shop_id = id + 1 if id >= @max_shop_id
end
end
end
@shop_data = {}
if FileTest.exist?(SHOPS_DATA_FILENAME)
file = File.open(SHOPS_DATA_FILENAME, 'r')
data = Marshal.load(file)
file.close
for map in @shops_created.keys
for id in @shops_created[map]
@shop_data[id] = data[id]
end
end
end
@money_owed = {}
if FileTest.exist?(MONEY_OWED_FILENAME)
file = File.open(MONEY_OWED_FILENAME, 'r')
money = Marshal.load(file)
file.close
money.each_key {|p| @money_owed[p] = money[p]}
end
#puts @shop_data
end
#------------------------------------------------------------------
# Gets the local extension mutex.
#------------------------------------------------------------------
def self.mutex
return @mutex
end
#------------------------------------------------------------------
# Calls constant updating on the server.
#------------------------------------------------------------------
def self.main
# while server is running
while RMXOS.server.running
@mutex.synchronize {
self.server_update
}
sleep(0.1) # 0.1 seconds pause, decreases server load
end
end
#------------------------------------------------------------------
# Handles the server update.
#------------------------------------------------------------------
def self.server_update
user_ids = []
RMXOS.clients.get.each {|c| user_ids.push(c.player.user_id) if c.player.user_id > 0}
for shop in @shop_data.keys
if !user_ids.include?(@shop_data[shop][2])
@shop_data[shop][2] = 0 #if a player isn't online, change it so no one is using the shop they were previously using
end
end
end
#------------------------------------------------------------------
# Handles updating from a client.
# client - Client instance (from Client.rb)
# Returns: Whether to stop check the message or not.
#------------------------------------------------------------------
def self.client_update(client)
case client.message
when /\AENT\Z/ # enter server, send login money
user_id = client.player.user_id
if @money_owed.keys.include?(user_id) && @money_owed[user_id] != 0
client.send('RMPS', @money_owed[user_id])
@money_owed[user_id] = 0
self.save_money_owed
end


when /\ATSES\t(.+)/ #try editting a shop
shop_id = $1.to_i
if !@shop_data.keys.include?(shop_id)
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "There is no shop with that id."))
client.send('EIS')
return true
end
if client.player.user_id != @shop_data[shop_id][1]
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "You do not own that shop."))
client.send('EIS')
return true
end
if @shop_data[shop_id][2] != 0
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "Selected shop is already in use."))
return true
end
@shop_data[shop_id][2] = client.player.user_id
client.send('OESC', @shop_data[shop_id][0], @shop_data[shop_id][4])
return true

when /\ASMP\t(.+)\t(.+)/ #send money to da people (if not online, add to money owed hash)
user_id = $1.to_i
amount = $2.to_i
user_ids = []
return if user_id == client.player.user_id
RMXOS.clients.get.each {|c| user_ids.push(c.player.user_id) if c.player.user_id > 0}
if user_ids.include?(user_id)
client.sender.send_to_id(user_id, RMXOS.make_message('RMPS', amount))
else
if @money_owed.include?(user_id)
@money_owed[user_id] += amount
else
@money_owed[user_id] = amount
end
self.save_money_owed
#puts @money_owed
end
return true

when /\ACNPS\t(.+)\t(.+)\t(.+)\t(.+)/ #create new player shop
owner_name = $1
owner_id = $2.to_i
map_id = $3.to_i
shop_name = $4
@shops_created[map_id] = [] if @shops_created[map_id] == nil
@shops_created[map_id].push(@max_shop_id)
@shop_data[@max_shop_id] = [owner_name, owner_id, 0, {}, shop_name]
client.send('NSI', @max_shop_id)
puts "New shop created by " + client.player.username + " on map " + map_id.to_s + " with id " + @max_shop_id.to_s + " and name " + shop_name
@max_shop_id += 1
self.save_shop_data
self.save_shop_list

return true

when /\ARPS\t(.+)\t(.+)/ #remove player shop
shop_id = $1.to_i
map_id = $2.to_i
if !@shops_created.keys.include?(shop_id) || !@shop_data.keys.include?(shop_id)
return true
end
@shops_created[map_id].delete(shop_id)
@shop_data[shop_id] = nil
self.save_shop_data
self.save_shop_list
return true


when /\ASMSM\t(.+)/
#puts "Request to send shop list"
map_id = $1.to_i
message = []
puts @shop_data
if @shops_created[map_id] != nil
for sid in @shops_created[map_id]
message.push(" " + @shop_data[sid][0] + "'s " + @shop_data[sid][4] + " [" + sid.to_s + "]") if @shop_data[sid] != nil
end
end
if message == []
text = "No shops on this map."
else
text = "Shops on map: " + message.join(',')
end
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorOk, 0, text))
#puts text
return true


when /\ASMDS\t(.+)/ #send map data on shops
map_id = $1.to_i
if !@shops_created.keys.include?(map_id)
return true
end
client.send('LMDS', [@shops_created[map_id].inspect])
return true

when /\ASSD\t(.+)\t(.+)/ #send specific shop data
shop_id = $1.to_i
map_id = $2.to_i
if !@shop_data.keys.include?(shop_id)
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "Selected shop does not exist."))
client.send('EISB')
return true
end
if @shops_created[map_id] == nil || !@shops_created[map_id].include?(shop_id)
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "Selected shop is not on the same map."))
return true
end
#puts @shop_data[shop_id][2]
self.server_update
if @shop_data[shop_id][2] != 0
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "Selected shop is already in use."))
return true
end
@shop_data[shop_id][2] = client.player.user_id
client.send('LSSD', @shop_data[shop_id].inspect)
return true

when /\ASIU\t(.+)\t(.+)/ #change shop in use
shop_id = $1.to_i
user = $2.to_i
if !@shop_data.keys.include?(shop_id)
return true
end
@shop_data[shop_id][2] = user
return true

when /\ASSO\t(.+)\t(.+)\t(.+)/ #set shop owner
owner_name = $1
owner_id = $2.to_i
shop_id = $3.to_i
if !@shop_data.keys.include?(shop_id)
return true
end
data = @shop_data[shop_id]
data[0] = owner_name
data[1] = owner_id
return true


when /\ASCD\t(.+)\t(.+)\t(.+)\t(.+)\t(.+)/ #change shop data
shop_id = $1.to_i
item_id = $2
amount = $3.to_i
price = $4.to_i
type = $5.to_i
if !@shop_data.keys.include?(shop_id)
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "Selected shop does not exist."))
client.send('EIS')
return true
end
self.server_update
if @shop_data[shop_id][2] != 0 && @shop_data[shop_id][2] != client.player.user_id
client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorError, 0, "Selected shop is already in use. Please wait until the current user is done."))
return true
end
data = @shop_data[shop_id]
items = data[3]
access_id = item_id + "-" + type.to_s
if items[access_id] == nil
items[access_id] = []
items[access_id][1] = amount
items[access_id][0] = price
items[access_id][2] = type
items[access_id][3] = item_id.to_i
else
items[access_id][1] += amount
items[access_id][0] = price
items[access_id][2] = type
items[access_id][3] = item_id.to_i
if items[access_id][1] <= 0
@shop_data[shop_id][3].delete(access_id)
end
end
#puts @shop_data
self.save_shop_data
if (amount > 0)
#client.send(RMXOS.make_message('CHT', RMXOS::Data::ColorOk, 0, "Item(s) placed for sale."))
end
#puts "Item " + item_id + "-" + type.to_s + " is in quantity " + item[1].to_s + " at price " + item[0].to_s + " in shop " + shop_id.to_s

return true
end
return false

end

def self.save_shop_data
file = File.open(SHOPS_DATA_FILENAME, 'w')
Marshal.dump(@shop_data, file)
file.close
end

def self.save_money_owed
file = File.open(MONEY_OWED_FILENAME, 'w')
Marshal.dump(@money_owed, file)
file.close
end

def self.save_shop_list
file = File.open(SHOPS_CREATED_FILENAME, 'w')
Marshal.dump(@shops_created, file)
file.close
end

end






Instructions

Place the script below RMX-OS and add the extension. Optionally configure a creation price in the client script. 3 .dat files will automatically be created in the server to keep track of shops and money owed to players. The names can be optionally configured.

Commands (Note: some cannot be used if shop is in use already):

  • /makeshop NAME - Creates a shop with the given name. Names are seen by other players and can give a description of the shop or just be for fun.

  • /editshop ID - Edits the shop with the given id as long as you are the owner. Shop id can be seen in the shop list and is shown when creating a shop.

  • /buy ID - Enters the shop with the given id on the current map and allows you to buy goods. The shop owner can use this to get back unsold goods for free.

  • /shops - Shows a list of all shops on the current map, including names, owners, and ids.




Compatibility

Requires RMX-OS 2.0. Probably not ideal for use with custom shop systems. Let me know if you find other compatibility issues.


Credits and Thanks


  • Me

  • Blizzard for making RMX-OS

  • Whitespirits for requesting and testing




Author's Notes

This is definitely my biggest script I've publically released. I hope you guys get some use out of it. As I mentioned above, I currently don't have RMXP so I won't be able to add many new features or fix bugs for now. If you have any questions, don't hesitate to let me know. Enjoy!