RMX-OS Player Shops
Authors: Wizered67
Version: 0.89
Type: Player Shop System
Key Term: RMX-OS Plugin
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
Demo
N/A
Script
#------------------------------------------------------------------------------#
# 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
#------------------------------------------------------------------------------
# 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!