[RESOLVED] Help with Tsukihime's 2 Player Script

Started by Paladin, January 06, 2017, 12:19:16 pm

Previous topic - Next topic

Paladin

January 06, 2017, 12:19:16 pm Last Edit: January 08, 2017, 09:18:33 pm by Paladin
Greetings,

I am using RPG Maker Ace, and it's very fun to use. I am trying to implement local two player capability. I've searched the web and it appears the only option right now is this script from Tsukihime:

http://www.rpgmakercentral.com/topic/4128-simple-2-player-script-development/

I followed his instructions, and it works just as he described; However, it has limitations. It creates a copy of the player sprite and allows you to control that as a second player, but you can't make the two players have separate sprites (as far as I am aware!).

Now despite knowing practically nothing about script programming, I really wanted to try and solve this myself. So I spent a long while modifying things in the script editor, deleting and testing, modifying script (don't worry, I ensured to undo any of the damage I caused after I tested).

My tampering was not altogether useless however. I discovered the following:

In "Spriteset_Map" in the script editor, go to about line 60, and you'll see the "Create Character Sprite" section. At the bottom of that small section there is the following code:

@character_sprites.push(Sprite_Character.new(@viewport1, $game_player))
    @map_id = $game_map.map_id

When I saw 'game_player' at the end, I decided to try and change it to 'game_player2' like in Tsukihime's script. The results were exciting.   :)

The first actor of the initial party could be controlled with the multiplayer script (w,a,s,d keys), and second actor in the initial party could be controlled with the default keys.

In Database on the System tab you can change the sprite of both characters to whatever you wanted.

But there is one catch. The movement of the default keys player is now delayed. To change direction, you have to press the key 2-3 times, and the player will move in it's initial direction one tile before moving correctly. Very odd. It reminded me of followers.

My theory is that if the follower movement delay was gone, the movement of the default keys player would be normal.

But alas, I know nothing of scripting. So while I'm suspecting this to be the culprit:

Script Editor > Game_Follower, at about line 55 there is a section called "Pursue Preceding Character"

....I really have not the slightest idea on how to fix it. I could be way off base. Sorry for the lengthy description, it all seems so complicated.  :???:  Any help is appreciated!

KK20

I'm not sure I understand what you mean by your edit to Spriteset_Map; doing so just only shows one actor on the map: $game_player2. In fact, you would be drawing him twice because the 2-player script already adds $game_player2 in, as seen:

class Spriteset_Map
  alias :th_multiplayer_create_characters :create_characters

  def create_characters
    th_multiplayer_create_characters
    @character_sprites.push(Sprite_Character.new(@viewport1, $game_player2))
  end
end

If all you want is to make the second party member be drawn for $game_player2, you can just add this method inside class Game_Multiplayer

  def actor
    $game_party.battle_members[1]
  end

This was taken from the Game_Player class:

  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    @character_name = actor ? actor.character_name : ""
    @character_index = actor ? actor.character_index : 0
    @followers.refresh
  end
  #--------------------------------------------------------------------------
  # * Get Corresponding Actor
  #--------------------------------------------------------------------------
  def actor
    $game_party.battle_members[0]
  end

If you define the actor the player is supposed to represent, the game will set the graphic for you automatically in refresh.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Paladin

Quote from: KK20 on January 07, 2017, 02:08:12 am

If all you want is to make the second party member be drawn for $game_player2, you can just add this method inside class Game_Multiplayer

  def actor
    $game_party.battle_members[1]
  end



Thank you KK20, you're a genius!  :D That explanation makes total sense. I undid my modification to the Spriteset_Map, and followed your instructions. This mod to the script resolved the lag issue.

But there is another problem. Since the whole setup makes a follower become a second player, I went ahead and tested to see if I could gain a follower with both of the players. It appears this two player setup disables the ability to have followers at all. Is this script simply incompatible with the follower function?

KK20

If you have "Show Player Followers" enabled in the System tab, then only $game_player will have followers; $game_player2 will not. (If it's not doing this, you must have another script that's causing issues)
But I don't know why you would want to do that since your "party" must consist of two actors: one for $game_player and one for $game_player2. Followers are just party members that are not in the first slot. In this case, $game_player will have an actor that looks like $game_player2 follow them around.

If you can explain what it is that you want, I can probably point you in the right direction.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Paladin

Show Player Followers was enabled, so it must be script like you said.

I started a fresh project and copied over the fullkeyboard and multiplayer scripts only. I added the other player in the system tab, and ran the game. It appeared just like your screenshot - Player1 and Player2 with another Player2 sprite following Player1. I must have forgot to undo some code adding/deleting during my initial haphazard 'troubleshooting'.  :facepalm:

It makes sense. If "Show Player Followers" is disabled, then it shows up just like I want it but without the ability to see followers I actually want to add.

I obviously can't have another player 2 sprite following player 1 around.

To answer your question, I'm trying to make the game two player with the ability for a player to walk up to an NPC and trigger an event that adds that NPC to the party. I don't mind if it only can follow Player1.

I really appreciate your help, thank you!

KK20

I found this script that can help with making events follow the player: http://himeworks.com/2013/03/event-followers/
In order to make it work for two players, I had to do some slight edits.
Multiplayer: ShowHide

module DataManager

  class << self
    alias :th_multiplayer_create_game_objects :create_game_objects
    alias :th_multiplayer_make_save_contents :make_save_contents
    alias :th_multiplayer_extract_save_contents :extract_save_contents
  end

  def self.create_game_objects
    th_multiplayer_create_game_objects
    $game_player2 = Game_Multiplayer.new

  end

  def self.make_save_contents
    contents = th_multiplayer_make_save_contents
    contents[:player2] = $game_player2
    contents
  end

  def self.extract_save_contents(contents)
    th_multiplayer_extract_save_contents(contents)
    $game_player2 = contents[:player2]
  end

  def self.setup_new_game
    create_game_objects
    $game_party.setup_starting_members
    $game_map.setup($data_system.start_map_id)
    $game_player.moveto($data_system.start_x, $data_system.start_y)
    $game_player.refresh
    $game_player2.moveto($data_system.start_x, $data_system.start_y)
    $game_player2.refresh
    Graphics.frame_count = 0
  end
end

class Game_Multiplayer < Game_Player

  DOWN = Input::Y # S in Keyboard
  LEFT = Input::X # A in Keyboard
  RIGHT = Input::Z # D in Keyboard
  UP = Input::R # W in Keyboard
  ENTER = Input::L # Q in Keyboard
  RUN = Input::A # Shift in Keyboard

  #--------------------------------------------------------------------------
  # * Get Corresponding Actor
  #--------------------------------------------------------------------------
  def actor
    $game_party.battle_members[1]
  end
 
  def move_by_input
    return unless movable?
    return if $game_map.interpreter.running?
    if Input.press?(DOWN)
      move_straight(2)
    elsif Input.press?(LEFT)
      move_straight(4)
    elsif Input.press?(RIGHT)
      move_straight(6)
    elsif Input.press?(UP)
      move_straight(8)
    end
  end

  def update_nonmoving(last_moving)
    return if $game_map.interpreter.running?
    if last_moving
      $game_party.on_player_walk
      return if check_touch_event
    end
    if movable? && Input.trigger?(ENTER)
      return if get_on_off_vehicle
      return if check_action_event
    end
    update_encounter if last_moving
  end
 
  #--------------------------------------------------------------------------
  # * Trigger Map Event
  #     triggers : Trigger array
  #     normal   : Is priority set to [Same as Characters] ?
  #--------------------------------------------------------------------------
  def start_map_event(x, y, triggers, normal)
    return if $game_map.interpreter.running?
    $game_map.events_xy(x, y).each do |event|
      if event.trigger_in?(triggers) && event.normal_priority? == normal
        event.start(true)
      end
    end
  end
end

class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # * Start Event
  #--------------------------------------------------------------------------
  def start(player2=false)
    return if empty?
    @triggered_by = player2 ? $game_player2 : $game_player
    @starting = true
    lock if trigger_in?([0,1,2])
  end
end


class Spriteset_Map
  alias :th_multiplayer_create_characters :create_characters

  def create_characters
    th_multiplayer_create_characters
    @character_sprites.push(Sprite_Character.new(@viewport1, $game_player2))
  end
end


class Scene_Map < Scene_Base
  alias :th_multiplayer_map_update :update
  def update
    th_multiplayer_map_update
    $game_player2.update if $game_player2
  end
end

Followers: ShowHide

=begin
#===============================================================================
Title: Event Followers
Author: Hime
Date: Feb 19, 2015
--------------------------------------------------------------------------------
** Change log
Fev 19, 2015
   - event followers also move diagonally
Mar 31, 2014
   - moving behind leader had the y-positions mixed up
Jan 9, 2014
   - fixed bug where adding a follower to a leader was following the last
     follower of the leader, not the leader itself
Jul 31, 2013
   - added new following logic for more accurate following
Jul 25, 2013
   - fixed bug where event follower speed wasn't updated to leader's speed
Mar 29, 2013
   - Events following the player will now follow the last follower
   - Initial release
--------------------------------------------------------------------------------   
** Terms of Use
* Free to use in non-commercial projects
* Contact me for commercial use
* No real support. The script is provided as-is
* Will do bug fixes, but no compatibility patches
* Features may be requested but no guarantees, especially if it is non-trivial
* Credits to Hime Works in your project
* Preserve this header
--------------------------------------------------------------------------------
** Description

This script allows events to designate a "leader" that another character will
follow. This could be the player, the current event, or any other event.

Any characters following a leader will follow that leader.
--------------------------------------------------------------------------------
** Installation

Place this below Materials and above Main

--------------------------------------------------------------------------------
** Usage

The following method calls are available to Game_Character objects.

    follow(event_id)
    stop_follow
   
This can be called by any character object such as players or events.
If you make a script call and say

    follow(event_id)
   
Then the event specified by the move route will follow that character.
You can also say things like

    $game_player.follow(event_id)
    $game_map.events[3].follow(event_id)

If the event_id is -1, then the character will follow the player.
otherwise, it will follow the specified event on the current map.

To stop following a character, make the script call

    stop_follow
   
Again, remember from whose perspective the script call is being made from.

#===============================================================================
=end
$imported = {} if $imported.nil?
$imported["TH_EventFollowers"] = true
#===============================================================================
# ** Configuration
#===============================================================================
module TH
  module Event_Followers
  end
end
#===============================================================================
# ** Rest of Script
#===============================================================================
class Game_Character < Game_CharacterBase
 
  attr_reader :event_followers
 
  alias :th_event_followers_init_public_members :init_public_members
  def init_public_members
    th_event_followers_init_public_members
    @event_followers = Game_EventFollowers.new(self)
    @leader = nil
  end
 
  alias :th_event_followers_move_straight :move_straight
  def move_straight(d, turn_ok = true)
    @event_followers.move if passable?(@x, @y, d)
    th_event_followers_move_straight(d, turn_ok)
  end
 
  alias :th_event_followers_update :update
  def update
    th_event_followers_update
    update_following if following?
  end

  #-----------------------------------------------------------------------------
  # New
  #-----------------------------------------------------------------------------
  def update_following
    @move_speed = @leader.real_move_speed
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def following?
    !@leader.nil?
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def last_follower
    return @event_followers[-1] if @event_followers.size > 0
    return self
  end
 
  #-----------------------------------------------------------------------------
  # New. Backup this character's original settings
  #-----------------------------------------------------------------------------
  def store_original_settings
    @old_through = @through
    @old_move_speed = @move_speed
    @old_real_move_speed = @real_move_speed
    @old_transparent = @transparent
    @old_walk_anime = @walk_anime
    @old_step_anime = @step_anime
    @old_direction_fix = @direction_fix
    @old_opacity = @opacity
    @old_blend_type = @blend_type
  end
 
  #-----------------------------------------------------------------------------
  # New. Once we stop following another character, revert all original settings
  #-----------------------------------------------------------------------------
  def revert_original_settings
    @through = @old_through
    @move_speed = @old_move_speed
    @real_move_speed = @old_real_move_speed
    @transparent = @old_transparent
    @walk_anime = @old_walk_anime
    @step_anime = @old_step_anime
    @direction_fix = @old_direction_fix
    @opacity = @old_opacity
    @blend_type = @old_blend_type
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def get_follow_leader(event_id) #KK20
    # -1 is assumed to be the player
    if event_id == 0
      @triggered_by
    elsif event_id == -1
      $game_player
    elsif event_id == -2
      $game_player2
    else
      $game_map.events[event_id]
    end
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def chase_preceding_character
    unless moving?
      sx = distance_x_from(@preceding_character.x)
      sy = distance_y_from(@preceding_character.y)     
      if sx != 0 && sy != 0
        move_diagonal(sx > 0 ? 4 : 6, sy > 0 ? 8 : 2)
      elsif sx != 0
        move_straight(sx > 0 ? 4 : 6)
      elsif sy != 0
        move_straight(sy > 0 ? 8 : 2)
      end
    end
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def move_behind_leader
    x = @leader.x
    y = @leader.y
   
    case @leader.direction
    when 2
      y += 1
    when 4
      x += 1
    when 6
      x -= 1
    when 8
      y -= 1
    end
    x = [[1, x].max, $game_map.width-1].min
    y = [[1, y].max, $game_map.height-1].min
    moveto(x, y)
  end
 
  #-----------------------------------------------------------------------------
  # New. Begin following the specified event.
  #-----------------------------------------------------------------------------
  def follow(event_id=0)
    return if following?
    @leader = get_follow_leader(event_id)
    @preceding_character = @leader.last_follower
    @leader.add_event_follower(self)
    move_behind_leader
    store_original_settings
    @move_speed     = @leader.move_speed
    @transparent    = @leader.transparent
    @walk_anime     = @leader.walk_anime
    @step_anime     = @leader.step_anime
    @direction_fix  = @leader.direction_fix
    @opacity        = @leader.opacity
    @blend_type     = @leader.blend_type
    @through = true
  end
 
  #-----------------------------------------------------------------------------
  # New. Stop following a leader
  #-----------------------------------------------------------------------------
  def stop_follow
    @through = false
    @leader.remove_event_follower(self)
    @preceding_character = nil
    @leader = nil
    revert_original_settings
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def add_event_follower(char)
    @event_followers.add(char)
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def remove_event_follower(char)
    @event_followers.remove(char)
  end
 
  alias :th_event_followers_move_diagonal :move_diagonal
  def move_diagonal(horz, vert)
    @event_followers.move if diagonal_passable?(@x, @y, horz, vert)
    th_event_followers_move_diagonal(horz, vert)
  end
end

class Game_Event < Game_Character
  #-----------------------------------------------------------------------------
  # Ignore autonomous movement if following leader
  #-----------------------------------------------------------------------------
  alias :th_event_followers_update_self_movement :update_self_movement
  def update_self_movement
    return if following?
    th_event_followers_update_self_movement
  end
end

class Game_Player < Game_Character
 
  #-----------------------------------------------------------------------------
  # New. Since player happens to have different types of followers we have to
  # check
  #-----------------------------------------------------------------------------
  def last_follower
    return super if @event_followers.size > 0
    follower_size = $game_player.followers.visible_folloers.size
    return @followers[follower_size - 1] if follower_size > 0
    return self
  end
 
  #-----------------------------------------------------------------------------
  # Ignore player movement input if following leader
  #-----------------------------------------------------------------------------
  alias :th_event_followers_movable? :movable?
  def movable?
    return false if following?
    th_event_followers_movable?
  end
 
  alias :th_event_followers_dash? :dash?
  def dash?
    return false if following?
    th_event_followers_dash?
  end
end

#-------------------------------------------------------------------------------
# Similar to player followers, except instead of pulling data from the party
# members it simply holds references to existing events
#-------------------------------------------------------------------------------
class Game_EventFollowers < Game_Followers
  include Enumerable
 
  def initialize(leader)
    @data = []
  end
 
  def size
    @data.size
  end
 
  def index(char)
    @data.index(char)
  end
 
  def add(character)
    @data.push(character)
  end
 
  def remove(character)
    @data.delete(character)
  end 
end

class Game_Interpreter
  def follow(id=0)
    $game_map.events[@map_id].follow(id)
    true
  end
end

With the "Show Player Followers" disabled, you can create an event on the map with a Script command
follow

and this will automatically follow the player that talked to it. To remove the event from following, the command is
$game_map.events[ID].stop_follow

where ID is the event ID that is following a player.

You can specify a specific player to follow using the following:

follow(-1) # Follows player 1
follow(-2) # Follows player 2

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Paladin

KK20,

This works great!  :) I've had that "Event Followers" page open on my browser for a few days, but hadn't looked at. With your mods, it's perfect!

