About RPG maker's internal bugs

Started by Wecoc, August 15, 2014, 08:29:43 pm

Previous topic - Next topic

Heretic86

Rounding just the Player's calculated move speed would be a solution.  Or perhaps .floor or .ceil as well?  I havent tried it, but have played with adjusting the values after exponential calculations in Downhill Ice.  Downhill Ice just adds ice_speed to real_y so it skips all that exponent stuff. I do use floating move speeds all over the place for random movement, just not for the player.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

Since no one has posted it, might as well just say that changing a sprite's x/y value by a float won't work. I experienced this occurrence with smooth scrolling scripts while working on a tilemap implementation. It seems to set it as the floor value.

As a result, slowly increasing (+0.1) will do absolutely nothing whereas slowly decreasing (-0.1) will consistently keep moving the sprite left/up 1 pixel. Not sure if this is a workaround, but I think you can add two variables that actually take in the x/y changes first before physically changing the Sprite's built-in x/y attributes, kind of like what @real_ does but with floats instead.





   


   
   


   
   

Other Projects
RPG Maker XP AceUpgrade RMXP to RMVXA performance!
XPA TilemapTilemap rewrite with many features, including custom resolution!



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

Join the CP Discord Server!

Wecoc

Hi guys, long time no see.
I solved another bug with event interaction. By default the event interaction only depends on the coordinates, and this has some problems.

Spoiler: ShowHide


With the script if the player can't go to the event position (without considering the event itself on the passability) there is not interaction.
Also using $game_map.events[ID].passable_touch = true/false you can enable or disable the fix on the event manually.

class Game_Character
  attr_accessor :passable_touch
  alias passable_touch_ini initialize unless $@
  def initialize
    passable_touch_ini
    @passable_touch = false
  end
 
  def no_event_passable?(x, y, d)
    new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
    new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
    return true  if @passable_touch == true
    return false unless $game_map.valid?(new_x, new_y)
    return true  if @through
    return false unless $game_map.passable?(x, y, d, self)
    return false unless $game_map.passable?(new_x, new_y, 10 - d)
    return true
  end
end

class Game_Event < Game_Character
  alias collision_passable_fix check_event_trigger_touch unless $@
  def check_event_trigger_touch(new_x, new_y)
    if @x != new_x
      d = (@x < new_x ? 6 : 4)
    elsif @y != new_y
      d = (@y < new_y ? 2 : 8)
    end
    if (@x == new_x) and (@y == new_y) or no_event_passable?(@x, @y, d)
      collision_passable_fix(new_x, new_y)
    end
  end
end

class Game_Player < Game_Character
  def check_event_trigger_there(triggers)
    result = false
    if $game_system.map_interpreter.running?
      return result
    end
    new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
    new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
    for event in $game_map.events.values
      if event.x == new_x and event.y == new_y and
        triggers.include?(event.trigger)
        if not event.jumping? and not event.over_trigger?
          passable = no_event_passable?(@x, @y, @direction)
          if event.passable_touch == true or passable
            event.start
            result = true
          end
        end
      end
    end
    if result == false
      if $game_map.counter?(new_x, new_y)
        new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
        new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
        for event in $game_map.events.values
          if event.x == new_x and event.y == new_y and
            triggers.include?(event.trigger)
            if not event.jumping? and not event.over_trigger?
              passable = no_event_passable?(@x, @y, @direction)
              if event.passable_touch == true or passable
                event.start
                result = true
              end
            end
          end
        end
      end
    end
    return result
  end
 
  def check_event_trigger_touch(x, y)
    result = false
    if $game_system.map_interpreter.running?
      return result
    end
    for event in $game_map.events.values
      if event.x == x and event.y == y and [1,2].include?(event.trigger)
        if not event.jumping? and not event.over_trigger?
          passable = no_event_passable?(@x, @y, @direction)
          if event.passable_touch == true or passable
            event.start
            result = true
          end
        end
      end
    end
    return result
  end
