Main Menu
  • Welcome to The RPG Maker Resource Kit.

Module implementation

Started by TDS, October 21, 2012, 10:58:39 PM

0 Members and 1 Guest are viewing this topic.

TDS

Well, this is not really a script for the public, but it is something I've been working on for a while.

This is just one of many modules that I am creating to speed up the creation of battle systems and other complex systems. The main idea behind them is to be able to add a lot of functions to a class like movement or effects.

I wanted to create DLL for this, but my knowledge in that are vague at best at the moment. Also something I wanted to ask is if it is possible to inject a module into a class without having to modify said class.

For example instead of going to each class and doing "include module_name", just do it with an external call like "inject_module_to_class(class_name, module_name)".

And here is the script, this is just the movement part of the script, other effects such as jumping are ready but I am not satisfied with the math.


#==============================================================================
# ** TDS Module
#    Version: 1.0
#------------------------------------------------------------------------------

#==============================================================================
# * Import to Global Hash *
#==============================================================================
($imported ||= {})[:TDS_Module] = true

module TDS
end


module TDS
  #============================================================================
  # ** Movement - (Movement Addon)
  #    Version: 1.0
  #----------------------------------------------------------------------------
  #  This Module handles object movement.
  #============================================================================
  module Movement
    #--------------------------------------------------------------------------
    # * Constants (Structs)
    #--------------------------------------------------------------------------
    # Battle Action Base
    Move_Object = Struct.new(:real_x, :real_y, :x, :y, :x_speed, :y_speed)   
    #--------------------------------------------------------------------------
    # * Object Initialization
    #--------------------------------------------------------------------------
    def initialize(*atts)
      super(*atts)
      # Initialize Moving Objects Hash
      clear_moving_objects
    end
    #--------------------------------------------------------------------------
    # * Clear All Moving Objects
    #--------------------------------------------------------------------------
    def clear_moving_objects ; @moving_objects = {} end
    #--------------------------------------------------------------------------
    # * Determine if Object can be moved
    #--------------------------------------------------------------------------
    def can_move?(obj) ; obj.is_a?(Window) or obj.is_a?(Sprite) end
    #--------------------------------------------------------------------------
    # * Determine if object is moving
    #--------------------------------------------------------------------------
    def moving_object?(obj)
      # Return false if Moving Objects Hash does not include
      return false if !@moving_objects.has_key?(obj)
      # Get Moving Object Destination Properties
      dest = @moving_objects[obj]
      # If Object Real X & Y is not equal to Destination X & Y
      return (dest.real_x != dest.x or dest.real_y != dest.y)
    end
    #--------------------------------------------------------------------------
    # * Remove Moving Object
    #--------------------------------------------------------------------------
    def remove_moving_object(obj) ; @moving_objects.delete(obj) end
    #--------------------------------------------------------------------------
    # * Finish Object Movement (Instant Move)
    #--------------------------------------------------------------------------
    def finish_object_movement(obj)
      # Return if Moving Objects hash does not include object
      return if !@moving_objects.has_key?(obj)
      # Get Moving Object Destination Properties & Set Object X and Y Positions
      dest = @moving_objects[obj] ; obj.x = dest.real_x = dest.x ; obj.y = dest.real_y = dest.y
    end     
    #--------------------------------------------------------------------------
    # * Object Initialization
    #     speed : movement speed [X, Y] (Array or Integer (Set both speeds))
    #--------------------------------------------------------------------------
    def move_obj_to(obj, x, y, speed, appr = false)
      # Return if Object cannot be moved
      return if !can_move?(obj)
      # Create Speed Array for X & Y Speeds if it's a single integer
      speed = [speed, speed] if speed.is_a?(Numeric)
      # Destination Properties
      dest = Move_Object.new(obj.x, obj.y, x, y, speed.at(0), speed.at(1))
      # If Approximation flag is true
      if appr
        # Set Destination Approximated X & Y Speed Speed
        dest.x_speed = (obj.x - x).abs.to_f / speed.at(0) ; dest.y_speed = (obj.y - y).abs.to_f / speed.at(1)
      end
      # Set Moving Objects Destination Properties
      @moving_objects[obj] = dest
      # Finish Object Movement if Speed is 0
      finish_object_movement(obj) if speed.reduce(:+) == 0
    end     
    #--------------------------------------------------------------------------
    # * Move Object towards coordinates
    #     x     : x amout to move towards
    #     y     : y amount to move towards
    #     dur   : movement duration
    #     appr  : if false duration is treated as speed
    #--------------------------------------------------------------------------
    def move_obj_towards(obj, x, y, dur, appr = true)
      # Move To Coordinates
      move_obj_to(obj, obj.x + x, obj.y + y, dur, appr)
    end
    #--------------------------------------------------------------------------
    # * Frame Update
    #--------------------------------------------------------------------------
    def update(*args, &block)
      # Update Super if Defined
      super(*args, &block) if defined?(super)     
      # Update Moving Objects
      @moving_objects.each {|obj, dest| update_moving_objects(obj, dest)}
    end
    #--------------------------------------------------------------------------
    # * Update Moving Objects
    #     obj  : moving object
    #     dest : destination properties
    #--------------------------------------------------------------------------
    def update_moving_objects(obj, dest)
      # Return if Destination Properties are not for Object Movement
      return if !dest.is_a?(Move_Object)
      # Left & Right Movement
      obj.x = dest.real_x = [dest.real_x - dest.x_speed, dest.x].max if dest.x < dest.real_x
      obj.x = dest.real_x = [dest.real_x + dest.x_speed, dest.x].min if dest.x > dest.real_x
      # Up & Down Movement
      obj.y = dest.real_y = [dest.real_y - dest.y_speed, dest.y].max if dest.y < dest.real_y
      obj.y = dest.real_y = [dest.real_y + dest.y_speed, dest.y].min if dest.y > dest.real_y
      # Remove Moving Object if it's not moving anymore
      remove_moving_object(obj) if !moving_object?(obj)
    end
  end
