(XP) Custom (Pixel) Movement with Heretics Modular Passable Script Compatibility

Started by schmoggi, June 14, 2016, 08:50:07 am

Previous topic - Next topic

schmoggi

Hey there,

As you know, we are all restricted to the 32px Tile Movement. The Problem ist, that Movement can look very blocky und feel a bit "unnatural", specially when you developing with a Zelda like Action Battle System in Mind. Though, there are plenty Scripts out there which allows Pixel Movement. Pixel Movement is fine by it self, but personally i don't like it in RPG Maker Games and in many cases you have to use a "Collision Map". Furthermore, i think it is an unnecessary Step. A "half-step" Movement like 16px instead of 32px is already fine in my oppinion and it could be made more precise when reduced to 8px. Anyway .. you run into Problems when you're a Fan of Heretics Scripts.

I guess many People around here know the famous Heretic's Collection  of Art and 100% Compatible Scripts, specially the  Heart of it, Modular Passable Script. If you don't know it, well then get to know it .. it is awesome!

Modular Passable - > http://forum.chaos-project.com/index.php/topic,14986.msg191610.html#msg191610
Collection - > http://forum.chaos-project.com/index.php/topic,13682.0.html

It allows many advanced things like Collision Optimizer, NPC on Event Tiles and the new added Moving Platforms. Things, that should have been in the original Release of XP Maker. If you ask me, i don't want to abandon that Scripts anymore, they offer so much. Sadly, like already said, if you're a Fan of Custom Movement, you have a Problem. In the Modular Passable Script it is stated:

Quote# -----  Compatability  -----
#
# This script will NOT be compatible with any other script that replaces
# either Game_Map Passable or Game_Character Passable.  If a script replaces
# Game_Event Refresh, that can be fixed by putting the entire Game_Event class
# below the conflicting script, but leave Game_Map and Game_Character classes
# in this script above other user created scripts, except the SDK.
#
# If the nature of other scripts is extremely different, expect problems.  So
# other scripts like Pixel Movement Scripts most likely will be too foreign
# for this script to offer any compatability.  Bananas and Screwdrivers.
#
# This is intended for making the Default Movements more Modular only.


I'm here to ask a capable Scripter (or more than one) if someone could take a glance on it and develop a fine Script, based on Modular Passable, which:

- gives you the Possibilty to alter the standard 32px Movement to 16px/8px/4px.
For my personal Taste, 16px would be already enough but i can imagine there are many out there with the desire for finer Movement. Also this would provide more customization

- relies on the standard Collision & Passability Detection
no nasty "Collision Maps"

If it helps, i've already got a fine Movement Scripts through which you can customize the Movement down to 16px/8px and so on. Since i plan to use Heretics Scripts, i can't use it anymore. If it helps, i can post it here so we do not need to make a fresh start and "only" need to make it compatible.

greetz

schmoggi

Bump...

I'm still on this and would be happy for any help. You can find the Script mentioned in the Opening Post i'm using under these Lines. I translated the few german Comments there were for you so you can understand it  better.

Spoiler: ShowHide
# Genocide_Movement 1.1 - 8 pixel precise movement for RPG Maker XP
#
# Author: derula
#
# This script is taken from the German RPGXP project "Genocide" and adjusted
# for standalone usage. Permission is granted by the author to freely use and
# edit it as long as the game "Genocide" and the code's original author remain
# credited in the code. Other means of crediting are voluntary, but appreciated.

module Genocide_Movement
# Configuration:
# Default 1/4 of a Tile an Event is moving
# Standard: 4 (32px), Minimum: 1(8px), Maximum: 4(32px)
EVENT_DISTANCE = 4
# Default 1/4 of a Tile the Player is moving
# Standard: 4 (32px), Minimum: 1(8px), Maximum: 4(32px)
PLAYER_DISTANCE = 2
# Turn Diagonalmove on or off
# Standard: true, Deactivate: false
PLAYER_8_DIR = true
# Key for Strafe:
# Standard: Input::SHIFT, Deactivate: nil
STRAFE_KEY = Input::SHIFT
# Cost for Diagonalmove
# Standard: Math.sqrt(2), Deactivate: nil
DIAGONAL_COST = Math.sqrt(2)