end


-----------------

I found a bug on the LittleDrago's fix called [XP] Parallel process in the first map.
The battle call from event restarts over and over...

KK20: No idea how to do that exactly.

KK20

October 04, 2015, 04:12:29 pm #23 Last Edit: October 06, 2016, 02:45:42 pm by KK20
I've done the float sprite movement a while ago. Included is a little test of it. Paste above Main.
Code: ruby

=begin
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Allows sprites to have their x and y values changed via float values rather than
automatically converting to int.

Normally, when you change the x/y of a sprite by a Float, it will floor the
value before setting the sprite.
Examples:
          sprite.x = 0.5 = floor(0.5) = 0
          sprite.y = -0.5 = floor(-0.5) = -1
          sprite.x = -1E10 = floor(-1E10) = -1

Also, adding a series of floats will do nothing. Look at the following:

          while true
            sprite.x += 0.99999
          end

This will not move the sprite at all.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
=end

 
# Takes in a number and an amount. This will add or reduce the number by that
# amount, whatever gets it closer to 0. If it would go beyond 0, it will stop at
# 0 (and if it is already 0, it will do nothing).
# You can make the amount negative to make the number move AWAY from 0 as well.
def to0(num, amt)
  # First check if the number is already 0
  if num.integer?
    return num if num == 0
  else
    return num if num =~ 0
  end
  # Now check whether we need to ADD or SUBTRACT to reach (or avoid) 0
  if num > 0
    num = [num - amt, 0].max
  else
    num = [num + amt, 0].min
  end
end

class Float
  # A close float comparison. Because of rounding errors, this comparison will
  # help compensate for that. Since 0 and 0.0001 are very close to each other,
  # it's best to use this custom comparison.
  def =~(num)
    return (self + 0.0001 >= num && self - 0.0001 <= num)
  end
end


# Contains modified set methods for X/Y that allow the use of Floats

class IntFloat
  attr_reader :value
 
  def initialize(n)
    @value = n
  end
 
  def to_i; @value.to_i; end
  def ceil; @value.ceil; end
  def floor; @value.floor; end
  def to_f; @value; end
  def inspect; @value.to_i; end
 
 
  def +(other)
    if other.is_a?(IntFloat)
      r = @value + other.value
    elsif other.is_a?(Integer)
      r = @value.floor + other
    elsif other.is_a?(Float)
      r = @value + other
    else
      raise "Cannot perform addition on a non-numeric object!"
    end
    r
  end
 
  def -(other)
    if other.is_a?(IntFloat)
      r = @value - other.value
    elsif other.is_a?(Integer)
      r = @value.floor - other
    elsif other.is_a?(Float)
      r = @value - other
    else
      raise "Cannot perform subtraction on a non-numeric object!"
    end
    r
  end
 
  def *(other)
    if other.is_a?(IntFloat)
      r = @value * other.value
    elsif other.is_a?(Integer)
      r = @value.floor * other
    elsif other.is_a?(Float)
      r = @value * other
    else
      raise "Cannot perform multiplication on a non-numeric object!"
    end
    r
  end
 
  def /(other)
    if other.is_a?(IntFloat)
      r = @value / other.value
    elsif other.is_a?(Integer)
      r = @value.floor / other
    elsif other.is_a?(Float)
      r = @value / other
    else
      raise "Cannot perform division on a non-numeric object!"
    end
    r
  end
 
  def set(other)
    if other.is_a?(IntFloat)
      @value = other.value
    elsif other.is_a?(Integer) || other.is_a?(Float)
      @value = other
    else
      raise "Cannot assign to a non-numeric object!"
    end
    roundOff
    @value
  end
 
 
  def roundOff
    if @value.to_f.abs =~ 1
      @value = @value.round
    end
   
  end
 
 
end