end


module TDS
  #============================================================================
  # ** Movement - (Movement Addon)
  #    Version: 1.0
  #----------------------------------------------------------------------------
  #  This Module handles object movement.
  #============================================================================
  module Movement
    #--------------------------------------------------------------------------
    # * Determine if moving
    #--------------------------------------------------------------------------
    def moving? ; moving_object?(self) end
    #--------------------------------------------------------------------------
    # * Move to Coordinates
    #--------------------------------------------------------------------------
    def move_to(x, y, speed, appr = false) ; move_obj_to(self, x, y, speed, appr) end     
    #--------------------------------------------------------------------------
    # * Finish Movement (Instant Move)
    #--------------------------------------------------------------------------
    def finish_movement ; finish_object_movement(self) end     
  end
end


And here is an example that can tested with any game, just go to any menu and press the CTRL key.


#==============================================================================
# ** Scene_Base
#------------------------------------------------------------------------------
#  This is a super class of all scenes within the game.
#==============================================================================

class Scene_Base
  #--------------------------------------------------------------------------
  # * Included Modules
  #--------------------------------------------------------------------------
  include TDS::Movement 
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    super
    update_basic
    # If Input Trigger CTRL
    if Input.trigger?(:CTRL)
      instance_variables.each do |varname|
        ivar = instance_variable_get(varname)
        # If Instance Variable is a window or sprite
        if ivar.is_a?(Window) or ivar.is_a?(Sprite)
          x = rand(Graphics.width - ivar.width)
          y = rand(Graphics.height - ivar.height)
          move_obj_to(ivar, x, y, 15 + rand(15), true)
        end       
      end
    end
  end
end


Here is another example, this one is to display the use of the module within a class like a window instead of the whole scene like the one above.


#==============================================================================
# ** Scene_Menu
#------------------------------------------------------------------------------
#  This class performs the menu screen processing.
#==============================================================================

Window_Base.send(:include, TDS::Movement)

class Scene_Menu < Scene_MenuBase
  #--------------------------------------------------------------------------
  # * Post-Start Processing
  #--------------------------------------------------------------------------
  def post_start
    # Move Windows outside of screen
    @command_window.move_to(-@command_window.width, @command_window.y, 0)
    @gold_window.move_to(-@gold_window.width, @gold_window.y, 0)
    @status_window.move_to(Graphics.width + @status_window.width, @status_window.y, 0)
    # Deactivate Command Window
    @command_window.deactivate
    super
    # Move Windows into screen
    @command_window.move_to(0, 0, 15, true)   
    @gold_window.move_to(0, @gold_window.y, 15, true)
    @status_window.move_to(@command_window.width, @status_window.y, 15, true)   
    # Get Windows
    windows = [@command_window, @gold_window, @status_window]   
    # While Windows are Moving
    while windows.any? {|w| w.moving?}
      # Update Basic Components
      update_basic
      # Go Through Basic Input (Cancel/Confirm)
      [:C, :B].each {|input|
        # Finish Window Movement If Input Trigger
        windows.each {|w| w.finish_movement} if Input.trigger?(input)
      }
    end
    # Activate Command Window
    @command_window.activate
  end 