I went ahead and did another test involving transferring players from one map to the next. While the follower did not show up on the other map, I simply copied the event to the new map and ran it as a parallel process so he would automatically be following once they transferred. I read some of the comments at the bottom of the website link you sent me and it appears that events are not transferable, so my workaround will suffice quite well.

It did bring up another issue with the multiplayer script though - when you transfer the players to a new map, player2 shows up just a few tiles away. The big problem is if the location is really small, like a path only one map tile in width, then player 2 is not visible in the new location. I would like either to have player2 land on the same tile as player1, or maybe on a specified tile. I've looked at the multiplayer script, Scene_Map, Game_Follower, and Game_Party, but I don't see anything obvious.

Of course, I know hardly anything concerning script, so maybe the solution was right in front of me.

Also, when I set an event's move route to approach player, it goes toward player1. My idea for fixing that was to go to Script Editor > 'Game_Character' and add this:

def move_toward_player2
    move_toward_character($game_player2)
  end

Then, go to 'Game_Event' and modify the 'random' script in the "Move Type : Approach" section. I'm not sure if I'm on the right track, but I want an NPC to approach either of the two players (who ever is closest) and trigger with event touch.

Thanks again!

KK20

January 08, 2017, 08:45:44 pm #7 Last Edit: January 08, 2017, 09:31:42 pm by KK20
Follower: ShowHide