class ::Numeric
# Map Position Coordinates calculate from Flowpoint Coordinates
def to_field
if (i = to_i) == self
i
else
(self-0.125).round
end
end
end

class ::Game_Character
#--------------------------------------------------------------------------
# * Calculate new position
#     x : x-coordinate
#     y : y-coordinate
#     d : direction (0,1,2,3,4,6,7,8,9)
#         * 0 = no change
#     s : steps (1..4)
#--------------------------------------------------------------------------
def get_new_coordinates(x, y, d, s = EVENT_DISTANCE)
# Get move distance
s *= 0.25
# Get new coordinates
if d != 0
dx = (d % 3 == 0 ? 1 : d % 3 == 1 ? -1 : 0)
dy = (d <= 3 ? 1 : d >= 7 ? -1 : 0)
new_x, new_y = x + dx * s, y + dy * s
else
dx = dy = 0
new_x, new_y = x, y
end
[new_x, new_y, dx, dy]
end
#--------------------------------------------------------------------------
# * Determine if Passable
#     x : x-coordinate
#     y : y-coordinate
#     d : direction (0,1,2,3,4,6,7,8,9)
#         * 0 = Determines if all directions are impassable (for jumping)
#     s : steps (1..4)
#     n : new coordinates as calculated by get_new_coordinates
#         if not specified, they are calculated here
#--------------------------------------------------------------------------
def passable?(x, y, d, s = EVENT_DISTANCE, n = get_new_coordinates(x, y, d, s))
# Get new coordinates
new_x, new_y, dx, dy = *n
# If coordinates are outside of map: impassable
return false unless $game_map.valid?(new_x, new_y)
# If through is ON: passable
return true if @through
# Get move distance
s *= 0.25
# Calculate Standfield
fx, fy = x.to_field, y.to_field
# Check, if moving to new field
cx = ((x - fx).abs < s and 0 < dx*(new_x - x))
cy = ((y - fy).abs < s and 0 < dy*(new_y - y))
if cx and cy
return false unless $game_map.passable?(fx, fy, d) and $game_map.passable?(fx + dx, fy + dy, 10 - d)
elsif cx
# Normal Check
return false unless $game_map.passable?(fx, fy, 5 + dx) and $game_map.passable?(fx + dx, fy, 5 - dx)
# If Player is between 2 Tiles...
if y != fy
cy = fy + (y <=> fy)
# ... Check neighbor Tile...
return false unless $game_map.passable?(fx, cy, 5 + dx) and $game_map.passable?(fx + dx, cy, 5 - dx)
# ... Make sure, fy ist the Tile above, cy the Tile below...
fy, cy = cy, fy if cy < fy
y = (y - fy) % 1
# ... check above Tile to the south...
return false if y <= 0.5 and not ($game_map.passable?(fx + dx, cy, 2) and $game_map.passable?(fx + dx, fy, 8))
# ... and check below Tile to the north
return false if y >= 0.5 and not ($game_map.passable?(fx + dx, fy, 8) and $game_map.passable?(fx + dx, cy, 2))
end
elsif cy
# Normal Check
return false unless $game_map.passable?(fx, fy, 5 - 3 * dy) and $game_map.passable?(fx, fy + dy, 5 + 3 * dy)
# If Player is between 2 Tiles...
if x != fx
cx = fx + (x <=> fx)
# ... Check neighbor Tile...
return false unless $game_map.passable?(cx, fy, 5 - 3 * dy) and $game_map.passable?(cx, fy + dy, 5 + 3 * dy)
# ... Make sure, fx ist the Tile left, cx the Tile right...
fx, cx = cx, fx if cx < fx
x = (x - fx) % 1
# ... check right Tile to the left...
return false if x <= 0.5 and not ($game_map.passable?(cx, fy + dy, 4) and $game_map.passable?(fx, fy + dy, 6))
# ... and check left Tile to the right...
return false if x >= 0.5 and not ($game_map.passable?(fy, fy + dy, 6) and $game_map.passable?(cx, fy + dy, 4))
end
end
# Loop all events
for event in $game_map.events.values
next if event == self or event.through
# If through is OFF and (self is player => partner graphic as character)
# and event coordinates are consistent with move destination
if #not event.through and event.character_name != "" and
(self != $game_player or event.character_name != "") and
(event.x - new_x).abs < 1 and (event.y - new_y).abs < 1
# impassable
return false
end
end
# If self is not player, player through is OFF coordinates are consistent
# with move destination and your own graphic is the character
if self != $game_player and not $game_player.through and
@character_name != "" and ($game_player.x - new_x).abs < 1 and
($game_player.y - new_y).abs < 1
# impassable
return false
end
# passable
return true
end
#--------------------------------------------------------------------------
# * Move
#     dir          : direction (0,1,2,3,4,6,7,8,9)
#     steps        : number of steps to move (4 = 1 field)
#     turn_enabled : a flag permits direction change on that spot
#--------------------------------------------------------------------------
def move(dir, steps = EVENT_DISTANCE, turn_enabled = true)
return if dir == 0
turned = false
# Turn to specified direction
if turn_enabled
turn(dir)
turned = true
end
# Calculate new coordinates
new_coords = get_new_coordinates(@x, @y, dir, steps)
# If passable and route free
if passable?(@x, @y, dir, steps, new_coords)
# Turn to specified direction
turn(dir) unless turned
# Update coordinates
@x, @y = new_coords
# Increase steps
increase_steps
# Slide if moving diagonally
elsif dir % 2 == 1 and
((m = dir % 6) == 1 and passable?(@x, @y, 4, steps, n = get_new_coordinates(@x, @y, 4, steps)) or
(m == 3 and passable?(@x, @y, 6, steps, n = get_new_coordinates(@x, @y, 6, steps))) or
(dir < 5 and passable?(@x, @y, 2, steps, n = get_new_coordinates(@x, @y, 2, steps))) or
(dir > 5 and passable?(@x, @y, 8, steps, n = get_new_coordinates(@x, @y, 8, steps))))
# Update coordinates
@x, @y = n#ew_coords
# Increase steps
increase_steps
# If impassable
else
# Determine if touch event is triggered
check_event_trigger_touch(new_coords[0], new_coords[1])
end
end
#--------------------------------------------------------------------------
# * Move Down
#     turn_enabled : a flag permits direction change on that spot
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_down(turn_enabled = true, steps = EVENT_DISTANCE)
move(2, steps, turn_enabled)
end
#--------------------------------------------------------------------------
# * Move Left
#     turn_enabled : a flag permits direction change on that spot
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_left(turn_enabled = true, steps = EVENT_DISTANCE)
move(4, steps, turn_enabled)
end
#--------------------------------------------------------------------------
# * Move Right
#     turn_enabled : a flag permits direction change on that spot
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_right(turn_enabled = true, steps = EVENT_DISTANCE)
move(6, steps, turn_enabled)
end
#--------------------------------------------------------------------------
# * Move up
#     turn_enabled : a flag permits direction change on that spot
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_up(turn_enabled = true, steps = EVENT_DISTANCE)
move(8, steps, turn_enabled)
end
#--------------------------------------------------------------------------
# * Move Lower Left
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_lower_left(steps = EVENT_DISTANCE)
move(1, steps)
end
#--------------------------------------------------------------------------
# * Move Lower Right
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_lower_right(steps = EVENT_DISTANCE)
move(3, steps)
end
#--------------------------------------------------------------------------
# * Move Upper Left
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_upper_left(steps = EVENT_DISTANCE)
move(7, steps)
end
#--------------------------------------------------------------------------
# * Move Upper Right
#     steps        : number of steps to move (4 = 1 field)
#--------------------------------------------------------------------------
def move_upper_right(steps = EVENT_DISTANCE)
move(9, steps)
end
#--------------------------------------------------------------------------
# * Turn
#     d     : direction (1,2,3,4,6,7,8,9)
#--------------------------------------------------------------------------
def turn(d)
# If no direction fix
unless @direction_fix
if d % 2 == 1
# If diagonal direction specified, replace it with good straight
# direction if the character is facing an opposite direction
if ((m = d % 6) == 1 and @direction == 6) or
(m == 3 and @direction == 4) or
(d < 5 and @direction == 8) or
(d > 5 and @direction == 2)
@direction = 10 - @direction
end
else
@direction = d
end
@stop_count = 0
end
end
#--------------------------------------------------------------------------
# * Update frame (move)
#--------------------------------------------------------------------------
def update_move
# Convert map coordinates from map move speed into move distance
distance = 2 ** @move_speed
# Apply cost for diagonal movement
if DIAGONAL_COST and @x * 128 != @real_x and @y * 128 != @real_y
distance = (distance / DIAGONAL_COST).round
end
# If logical coordinates are further down than real coordinates
if @y * 128 > @real_y
# Move down
@real_y = [@real_y + distance, @y * 128].min
end
# If logical coordinates are more to the left than real coordinates
if @x * 128 < @real_x
# Move left
@real_x = [@real_x - distance, @x * 128].max
end
# If logical coordinates are more to the right than real coordinates
if @x * 128 > @real_x
# Move right
@real_x = [@real_x + distance, @x * 128].min
end
# If logical coordinates are further up than real coordinates
if @y * 128 < @real_y
# Move up
@real_y = [@real_y - distance, @y * 128].max
end
# If move animation is ON
if @walk_anime
# Increase animation count by 1.5
@anime_count += 1.5
# If move animation is OFF, and stop animation is ON
elsif @step_anime
# Increase animation count by 1
@anime_count += 1
end
end
end