class Sprite
 
  alias init_offsets initialize
  def initialize(vp = nil)
    @intfloat_x = IntFloat.new(0.0)
    @intfloat_y = IntFloat.new(0.0)
    init_offsets(vp)
  end
 
  def x; @intfloat_x; end
  def y; @intfloat_y; end
 
  alias set_x x=
  def x=(amt)
    @intfloat_x.set(amt)
    set_x(@intfloat_x.to_i)
  end
 
  alias set_y y=
  def y=(amt)
    @intfloat_y.set(amt)
    set_y(@intfloat_y.to_i)
  end
 
  def xInt(x)
    set_x(x)
  end
 
  def yInt(y)
    set_y(y)
  end
 
  def xInt=(x)
    xInt(x)
  end
 
  def yInt=(y)
    yInt(y)
  end
 
  def actualX
    return @intfloat_x.to_f
  end
 
  def actualY
    return @intfloat_y.to_f
  end
 
end



=begin
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Just a test program with the Float sprite movement. Move a red square around the
screen. Has a standard acceleration setup that is added to the sprite's X/Y
coordinates. If using Console debugger, hold SHIFT key to see values.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
=end
begin
  #Graphics.resize_screen(1024, 1024)
  sprite = Sprite.new
  sprite.bitmap = Bitmap.new(32,32)
  sprite.bitmap.fill_rect(0,0,32,32, Color.new(255, 0, 0))
  accel_x = 0.0
  accel_y = 0.0
 
  while true
    Graphics.update
    Input.update
   
    input = Input.dir8
   
    if Input.press?(Input::SHIFT)
      #puts ["Sprite Location: ", sprite.x, sprite.y]
      #puts ["Sprite Acutal: ",sprite.actualX, sprite.actualY]
      #puts ["accel: ", accel_x, accel_y]
      #puts "-------"
    end
    if Input.trigger?(Input::C)
      sprite.y += 10
    end
    if Input.trigger?(Input::B)
      sprite.x += 0.3
    elsif Input.trigger?(Input::A)
      sprite.x -= 0.3
    end
   
    # Get input and set sprite acceleration
    if input != 0
      if [1,2,3].include?(input)
        accel_y += (accel_y >= 0 ? 0.2 : 0.5)
      elsif [7,8,9].include?(input)
        accel_y -= (accel_y <= 0 ? 0.2 : 0.5)
      end
     
      if [1,4,7].include?(input)
        accel_x -= (accel_x <= 0 ? 0.2 : 0.5)
      elsif [3,6,9].include?(input)
        accel_x += (accel_x >= 0 ? 0.2 : 0.5)
      end
    end
   
    # If not inputting in a desired direction, slow down in that direction
    if [0,4,6].include?(input)
      accel_y = to0(accel_y, 0.1).to_f
    end
    if [0,2,8].include?(input)
      accel_x = to0(accel_x, 0.1).to_f
    end
   
    # Acceleration speed cap
    if accel_y.abs > 10
      accel_y = (accel_y > 0 ? 10 : -10)
    end
    if accel_x.abs > 10
      accel_x = (accel_x > 0 ? 10 : -10)
    end
   
    # Move the sprite
    sprite.x += accel_x
    sprite.y += accel_y
   
   
  end
end






   


   
   


   
   

Other Projects
RPG Maker XP AceUpgrade RMXP to RMVXA performance!
XPA TilemapTilemap rewrite with many features, including custom resolution!



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

Join the CP Discord Server!

LiTTleDRAgo

Sorry necropost :P

Quote from: KK20 on October 04, 2015, 04:12:29 pm
I've done the float sprite movement a while ago. Included is a little test of it. Paste above Main.
Spoiler: ShowHide
Code: ruby

=begin
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Allows sprites to have their x and y values changed via float values rather than
automatically converting to int.

Normally, when you change the x/y of a sprite by a Float, it will floor the
value before setting the sprite.
Examples:
          sprite.x = 0.5 = floor(0.5) = 0
          sprite.y = -0.5 = floor(-0.5) = -1
          sprite.x = -1E10 = floor(-1E10) = -1