=begin
#===============================================================================
Title: Event Followers
Author: Hime
Date: Feb 19, 2015
--------------------------------------------------------------------------------
** Change log
Fev 19, 2015
   - event followers also move diagonally
Mar 31, 2014
   - moving behind leader had the y-positions mixed up
Jan 9, 2014
   - fixed bug where adding a follower to a leader was following the last
     follower of the leader, not the leader itself
Jul 31, 2013
   - added new following logic for more accurate following
Jul 25, 2013
   - fixed bug where event follower speed wasn't updated to leader's speed
Mar 29, 2013
   - Events following the player will now follow the last follower
   - Initial release
--------------------------------------------------------------------------------   
** Terms of Use
* Free to use in non-commercial projects
* Contact me for commercial use
* No real support. The script is provided as-is
* Will do bug fixes, but no compatibility patches
* Features may be requested but no guarantees, especially if it is non-trivial
* Credits to Hime Works in your project
* Preserve this header
--------------------------------------------------------------------------------
** Description

This script allows events to designate a "leader" that another character will
follow. This could be the player, the current event, or any other event.

Any characters following a leader will follow that leader.
--------------------------------------------------------------------------------
** Installation

Place this below Materials and above Main

--------------------------------------------------------------------------------
** Usage

The following method calls are available to Game_Character objects.

    follow(event_id)
    stop_follow
   