class ::Game_Event
#--------------------------------------------------------------------------
# * Touch Event Starting Determinant
#--------------------------------------------------------------------------
def check_event_trigger_touch(x, y)
# If event is running
if $game_system.map_interpreter.running?
return
end
# If starting determinant other than jumping is front event and
# trigger is [touch from event] and consistent with player coordinates
if @trigger == 2 and not over_trigger? and not jumping? and
($game_player.x - @x).abs <= 0.5 and ($game_player.y - @y).abs <= 0.5
start
end
end
#--------------------------------------------------------------------------
# * Automatic Event Starting Determinant
#--------------------------------------------------------------------------
def check_event_trigger_auto
# If starting determinant other than jumping is same position event and
# trigger is [touch from event] and consistent with player coordinates
if @trigger == 2 and over_trigger? and not jumping? and
($game_player.x - @x).abs <= 0.5 and ($game_player.y - @y).abs <= 0.5
start
# If trigger is [auto run]
elsif @trigger == 3
start
end
end
end

# Shots/Hits and 8Dir- and Pixelmovement
class ::Game_Player
def move(dir, steps = PLAYER_DISTANCE, turn_enabled = true)
super
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
def update
# Remember whether or not moving in local variables
last_moving = moving?
# Fix Direction
if STRAFE_KEY
if Input.press?(STRAFE_KEY)
if @pre_fix == nil
@pre_fix = @direction_fix
@direction_fix = true
end
elsif @pre_fix != nil
@direction_fix = @pre_fix
@pre_fix = nil
end
end
# If moving, event running, move route forcing, and message window
# display are all not occurring
if not (moving? or $game_system.map_interpreter.running? or
@move_route_forcing or $game_temp.message_window_showing)
# Move player in the direction the directional button is being pressed
move(PLAYER_8_DIR ? Input.dir8 : Input.dir4)
end
# Remember coordinates in local variables
last_real_x = @real_x
last_real_y = @real_y
super
# If character moves down and is positioned lower than the center
# of the screen
if @real_y > last_real_y and @real_y - $game_map.display_y > CENTER_Y
# Scroll map down
$game_map.scroll_down(@real_y - last_real_y)
end
# If character moves left and is positioned more let on-screen than
# center
if @real_x < last_real_x and @real_x - $game_map.display_x < CENTER_X
# Scroll map left
$game_map.scroll_left(last_real_x - @real_x)
end
# If character moves right and is positioned more right on-screen than
# center
if @real_x > last_real_x and @real_x - $game_map.display_x > CENTER_X
# Scroll map right
$game_map.scroll_right(@real_x - last_real_x)
end
# If character moves up and is positioned higher than the center
# of the screen
if @real_y < last_real_y and @real_y - $game_map.display_y < CENTER_Y
# Scroll map up
$game_map.scroll_up(last_real_y - @real_y)
end
# If not moving
unless moving?
# If player was moving last time
if last_moving
# Event determinant is via touch of same position event
result = check_event_trigger_here([1,2])
# If event which started does not exist
if result == false
# Disregard if debug mode is ON and ctrl key was pressed
unless $DEBUG and Input.press?(Input::CTRL)
# Encounter countdown
if @encounter_count > 0
@encounter_count -= 1
end
end
end
end
# If C button was pressed
if Input.trigger?(Input::C)
# Same position and front event determinant
check_event_trigger_here([0])
check_event_trigger_there([0,1,2])
end
end
end
#--------------------------------------------------------------------------
# * Passable Determinants
#     x : x-coordinate
#     y : y-coordinate
#     d : direction (0,2,4,6,8)
#         * 0 = Determines if all directions are impassable (for jumping)
#     s : steps (1..4)
#--------------------------------------------------------------------------
def passable?(x, y, d, s = 4, n = get_new_coordinates(x, y, d, s))
# If coordinates are outside of map: impassable
return false unless $game_map.valid?(n[0], n[1])
# If debug mode is ON and ctrl key was pressed: passable
return true if $DEBUG and Input.press?(Input::CTRL)
super
end
#--------------------------------------------------------------------------
# * Same Position Starting Determinant
#--------------------------------------------------------------------------
def check_event_trigger_here(triggers)
# If no event is running
unless $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
# If triggers are consistent, starting determinant is same position event
# (other than jumping) and event coordinates are consistent
if event.over_trigger? and triggers.include?(event.trigger) and
(not event.jumping?) and (event.x - @x).abs <= 0.5 and (event.y - @y).abs <= 0.5
event.start
return true
end
end
end
return false
end
#--------------------------------------------------------------------------
# * Front Envent Starting Determinant
#--------------------------------------------------------------------------
def check_event_trigger_there(triggers)
# If no event is running
unless $game_system.map_interpreter.running?
# Calculate front event coordinates
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
# All event loops
for event in $game_map.events.values
# If triggers are consistent, starting determinant is front event
# (other than jumping) and event coordinates are consistent
if not event.over_trigger? and triggers.include?(event.trigger) and
not event.jumping? and (event.x - new_x).abs <= 0.5 and (event.y - new_y).abs <= 0.5
event.start
return true
end
end
# If front tile is a counter
if $game_map.counter?(new_x, new_y)
# Calculate 1 tile inside coordinates
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
# All event loops
for event in $game_map.events.values
# If triggers are consistent, starting determinant is front event
# (other than jumping) and event coordinates are consistent
if not event.over_trigger? and triggers.include?(event.trigger) and
not event.jumping? and (event.x - new_x).abs <= 0.5 and (event.y - new_y).abs <= 0.5
event.start
return true
end
end
end
end
return false
end
#--------------------------------------------------------------------------
# * Touch Event Starting Determinant
#--------------------------------------------------------------------------
def check_event_trigger_touch(x, y)
# If no event is running
unless $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
# If triggers are consistent, starting determinant is front event
# (other than jumping) and event coordinates are consistent
if not event.over_trigger? and not event.jumping? and
[1,2].include?(event.trigger) and (event.x - x).abs <= 0.5 and (event.y - y).abs <= 0.5
event.start
return true
end
end
end
return false
end
end

class ::Game_Map
alias :old_passable? :passable?
#--------------------------------------------------------------------------
# * Determine if Passable
#     x          : x-coordinate
#     y          : y-coordinate
#     d          : direction (0,...,10)
#                  *  0,10 = determine if all directions are impassable
#     self_event : Self (If event is determined passable)
#--------------------------------------------------------------------------
def passable?(x, y, d, self_event = nil)
# Handle diagonal passability
if d % 2 == 1
# Calculate temporary movement direction indicators
dx, dy = d % 3 == 1 ? -1 : 1, 6 <=> d
# Get new position
new_x, new_y = x + dx, y + dy
# Get movement directions
dx, dy = 5 + dx, 5 + 3 * dy
# Calculate passability
passable?(x, y, dx, self_event) and passable?(x, new_y, dx, self_event) and
passable?(x, y, dy, self_event) and passable?(new_x, y, dy, self_event)
else
old_passable?(x, y, d, self_event)
end
end
end
end


Thanks a Lot

greetz