roninator2

Triacular - Steal Shop v1.06

Dec 4th, 2024
10
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 27.58 KB | None | 0 0
  1. #==============================================================================
  2. #
  3. # ▼ Triacular - Steal Shop v1.06
  4. # -- Last Updated: 2019.03.13
  5. # -- Level: Easy, Normal
  6. # -- Requires: n/a
  7. #
  8. #==============================================================================
  9. #==============================================================================
  10. # ▼ Introduction
  11. # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  12. # This script allows stealing of items in scene shop, made for RPG Maker VX Ace.
  13. #
  14. # Please give credit to Triacular
  15. # free for non-commercial and commercial uses
  16. #==============================================================================
  17. ($imported ||= {})[:Triacular_ShopSteal] = 1.06
  18. #==============================================================================
  19. # ▼ Versions Updates
  20. # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  21. # 2019-01-26 - Initial script                         - Triacular
  22. # 2019-01-27 - Reduced duplicate parts                - Triacular
  23. # 2019-02-12 - Added switch to disable script         - Triacular
  24. # 2019-02-13 - Added alias, removed global switches   - Roninator2
  25. # 2019-03-03 - Changed to not show the number window  - Roninator2
  26. # 2019-03-13 - Several Changes. Listed below          - CaRaCrAzY_Petrassi
  27. #   ▼ [Compatibility]
  28. #     Moved all classes inside a namespace module
  29. #     Changed the use of Aliasing to Method Wrapping    
  30. #     Added a component module for wrapping extension objects
  31. #     Moved all Scene_Shop's new methods to an extension class
  32. #     Added version data to $imported global variable
  33. #   ▼ [Convenience]
  34. #     Moved constants to the module root
  35. #     Transformed the DICE constant into VARIABLE_STEAL_BONUS
  36. #   ▼ [Features]
  37. #     Displays the Stealing Success rate insteand of item's price.
  38. #     Added a constant for controling the [steal] command's position in the list
  39. #     Added a switch to enable or disable steal command mid-game
  40. #     Added a constant for inputing a formula for General Stealing Success Rate
  41. #     Entries in the shop that has zero percent chance of stealing are disabled
  42. #     Added notetag support for editing Custom Stealing Success Rate formulas
  43. #==============================================================================
  44. #==============================================================================
  45. # ▼ Notetags
  46. # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  47. # This script support the following notetags:
  48. #
  49. #-------------------------------------------------------------------------------
  50. #   <steal: x>
  51. #-------------------------------------------------------------------------------
  52. #   This will add a custom calculation method for a particular item. This
  53. #   means that the default Success Rate formula will be replaced for this one.
  54. #     x : formula
  55. #
  56. #   Considerations about the Formula:
  57. #   The formula is an actual piece of code that will be evaluated, its final
  58. #   output must be a number between zero and one that will be converted into a
  59. #   percentage; and these are the following values that will be passed to it:
  60. #     a : Game Party   (Same thing as $game_party)
  61. #     b : item         (The item itself)
  62. #     p : item's price (converted to float, used for calculating divisions)
  63. #
  64. #   Examples:
  65. #   <steal: a.agi / p>
  66. #   <steal: 0.5>
  67. #   <steal: a.agi / (a.agi + p / 10.0)>
  68. #==============================================================================
  69.  
  70. #===============================================================================
  71. # ** TRIACULAR
  72. #-------------------------------------------------------------------------------
  73. #   Module used for namespacing the Shop Steal System
  74. #   Everything about this system is contained inside this module.
  75. #===============================================================================
  76.  
  77. module TRIACULAR
  78.   #-----------------------------------------------------------------------------
  79.   # * ID of the Switch that enables the script
  80.   #-----------------------------------------------------------------------------
  81.   SWITCH_ENABLE        = 22    
  82.  
  83.   #-----------------------------------------------------------------------------
  84.   # * ID of the Switch that will become ON when stealing fails
  85.   #-----------------------------------------------------------------------------
  86.   SWITCH_STEAL         = 21    
  87.  
  88.   #-----------------------------------------------------------------------------
  89.   # * ID of the Switch that enable/disable the shop's [Steal] command at runtime
  90.   #-----------------------------------------------------------------------------
  91.   SWITCH_COMMAND       = 23    
  92.  
  93.   #-----------------------------------------------------------------------------
  94.   # * ID of the Variable responsible for giving a percent bonus to steal chance
  95.   #-----------------------------------------------------------------------------
  96.   VARIABLE_STEAL_BONUS = 20    
  97.  
  98.   #-----------------------------------------------------------------------------
  99.   # * [Steal] command's name in the Shop's command list
  100.   #-----------------------------------------------------------------------------
  101.   VOCAB_STEAL          = "Steal"
  102.  
  103.   #-----------------------------------------------------------------------------
  104.   # * Steal command's position at Shop's command list
  105.   #   Useful if you want the [Steal] command to appear between [Buy] or [Sell]
  106.   #-----------------------------------------------------------------------------
  107.   COMMAND_POSITION     = 2      
  108.  
  109.   #-----------------------------------------------------------------------------
  110.   # * Dynamically increases the number of columns in Shop's command list to fit
  111.   #   the [Steal] command entry.
  112.   #-----------------------------------------------------------------------------
  113.   DYNAMIC_COL_COUNT    = true  
  114.  
  115.   #-----------------------------------------------------------------------------
  116.   # * General Stealing Success Rate formula usef for all items lacking a Custom
  117.   #   Stealing Formula notetag.
  118.   #-----------------------------------------------------------------------------
  119.   #     a : Game Party (Same thing as $game_party)
  120.   #     b : item
  121.   #     p : item's price (converted to float, used for calculating divisions)
  122.   #-----------------------------------------------------------------------------
  123.   STEAL_FORMULA        = "a.agi / p"
  124.  
  125.   #-----------------------------------------------------------------------------
  126.   # * Maximum Stealing Rate chance
  127.   #-----------------------------------------------------------------------------
  128.   STEAL_RATE_CAP       = 0.9
  129.  
  130.   #=============================================================================
  131.   # ** Component
  132.   #-----------------------------------------------------------------------------
  133.   #  Component used to avoid poluting the engine classes with new methods.
  134.   #=============================================================================
  135.  
  136.   module Component
  137.     #---------------------------------------------------------------------------
  138.     # * Forwards calls from a class public interface through an attribute
  139.     #---------------------------------------------------------------------------
  140.     #   Public instance method calls from a given class are forwarded to the
  141.     #   given attribute.
  142.     #
  143.     #   By design, method calls from super classes are not forwarded. You
  144.     #   should explicitly invoke this command again for each super class you
  145.     #   need its calls forwarded.
  146.     #---------------------------------------------------------------------------
  147.     def forward(klass, attribute)
  148.       klass.public_instance_methods(false).each do |meth|
  149.         define_method(meth) { |*args| self.send(attribute).send(meth, *args) }
  150.       end
  151.     end
  152.     #---------------------------------------------------------------------------
  153.     # * Adds itself as a component attribute into a compositor class
  154.     #---------------------------------------------------------------------------
  155.     #   This command is equivalent to reopenning a class definition and adding
  156.     #   a getter for lazily loading and retrieving a component's instance.
  157.     #
  158.     #   That means every instance of a composite class will wrap a lazy loaded
  159.     #   component instance through an automatically generated attribute.
  160.     #---------------------------------------------------------------------------
  161.     def compose(composite, attr_name)
  162.       component = self
  163.       body = eval("proc { @#{attr_name} ||= component.new(self) }")
  164.       composite.send(:define_method, attr_name) { instance_eval(&body) }
  165.     end
  166.     #---------------------------------------------------------------------------
  167.     # * Generates attribute readers for getting forwarded instance variables
  168.     #---------------------------------------------------------------------------
  169.     #   This method really breaks incapsulation. Use it only when dealing with
  170.     #   compositors that lack attr_readers.
  171.     #---------------------------------------------------------------------------
  172.     def forward_reader(compositor, *args)
  173.       args.each do |variable|
  174.         define_method(variable) do
  175.           self.send(compositor).instance_variable_get("@#{variable}".to_sym)
  176.         end
  177.       end
  178.     end
  179.   end
  180.  
  181.   #==============================================================================
  182.   # ** Window_ShopCommand
  183.   #------------------------------------------------------------------------------
  184.   #  Wraping and redefining methods inside Window_ShopCommand class
  185.   #==============================================================================
  186.  
  187.   class ::Window_ShopCommand
  188.     #---------------------------------------------------------------------------
  189.     # * Wraping Methods for redefinition
  190.     #---------------------------------------------------------------------------
  191.     old_make_command_list = instance_method :make_command_list
  192.     old_col_max           = instance_method :col_max
  193.     #---------------------------------------------------------------------------
  194.     # * Redefining method
  195.     #---------------------------------------------------------------------------
  196.     #   Updates the shining command effect
  197.     #---------------------------------------------------------------------------
  198.     define_method :make_command_list do |*args|
  199.       expectations = old_make_command_list.bind(self).(*args)
  200.       return expectations unless $game_switches[SWITCH_ENABLE]
  201.       command = {
  202.         name:    VOCAB_STEAL,
  203.         symbol:  :steal,
  204.         enabled: $game_switches[SWITCH_COMMAND],
  205.       }
  206.       @list.insert([[COMMAND_POSITION, 0].max, @list.size].min, command)
  207.       expectations
  208.     end
  209.     #--------------------------------------------------------------------------
  210.     # * Get Digit Count
  211.     #--------------------------------------------------------------------------
  212.     define_method :col_max do |*args|
  213.       old_col_max.bind(self).(*args) + ($game_switches[SWITCH_ENABLE] ? 1 : 0)
  214.     end if DYNAMIC_COL_COUNT
  215.   end
  216.  
  217.   #=============================================================================
  218.   # ** Scene_Shop
  219.   #-----------------------------------------------------------------------------
  220.   #   Wraping and redefining methods inside Scene_Shop class
  221.   #=============================================================================
  222.  
  223.   class ::Scene_Shop
  224.     #---------------------------------------------------------------------------
  225.     # * Wraping Methods for redefinition
  226.     #---------------------------------------------------------------------------
  227.     old_start                 = instance_method :start
  228.     old_create_command_window = instance_method :create_command_window
  229.     #---------------------------------------------------------------------------
  230.     # * Redefining method
  231.     #---------------------------------------------------------------------------
  232.     #   Adding Steal Window creation to scene Start
  233.     #---------------------------------------------------------------------------
  234.     define_method :start do |*args|
  235.       expectations  = old_start.bind(self).(*args)
  236.       wy            = @dummy_window.y
  237.       wh            = @dummy_window.height
  238.       @steal_window = Window_ShopSteal.new(0, wy, wh, @goods)
  239.       triacular_shopsteal.create_steal_window
  240.       expectations
  241.     end
  242.     #---------------------------------------------------------------------------
  243.     # * Redefining method
  244.     #---------------------------------------------------------------------------
  245.     # * Adding :steal handler to command window
  246.     #---------------------------------------------------------------------------
  247.     define_method :create_command_window do |*args|
  248.       expectations = old_create_command_window.bind(self).(*args)
  249.       meth         = triacular_shopsteal.method(:command_steal)
  250.       @command_window.set_handler(:steal, meth)
  251.       expectations
  252.     end
  253.   end
  254.  
  255.   #=============================================================================
  256.   # ** Stealable_Item
  257.   #-----------------------------------------------------------------------------
  258.   #  Class containing steal data to be inserted into RPG::BaseItem
  259.   #  Used almost exclusively to extract notetags about stealing.
  260.   #=============================================================================
  261.  
  262.   class Stealable_Item
  263.     #---------------------------------------------------------------------------
  264.     # * Macros
  265.     #---------------------------------------------------------------------------
  266.     extend  Component                      
  267.     compose RPG::BaseItem, :triacular_shopsteal
  268.     forward RPG::BaseItem, :item          
  269.     #---------------------------------------------------------------------------
  270.     # * Public instance attributes
  271.     #---------------------------------------------------------------------------
  272.     attr_reader :item # BaseItem wrapping this object
  273.     #---------------------------------------------------------------------------
  274.     # * Object Initialization
  275.     #---------------------------------------------------------------------------
  276.     def initialize(item)
  277.       @item = item
  278.     end
  279.     #-------------------------------------------------------------------------
  280.     # * Calculates the chances of stealing this item
  281.     #-------------------------------------------------------------------------
  282.     def rate
  283.       rate = steal_rate_formula.($game_party, item, item.price.to_f)
  284.       rate += $game_variables[VARIABLE_STEAL_BONUS] / 100.0
  285.       [[rate, 0].max, STEAL_RATE_CAP].min
  286.     end
  287.    
  288.     private
  289.     #---------------------------------------------------------------------------
  290.     # * Formula for calculating the chances of stealing this item from a shop
  291.     #---------------------------------------------------------------------------
  292.     def steal_rate_formula
  293.       @steal_rate_formula ||= retrieve_steal_rate_data
  294.     end
  295.     #---------------------------------------------------------------------------
  296.     # * Steal Rate formula from a retrieved notetag
  297.     #---------------------------------------------------------------------------
  298.     def retrieve_steal_rate_data
  299.       if match_data = steal_rate_tag
  300.         eval("lambda {|a, b, p| #{match_data[:value]} }")
  301.       else
  302.         Steal.default_rate
  303.       end
  304.     end
  305.     #---------------------------------------------------------------------------
  306.     # * Match_data from all matches of <steal: formula>
  307.     #---------------------------------------------------------------------------
  308.     def steal_rate_tag
  309.       Notetag.scan_tags(note, :steal, :single).first
  310.     end
  311.   end
  312.  
  313.   #=============================================================================
  314.   # ** Window_ShopSteal
  315.   #-----------------------------------------------------------------------------
  316.   #  This window displays a list of steal goods on the shop screen.
  317.   #=============================================================================
  318.  
  319.   class Window_ShopSteal < Window_Selectable
  320.     #--------------------------------------------------------------------------
  321.     # * Public Instance Variables
  322.     #--------------------------------------------------------------------------
  323.     attr_reader :status_window # Status window
  324.     #--------------------------------------------------------------------------
  325.     # * Object Initialization
  326.     #--------------------------------------------------------------------------
  327.     def initialize(x, y, height, shop_goods)
  328.       super(x, y, window_width, height)
  329.       @shop_goods = shop_goods
  330.       refresh
  331.       select(0)
  332.     end
  333.     #--------------------------------------------------------------------------
  334.     # * Get Window Width
  335.     #--------------------------------------------------------------------------
  336.     def window_width
  337.       return 304
  338.     end
  339.     #--------------------------------------------------------------------------
  340.     # * Get Number of Items
  341.     #--------------------------------------------------------------------------
  342.     def item_max
  343.       @data ? @data.size : 1
  344.     end
  345.     #--------------------------------------------------------------------------
  346.     # * Get Item
  347.     #--------------------------------------------------------------------------
  348.     def item
  349.       @data[index]
  350.     end
  351.     #--------------------------------------------------------------------------
  352.     # * Get Activation State of Selection Item
  353.     #--------------------------------------------------------------------------
  354.     def current_item_enabled?
  355.       enable?(@data[index])
  356.     end
  357.     #--------------------------------------------------------------------------
  358.     # * Get Price of Item
  359.     #--------------------------------------------------------------------------
  360.     def price(item)
  361.       @price[item]
  362.     end
  363.     #--------------------------------------------------------------------------
  364.     # * Display in Enabled State?
  365.     #--------------------------------------------------------------------------
  366.     def enable?(item)
  367.       item && price(item) > 0 && !$game_party.item_max?(item)
  368.     end
  369.     #--------------------------------------------------------------------------
  370.     # * Refresh
  371.     #--------------------------------------------------------------------------
  372.     def refresh
  373.       make_item_list
  374.       create_contents
  375.       draw_all_items
  376.     end
  377.     #--------------------------------------------------------------------------
  378.     # * Create Item List
  379.     #--------------------------------------------------------------------------
  380.     def make_item_list
  381.       containers = [$data_items, $data_weapons, $data_armors]
  382.       @data = @shop_goods.map { |goods| item = containers[goods[0]][goods[1]] }
  383.       @price = Hash[@data.map { |item| [item, item.triacular_shopsteal.rate] }]
  384.     end
  385.     #--------------------------------------------------------------------------
  386.     # * Draw Item
  387.     #--------------------------------------------------------------------------
  388.     def draw_item(index)
  389.       item = @data[index]
  390.       rect = item_rect(index)
  391.       draw_item_name(item, rect.x, rect.y, enable?(item))
  392.       rect.width -= 4
  393.       draw_text(rect, "#{(price(item) * 100).to_i}%", 2)
  394.     end
  395.     #--------------------------------------------------------------------------
  396.     # * Set Status Window
  397.     #--------------------------------------------------------------------------
  398.     def status_window=(status_window)
  399.       @status_window = status_window
  400.       call_update_help
  401.     end
  402.     #--------------------------------------------------------------------------
  403.     # * Update Help Text
  404.     #--------------------------------------------------------------------------
  405.     def update_help
  406.       @help_window.set_item(item) if @help_window
  407.       @status_window.item = item if @status_window
  408.     end
  409.   end
  410.  
  411.   #=============================================================================
  412.   # ** Steal
  413.   #-----------------------------------------------------------------------------
  414.   #  Class containing additional data to be inserted into Scene_Shop
  415.   #=============================================================================
  416.  
  417.   module Steal
  418.     #-------------------------------------------------------------------------
  419.     # * Default Stealing Success formula defined at the start of the script.
  420.     #   This formula is used for items lacking a <steal:formula> Notetag.
  421.     #-------------------------------------------------------------------------
  422.     def self.default_rate
  423.       @default_rate ||= eval("lambda {|a, b, p| #{STEAL_FORMULA} }")
  424.     end
  425.   end
  426.  
  427.   #=============================================================================
  428.   # ** Scene_ShopSteal
  429.   #-----------------------------------------------------------------------------
  430.   #  Class containing additional data to be inserted into Scene_Shop
  431.   #=============================================================================
  432.  
  433.   class Scene_ShopSteal
  434.     #---------------------------------------------------------------------------
  435.     # * Macros
  436.     #---------------------------------------------------------------------------
  437.     extend  Component              
  438.     compose Scene_Shop    , :triacular_shopsteal        
  439.     forward Scene_Shop    , :scene
  440.     forward Scene_MenuBase, :scene
  441.     forward Scene_Base    , :scene
  442.     #--------------------------------------------------------------------------
  443.     # * Public Instance Attributes
  444.     #--------------------------------------------------------------------------
  445.     forward_reader :scene, :command_window, :help_window, :dummy_window
  446.     forward_reader :scene, :status_window , :goods      , :viewport
  447.     forward_reader :scene, :steal_window
  448.     #---------------------------------------------------------------------------
  449.     # * Public instance attributes
  450.     #---------------------------------------------------------------------------
  451.     attr_reader :scene # Scene wraping this object. The compositor.
  452.     #---------------------------------------------------------------------------
  453.     # * Object Initialization
  454.     #---------------------------------------------------------------------------
  455.     def initialize(scene)
  456.       @scene = scene
  457.     end
  458.     #--------------------------------------------------------------------------
  459.     # * Create Steal Window
  460.     #--------------------------------------------------------------------------
  461.     def create_steal_window
  462.       steal_window.viewport      = viewport
  463.       steal_window.help_window   = help_window
  464.       steal_window.status_window = status_window
  465.       steal_window.hide
  466.       steal_window.set_handler(:ok,     method(:on_steal_ok))
  467.       steal_window.set_handler(:cancel, method(:on_steal_cancel))
  468.     end
  469.     #--------------------------------------------------------------------------
  470.     # * The current selected item
  471.     #--------------------------------------------------------------------------
  472.     def item
  473.       steal_window.item
  474.     end
  475.     #--------------------------------------------------------------------------
  476.     # * [Steal] Command
  477.     #--------------------------------------------------------------------------
  478.     def command_steal
  479.       dummy_window.hide
  480.       activate_steal_window
  481.     end
  482.     #--------------------------------------------------------------------------
  483.     # * Category Steal [Cancel]
  484.     #--------------------------------------------------------------------------
  485.     def on_steal_cancel
  486.       command_window.activate
  487.       dummy_window.show
  488.       steal_window.hide
  489.       status_window.hide
  490.       status_window.item = nil
  491.       help_window.clear
  492.     end
  493.     #--------------------------------------------------------------------------
  494.     # * Category Steal [OK]
  495.     #--------------------------------------------------------------------------
  496.     def on_steal_ok
  497.       do_steal(1)
  498.     end
  499.     #--------------------------------------------------------------------------
  500.     # * Quantity Input [OK]
  501.     #--------------------------------------------------------------------------
  502.     def activate_steal_window
  503.       steal_window.show.activate
  504.       status_window.show
  505.     end
  506.     #--------------------------------------------------------------------------
  507.     # * Execute Steal
  508.     #--------------------------------------------------------------------------
  509.     def do_steal(number)
  510.       if rand <= item.triacular_shopsteal.rate
  511.         $game_party.gain_item(item, number)
  512.         activate_steal_window
  513.       else
  514.         $game_switches[SWITCH_STEAL] = true
  515.         SceneManager.goto(Scene_Map)
  516.       end
  517.     end
  518.   end
  519.  
  520.   #=============================================================================
  521.   # ** Notetag
  522.   #-----------------------------------------------------------------------------
  523.   #  Module for extrating Notetags
  524.   #=============================================================================
  525.  
  526.   module Notetag
  527.     class << self
  528.       #-------------------------------------------------------------------------
  529.       # * Extracts notetag data matching the given pattern type
  530.       #-------------------------------------------------------------------------
  531.       #     note : The note field or string
  532.       #     tag  : The tag's name i.e. :Expertise will search for <Expertise>
  533.       #     type : The notetag's pattern (:plain, :single or :pair)
  534.       #-------------------------------------------------------------------------
  535.       def scan_tags(note, tag, type)
  536.         note.to_enum(:scan, pattern(tag, type)).map { Regexp.last_match }
  537.       end
  538.      
  539.       private
  540.       #-------------------------------------------------------------------------
  541.       # * Return a regex for the given pattern to match the given tag_name
  542.       #-------------------------------------------------------------------------
  543.       def pattern(tag, type)
  544.         method(type).(tag)
  545.       end
  546.       #-------------------------------------------------------------------------
  547.       # * Regex for retrieving a notetag containing only one value
  548.       #     base format: <tag_name> or <tag_name:opt>
  549.       #-------------------------------------------------------------------------
  550.       def plain(tag_name)
  551.         %r{
  552.           (?: <\s*#{tag_name}\s*) # < tag_name    the tag name
  553.           (?: :\s*(?<opt>  .+?))? # : optional    the optional value
  554.           (?: \s*\/?>)            # />            the tag closing
  555.         }ix
  556.       end
  557.       #-------------------------------------------------------------------------
  558.       # * Regex for retrieving a notetag containing only one value
  559.       #     base format: <tag_name:value> or <tag_name:value,opt>
  560.       #-------------------------------------------------------------------------
  561.       def single(tag_name)
  562.         %r{
  563.           (?: <\s*#{tag_name}\s*) # < tag_name    the tag name
  564.           (?: :\s*(?<value>.+?))  # : value       the second value
  565.           (?: ,\s*(?<opt>  .+?))? # , optional    the optional value
  566.           (?: \s*\/?>)            # />            the tag closing
  567.         }ix
  568.       end
  569.       #-------------------------------------------------------------------------
  570.       # * Regex for retrieving a notetag containing two values
  571.       #     base format: <tag_name:key,value> or <tag_name:key,value,opt>
  572.       #-------------------------------------------------------------------------
  573.       def pair(tag_name)
  574.         %r{
  575.           (?: <\s*#{tag_name}\s*) # < tag_name    the tag name
  576.           (?: :\s*(?<key>  .+?))  # : key         the first value
  577.           (?: ,\s*(?<value>.+?))  # , value       the second value
  578.           (?: ,\s*(?<opt>  .+?))? # , optional    the optional value
  579.           (?: \s*\/?>)            # />            the tag closing
  580.         }ix
  581.       end
  582.     end
  583.   end
  584. end
Add Comment
Please, Sign In to add comment