This can be called by any character object such as players or events.
If you make a script call and say

    follow(event_id)
   
Then the event specified by the move route will follow that character.
You can also say things like

    $game_player.follow(event_id)
    $game_map.events[3].follow(event_id)

If the event_id is -1, then the character will follow the player.
otherwise, it will follow the specified event on the current map.

To stop following a character, make the script call

    stop_follow
   
Again, remember from whose perspective the script call is being made from.

#===============================================================================
=end
$imported = {} if $imported.nil?
$imported["TH_EventFollowers"] = true
#===============================================================================
# ** Configuration
#===============================================================================
module TH
  module Event_Followers
  end
end
#===============================================================================
# ** Rest of Script
#===============================================================================
class Game_Character < Game_CharacterBase
 
  attr_reader :event_followers
 
  alias :th_event_followers_init_public_members :init_public_members
  def init_public_members
    th_event_followers_init_public_members
    @event_followers = Game_EventFollowers.new(self)
    @leader = nil
  end
 
  alias :th_event_followers_move_straight :move_straight
  def move_straight(d, turn_ok = true)
    @event_followers.move if passable?(@x, @y, d)
    th_event_followers_move_straight(d, turn_ok)
  end
 
  alias :th_event_followers_update :update
  def update
    th_event_followers_update
    update_following if following?
  end

  #-----------------------------------------------------------------------------
  # New
  #-----------------------------------------------------------------------------
  def update_following
    @move_speed = @leader.real_move_speed
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def following?
    !@leader.nil?
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def last_follower
    return @event_followers[-1] if @event_followers.size > 0
    return self
  end
 
  #-----------------------------------------------------------------------------
  # New. Backup this character's original settings
  #-----------------------------------------------------------------------------
  def store_original_settings
    @old_through = @through
    @old_move_speed = @move_speed
    @old_real_move_speed = @real_move_speed
    @old_transparent = @transparent
    @old_walk_anime = @walk_anime
    @old_step_anime = @step_anime
    @old_direction_fix = @direction_fix
    @old_opacity = @opacity
    @old_blend_type = @blend_type
  end
 
  #-----------------------------------------------------------------------------
  # New. Once we stop following another character, revert all original settings
  #-----------------------------------------------------------------------------
  def revert_original_settings
    @through = @old_through
    @move_speed = @old_move_speed
    @real_move_speed = @old_real_move_speed
    @transparent = @old_transparent
    @walk_anime = @old_walk_anime
    @step_anime = @old_step_anime
    @direction_fix = @old_direction_fix
    @opacity = @old_opacity
    @blend_type = @old_blend_type
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def get_follow_leader(event_id) #KK20
    # -1 is assumed to be the player
    if event_id == 0
      @triggered_by
    elsif event_id == -1
      $game_player
    elsif event_id == -2
      $game_player2
    else
      $game_map.events[event_id]
    end
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def chase_preceding_character
    unless moving?
      sx = distance_x_from(@preceding_character.x)
      sy = distance_y_from(@preceding_character.y)     
      if sx != 0 && sy != 0
        move_diagonal(sx > 0 ? 4 : 6, sy > 0 ? 8 : 2)
      elsif sx != 0
        move_straight(sx > 0 ? 4 : 6)
      elsif sy != 0
        move_straight(sy > 0 ? 8 : 2)
      end
    end
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def move_behind_leader
    x = @leader.x
    y = @leader.y
   
    case @leader.direction
    when 2
      y += 1
    when 4
      x += 1
    when 6
      x -= 1
    when 8
      y -= 1
    end
    x = [[1, x].max, $game_map.width-1].min
    y = [[1, y].max, $game_map.height-1].min
    moveto(x, y)
  end
 
  #-----------------------------------------------------------------------------
  # New. Begin following the specified event.
  #-----------------------------------------------------------------------------
  def follow(event_id=0)
    return if following?
    @leader = get_follow_leader(event_id)
    @preceding_character = @leader.last_follower
    @leader.add_event_follower(self)
    move_behind_leader
    store_original_settings
    @move_speed     = @leader.move_speed
    @transparent    = @leader.transparent
    @walk_anime     = @leader.walk_anime
    @step_anime     = @leader.step_anime
    @direction_fix  = @leader.direction_fix
    @opacity        = @leader.opacity
    @blend_type     = @leader.blend_type
    @through = true
  end
 
  #-----------------------------------------------------------------------------
  # New. Stop following a leader
  #-----------------------------------------------------------------------------
  def stop_follow
    @through = false
    @leader.remove_event_follower(self)
    @preceding_character = nil
    @leader = nil
    revert_original_settings
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def add_event_follower(char)
    @event_followers.add(char)
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def remove_event_follower(char)
    @event_followers.remove(char)
  end
 
  alias :th_event_followers_move_diagonal :move_diagonal
  def move_diagonal(horz, vert)
    @event_followers.move if diagonal_passable?(@x, @y, horz, vert)
    th_event_followers_move_diagonal(horz, vert)
  end