Also, adding a series of floats will do nothing. Look at the following:

          while true
            sprite.x += 0.99999
          end

This will not move the sprite at all.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
=end

 
# Takes in a number and an amount. This will add or reduce the number by that
# amount, whatever gets it closer to 0. If it would go beyond 0, it will stop at
# 0 (and if it is already 0, it will do nothing).
# You can make the amount negative to make the number move AWAY from 0 as well.
def to0(num, amt)
  # First check if the number is already 0
  if num.integer?
    return num if num == 0
  else
    return num if num =~ 0
  end
  # Now check whether we need to ADD or SUBTRACT to reach (or avoid) 0
  if num > 0
    num = [num - amt, 0].max
  else
    num = [num + amt, 0].min
  end
end

class Float
  # A close float comparison. Because of rounding errors, this comparison will
  # help compensate for that. Since 0 and 0.0001 are very close to each other,
  # it's best to use this custom comparison.
  def =~(num)
    return (self + 0.0001 >= num && self - 0.0001 <= num)
  end
end


# Contains modified set methods for X/Y that allow the use of Floats

class IntFloat
  attr_reader :value
 
  def initialize(n)
    @value = n
  end
 
  def to_i; @value.to_i; end
  def ceil; @value.ceil; end
  def floor; @value.floor; end
  def to_f; @value; end
  def inspect; @value.to_i; end
 
 
  def +(other)
    if other.is_a?(IntFloat)
      r = @value + other.value
    elsif other.is_a?(Integer)
      r = @value.floor + other
    elsif other.is_a?(Float)
      r = @value + other
    else
      raise "Cannot perform addition on a non-numeric object!"
    end
    r
  end
 
  def -(other)
    if other.is_a?(IntFloat)
      r = @value - other.value
    elsif other.is_a?(Integer)
      r = @value.floor - other
    elsif other.is_a?(Float)
      r = @value - other
    else
      raise "Cannot perform subtraction on a non-numeric object!"
    end
    r
  end
 
  def *(other)
    if other.is_a?(IntFloat)
      r = @value * other.value
    elsif other.is_a?(Integer)
      r = @value.floor * other
    elsif other.is_a?(Float)
      r = @value * other
    else
      raise "Cannot perform multiplication on a non-numeric object!"
    end
    r
  end
 
  def /(other)
    if other.is_a?(IntFloat)
      r = @value / other.value
    elsif other.is_a?(Integer)
      r = @value.floor / other
    elsif other.is_a?(Float)
      r = @value / other
    else
      raise "Cannot perform division on a non-numeric object!"
    end
    r
  end
 
  def set(other)
    if other.is_a?(IntFloat)
      @value = other.value
    elsif other.is_a?(Integer) || other.is_a?(Float)
      @value = other
    else
      raise "Cannot assign to a non-numeric object!"
    end
    roundOff
    @value
  end
 
 
  def roundOff
    if @value.to_f.abs =~ 1
      @value = @value.round
    end
   
  end
 
 
end



class Sprite
 
  alias init_offsets initialize
  def initialize(vp = nil)
    @intfloat_x = IntFloat.new(0.0)
    @intfloat_y = IntFloat.new(0.0)
    init_offsets(vp)
  end
 
  def x; @intfloat_x; end
  def y; @intfloat_y; end
 
  alias set_x x=
  def x=(amt)
    @intfloat_x.set(amt)
    set_x(@intfloat_x.to_i)
  end
 
  alias set_y y=
  def y=(amt)
    @intfloat_y.set(amt)
    set_y(@intfloat_y.to_i)
  end
 
  def xInt(x)
    set_x(x)
  end
 
  def yInt(y)
    set_y(y)
  end
 
  def xInt=(x)
    xInt(x)
  end
 
  def yInt=(y)
    yInt(y)
  end
 
  def actualX
    return @intfloat_x.to_f
  end
 
  def actualY
    return @intfloat_y.to_f
  end
 