end

cozziekuns

Quote from: TDS on October 21, 2012, 10:58:39 PM
For example instead of going to each class and doing "include module_name", just do it with an external call like "inject_module_to_class(class_name, module_name)".

You can easily do that with the extend method, though I don't know if there are any differences between extend and include.

Example:


module TDS
  def do_something
    p "something"
  end
end

class Cozziekuns
end

Cozziekuns.do_something   # Error

Cozziekuns.extend(TDS)
Cozziekuns.do_something   # "something"


Haven't looked at your script yet, I'll do it when I get the time. Looks good so far though; a module like yours would be really helpful!

TDS

Thanks Cozziekuns, extend seems to work for injecting the methods of a module into a class, but it does not pass it onto the classes that inherit from it. I used extend on Window_Base, but it did not seem to work like the include method does, the methods did seemed to have been passed onto the new classes, but I could not use them.

D&P3

This goes a little out of what I know. But is there a way to check every class name in an entire project?
if there is, a 'for' loop making a list of every class name could be used to use that extend method?

Going out on a limb here :-\
All of my scripts are totally free to use for commercial use. You don't need to ask me for permission. I'm too lazy to update every single script post I ever made with this addendum. So ignore whatever "rule" I posted there. :)

All scripts can be found at: https://pastebin.com/u/diamondandplatinum3

modern algebra

#4
I will look at the main script soon, but just to address the current issue: you don't need to go beyond include, in my opinion; extend adds the methods to the singleton class for the instance. So, if you had:


module Cardboard
  def foldable?
    true
  end
end

a = Box.new
b = Box.new
a.extend(Cardboard)

# a.foldable? # true
# b.foldable? # NoMethod Error


The methods of Cardboard are only added to the a instance. When you use it on the class object itself, then you are actually adding the methods to the Class object for Box, so if you have:


Box.extend(Cardboard)

a = Box.new
b = Box.new

# a.foldable? # NoMethod Error
# b.foldable? # NoMethod Error
# Box.foldable? # true


Anyway, I believe the method you are looking for if you want to add the methods to every instance of the same object is actually include itself, but since it is a private method you might need to do the following:


Box.send(:include, Cardboard)

a = Box.new
b = Box.new

# a.foldable? # true
# b.foldable? # true
# Box.foldable? # NoMethod Error

pacdiggity

I'd just like to say I've always loved the classes and whatnot MA uses when explaining a function. Thank you. That will be all from me for the time being.
it's like a metaphor or something i don't know

TDS

It worked like a charm modern algebra.

I've updated the module to handle self methods as well and here is another example or the method implementation into the scene_menu.


#==============================================================================
# ** Scene_Menu
#------------------------------------------------------------------------------
#  This class performs the menu screen processing.
#==============================================================================

Window_Base.send(:include, TDS::Movement)

class Scene_Menu < Scene_MenuBase
  #--------------------------------------------------------------------------
  # * Post-Start Processing
  #--------------------------------------------------------------------------
  def post_start
    # Move Windows outside of screen
    @command_window.move_to(-@command_window.width, @command_window.y, 0)
    @gold_window.move_to(-@gold_window.width, @gold_window.y, 0)
    @status_window.move_to(Graphics.width + @status_window.width, @status_window.y, 0)
    # Deactivate Command Window
    @command_window.deactivate
    super
    # Move Windows into screen
    @command_window.move_to(0, 0, 15, true)   
    @gold_window.move_to(0, @gold_window.y, 15, true)
    @status_window.move_to(@command_window.width, @status_window.y, 15, true)   
    # Get Windows
    windows = [@command_window, @gold_window, @status_window]   
    # While Windows are Moving
    while windows.any? {|w| w.moving?}
      # Update Basic Components
      update_basic
      # Go Through Basic Input (Cancel/Confirm)
      [:C, :B].each {|input|
        # Finish Window Movement If Input Trigger
        windows.each {|w| w.finish_movement} if Input.trigger?(input)
      }
    end
    # Activate Command Window
    @command_window.activate
  end 
end


All it does is move the windows outside of the screen, then it moves them back into the screen.

modern algebra

I have some thoughts, but don't have the time to prepare them right at this moment. I will get back to you soon though.

TDS

That's great, I was hoping to see more activity though. Also more information about module use from other scripters and their experience working with it.

When I started this I was using a hash with symbols like @move_mod[:real_x], @move_mod[:real_y], etc that basically did the same thing as the struct, but I wasn't sure if it was slower or faster.