end

class Game_Event < Game_Character
  #-----------------------------------------------------------------------------
  # Ignore autonomous movement if following leader
  #-----------------------------------------------------------------------------
  alias :th_event_followers_update_self_movement :update_self_movement
  def update_self_movement
    return if following?
    th_event_followers_update_self_movement
  end
end

class Game_Player < Game_Character
 
  #-----------------------------------------------------------------------------
  # New. Since player happens to have different types of followers we have to
  # check
  #-----------------------------------------------------------------------------
  def last_follower
    return super if @event_followers.size > 0
    follower_size = $game_player.followers.visible_folloers.size
    return @followers[follower_size - 1] if follower_size > 0
    return self
  end
 
  #-----------------------------------------------------------------------------
  # Ignore player movement input if following leader
  #-----------------------------------------------------------------------------
  alias :th_event_followers_movable? :movable?
  def movable?
    return false if following?
    th_event_followers_movable?
  end
 
  alias :th_event_followers_dash? :dash?
  def dash?
    return false if following?
    th_event_followers_dash?
  end
end

#-------------------------------------------------------------------------------
# Similar to player followers, except instead of pulling data from the party
# members it simply holds references to existing events
#-------------------------------------------------------------------------------
class Game_EventFollowers < Game_Followers
  include Enumerable
 
  def initialize(leader)
    @data = []
  end
 
  def size
    @data.size
  end
 
  def index(char)
    @data.index(char)
  end
 
  def add(character)
    @data.push(character)
  end
 
  def remove(character)
    @data.delete(character)
  end 