end



=begin
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Just a test program with the Float sprite movement. Move a red square around the
screen. Has a standard acceleration setup that is added to the sprite's X/Y
coordinates. If using Console debugger, hold SHIFT key to see values.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
=end
begin
  #Graphics.resize_screen(1024, 1024)
  sprite = Sprite.new
  sprite.bitmap = Bitmap.new(32,32)
  sprite.bitmap.fill_rect(0,0,32,32, Color.new(255, 0, 0))
  accel_x = 0.0
  accel_y = 0.0
 
  while true
    Graphics.update
    Input.update
   
    input = Input.dir8
   
    if Input.press?(Input::SHIFT)
      #puts ["Sprite Location: ", sprite.x, sprite.y]
      #puts ["Sprite Acutal: ",sprite.actualX, sprite.actualY]
      #puts ["accel: ", accel_x, accel_y]
      #puts "-------"
    end
    if Input.trigger?(Input::C)
      sprite.y += 10
    end
    if Input.trigger?(Input::B)
      sprite.x += 0.3
    elsif Input.trigger?(Input::A)
      sprite.x -= 0.3
    end
   
    # Get input and set sprite acceleration
    if input != 0
      if [1,2,3].include?(input)
        accel_y += (accel_y >= 0 ? 0.2 : 0.5)
      elsif [7,8,9].include?(input)
        accel_y -= (accel_y <= 0 ? 0.2 : 0.5)
      end
     
      if [1,4,7].include?(input)
        accel_x -= (accel_x <= 0 ? 0.2 : 0.5)
      elsif [3,6,9].include?(input)
        accel_x += (accel_x >= 0 ? 0.2 : 0.5)
      end
    end
   
    # If not inputting in a desired direction, slow down in that direction
    if [0,4,6].include?(input)
      accel_y = to0(accel_y, 0.1).to_f
    end
    if [0,2,8].include?(input)
      accel_x = to0(accel_x, 0.1).to_f
    end
   
    # Acceleration speed cap
    if accel_y.abs > 10
      accel_y = (accel_y > 0 ? 10 : -10)
    end
    if accel_x.abs > 10
      accel_x = (accel_x > 0 ? 10 : -10)
    end
   
    # Move the sprite
    sprite.x += accel_x
    sprite.y += accel_y
   
   
  end
end




I tested your code and found another issue with it.

Based on your code, of course the sprite x and y values could be changed into floats.
However, that also makes it unusable to other operation as well...

p sprite.x < 0 # throws error
p 10 + sprite.x # throws error
p sprite.x # throws IntFloat class (can't be used in numeric operation)


I think normal float variable is enough to fix float sprite movement.


#==============================================================================
# ** Sprite
#------------------------------------------------------------------------------
#  A sprite class for bitmap processing.
#==============================================================================
class Sprite
  #---------------------------------------------------------------------------
  # * Alias Listing
  #---------------------------------------------------------------------------
  $@ || alias_method(:set_x_alias, :"x=")
  $@ || alias_method(:set_y_alias, :"y=")
  $@ || alias_method(:get_x_alias, :"x")
  $@ || alias_method(:get_y_alias, :"y")
  #---------------------------------------------------------------------------
  # * Aliased method: x=, y=
  #---------------------------------------------------------------------------
  define_method(:x=)  {|amt| set_x_alias((@intfloat_x = amt).to_i) }
  define_method(:y=)  {|amt| set_y_alias((@intfloat_y = amt).to_i) }
  define_method(:x)   { @intfloat_x ||= get_x_alias }
  define_method(:y)   { @intfloat_y ||= get_y_alias }

end


Quote from: Wecoc on October 04, 2015, 02:26:59 pm
I found a bug on the LittleDrago's fix called [XP] Parallel process in the first map.
The battle call from event restarts over and over...


Oops... Sorry I didn't look at this thread until now (that's about 2 years :O)