end

class Game_Interpreter
  def follow(id=0)
    $game_map.events[@event_id].follow(id)
    true
  end
end

Multiplayer: ShowHide

module DataManager

  class << self
    alias :th_multiplayer_create_game_objects :create_game_objects
    alias :th_multiplayer_make_save_contents :make_save_contents
    alias :th_multiplayer_extract_save_contents :extract_save_contents
  end

  def self.create_game_objects
    th_multiplayer_create_game_objects
    $game_player2 = Game_Multiplayer.new

  end

  def self.make_save_contents
    contents = th_multiplayer_make_save_contents
    contents[:player2] = $game_player2
    contents
  end

  def self.extract_save_contents(contents)
    th_multiplayer_extract_save_contents(contents)
    $game_player2 = contents[:player2]
  end

  def self.setup_new_game
    create_game_objects
    $game_party.setup_starting_members
    $game_map.setup($data_system.start_map_id)
    $game_player.moveto($data_system.start_x, $data_system.start_y)
    $game_player.refresh
    $game_player2.moveto($data_system.start_x, $data_system.start_y)
    $game_player2.refresh
    Graphics.frame_count = 0
  end
end

class Game_Multiplayer < Game_Player

  DOWN = Input::Y # S in Keyboard
  LEFT = Input::X # A in Keyboard
  RIGHT = Input::Z # D in Keyboard
  UP = Input::R # W in Keyboard
  ENTER = Input::L # Q in Keyboard
  RUN = Input::A # Shift in Keyboard

  #--------------------------------------------------------------------------
  # * Get Corresponding Actor
  #--------------------------------------------------------------------------
  def actor
    $game_party.battle_members[1]
  end
 
  def move_by_input
    return unless movable?
    return if $game_map.interpreter.running?
    if Input.press?(DOWN)
      move_straight(2)
    elsif Input.press?(LEFT)
      move_straight(4)
    elsif Input.press?(RIGHT)
      move_straight(6)
    elsif Input.press?(UP)
      move_straight(8)
    end
  end

  def update_nonmoving(last_moving)
    return if $game_map.interpreter.running?
    if last_moving
      $game_party.on_player_walk
      return if check_touch_event
    end
    if movable? && Input.trigger?(ENTER)
      return if get_on_off_vehicle
      return if check_action_event
    end
    update_encounter if last_moving
  end
 
  #--------------------------------------------------------------------------
  # * Trigger Map Event
  #     triggers : Trigger array
  #     normal   : Is priority set to [Same as Characters] ?
  #--------------------------------------------------------------------------
  def start_map_event(x, y, triggers, normal)
    return if $game_map.interpreter.running?
    $game_map.events_xy(x, y).each do |event|
      if event.trigger_in?(triggers) && event.normal_priority? == normal
        event.start(true)
      end
    end
  end
end

class Game_Character
  #--------------------------------------------------------------------------
  # * Move toward Player
  #--------------------------------------------------------------------------
  def move_toward_player(player=$game_player)
    move_toward_character(player)
  end
end


class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # * Start Event
  #--------------------------------------------------------------------------
  def start(player2=false)
    return if empty?
    @triggered_by = player2 ? $game_player2 : $game_player
    @starting = true
    lock if trigger_in?([0,1,2])
  end
 
  #--------------------------------------------------------------------------
  # * Move Type : Approach
  #--------------------------------------------------------------------------
  def move_type_toward_player
    closest_player = near_the_player?
    if closest_player
      case rand(6)
      when 0..3;  move_toward_player(closest_player)
      when 4;     move_random
      when 5;     move_forward
      end
    else
      move_random
    end
  end
  #--------------------------------------------------------------------------
  # * Determine if Near Player
  #--------------------------------------------------------------------------
  def near_the_player?
    sx1 = distance_x_from($game_player.x).abs
    sy1 = distance_y_from($game_player.y).abs
    sx2 = distance_x_from($game_player2.x).abs
    sy2 = distance_y_from($game_player2.y).abs
    if sx1 + sy1 < 20 || sx2 + sy2 < 20
      diff = (sx1 + sy1) - (sx2 + sy2)
      return $game_player if diff < 0
      return $game_player2 if diff > 0
      return [$game_player, $game_player2][rand(2)]
    end
    return false
  end