Spoiler: ShowHide
#==============================================================================
# ** Scene_Map
#------------------------------------------------------------------------------
#  This class performs map screen processing.
#==============================================================================

class Scene_Map
  #--------------------------------------------------------------------------
  # * Constant
  #--------------------------------------------------------------------------
  Nothing = Struct.new(:update,:dispose)
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------
  $@ || alias_method(:main_parallel_process_fix, :main)
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def main
    init_parallel_process_fix
    main_parallel_process_fix
  end
  #--------------------------------------------------------------------------
  # * init_parallel_process_fix
  #--------------------------------------------------------------------------
  def init_parallel_process_fix
    @spriteset = Nothing.new
    Input.update
    update_systems
    transfer_player                    if $game_temp.player_transferring
    clear_flag_calling
    @spriteset.dispose
    return $scene = Scene_Gameover.new if $game_temp.gameover
    return $scene = Scene_Title.new    if $game_temp.to_title
  end
  #--------------------------------------------------------------------------
  # * update_systems
  #--------------------------------------------------------------------------
  def update_systems
    $game_map.update
    $game_system.map_interpreter.update
    $game_player.update
    $game_system.update
    $game_screen.update
  end
  #--------------------------------------------------------------------------
  # * clear_flag_calling
  #--------------------------------------------------------------------------
  def clear_flag_calling
    $game_temp.battle_calling = false
    $game_temp.shop_calling   = false
    $game_temp.name_calling   = false
    $game_temp.menu_calling   = false
    $game_temp.save_calling   = false
    $game_temp.debug_calling  = false
  end
end


untested ~
(Dunno if that would fix the problem or not, since it seems I can't reproduce the bug)

KK20

Yeah, that was years ago though. I've refined it more somewhere in one of these many projects.





   


   
   


   
   

Other Projects
RPG Maker XP AceUpgrade RMXP to RMVXA performance!
XPA TilemapTilemap rewrite with many features, including custom resolution!



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

Join the CP Discord Server!

LiTTleDRAgo

I see...
I just confirming things since I also got that same problem and found your post while searching in google.

Thank you for that post, I got inspiration because of it. :D

KK20

Thanks to Kise for reporting this: http://forum.chaos-project.com/index.php/topic,15768.0.html
Added to first post a way to make enemies disregard the use of a skill if it cannot even use it due to SP costs or disabling states.





   


   
   


   
   

Other Projects
RPG Maker XP AceUpgrade RMXP to RMVXA performance!
XPA TilemapTilemap rewrite with many features, including custom resolution!



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

Join the CP Discord Server!

KK20

Added a fix by LiTTleDRAgo regarding file existence testing in encrypted games.





   


   
   


   
   

Other Projects
RPG Maker XP AceUpgrade RMXP to RMVXA performance!
XPA TilemapTilemap rewrite with many features, including custom resolution!



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

Join the CP Discord Server!

KK20

[XP] Status Window Refreshing During Battle Events

Description

When in battle, the Window_BattleStatus keeps refreshing while a battle event is running. You can see this under Scene_Battle#update:

    # If battle event is running
    if $game_system.battle_interpreter.running?
      # Update interpreter
      $game_system.battle_interpreter.update
      # If a battler which is forcing actions doesn't exist
      if $game_temp.forcing_battler == nil
        # If battle event has finished running
        unless $game_system.battle_interpreter.running?
          # Rerun battle event set up if battle continues
          unless judge
            setup_battle_event
          end
        end
        # If not after battle phase
        if @phase != 5
          # Refresh status window
          @status_window.refresh
        end
      end
    end


Solution

A user posted a potential solution on the official RM forums: https://forums.rpgmakerweb.com/index.php?threads/rmxp-battle-text-lag.111312/#post-987292





   


   
   


   
   

Other Projects
RPG Maker XP AceUpgrade RMXP to RMVXA performance!
XPA TilemapTilemap rewrite with many features, including custom resolution!



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

Join the CP Discord Server!