end


class Spriteset_Map
  alias :th_multiplayer_create_characters :create_characters

  def create_characters
    th_multiplayer_create_characters
    @character_sprites.push(Sprite_Character.new(@viewport1, $game_player2))
  end
end


class Scene_Map < Scene_Base
  alias :th_multiplayer_map_update :update
  def update
    th_multiplayer_map_update
    $game_player2.update if $game_player2
  end
 
  #--------------------------------------------------------------------------
  # * Player Transfer Processing
  #--------------------------------------------------------------------------
  def perform_transfer
    pre_transfer
    $game_player.perform_transfer
    $game_player2.perform_transfer
    post_transfer
  end
end

class Game_Interpreter
  #--------------------------------------------------------------------------
  # * Transfer Player
  #--------------------------------------------------------------------------
  def command_201
    return if $game_party.in_battle
    Fiber.yield while $game_player.transfer? || $game_message.visible
    if @params[0] == 0                      # Direct designation
      map_id = @params[1]
      x = @params[2]
      y = @params[3]
    else                                    # Designation with variables
      map_id = $game_variables[@params[1]]
      x = $game_variables[@params[2]]
      y = $game_variables[@params[3]]
    end
    $game_player.reserve_transfer(map_id, x, y, @params[4])
    $game_player2.reserve_transfer(map_id, x, y, @params[4])
    $game_temp.fade_type = @params[5]
    Fiber.yield while $game_player.transfer?
  end
end

Noticed a bug in the script I posted yesterday; no idea how that passed by me before.
Transfer event will now put the second player in the specified teleport spot.
An event with an Autonomous Movement type Approach will generally move towards the closest player.

But you will soon see that event touch trigger won't work and will phase through the second player. There's a lot of other issues like this when you add another player entity that the game has no idea of. That's the reason why that script was only a Proof of Concept--there's just so much more that needs to be done.

This is pretty much the extent of all I can help you with. If you're still serious with this, I advise getting a full-time scripter who is willing to rewrite the default scripts for you.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Paladin

The transfer now works flawlessly! KK20, you have gone above and beyond my expectations help-wise. I understand that rpg-maker wasn't designed to have 2 player ability to start with, but with Tsukihime's script and all your mods here, I think this is fantastic.

Thank you for spending your time and effort in getting this sorted out. I'm going to mark this topic as resolved as this final solution is at present satisfactory for me. As you said, this would need much more work in order to make it fully ingrained into the system.

If this little project gets to a reasonable size, I will do just as you suggest.  :)