class Game_Character attr_reader :id attr_reader :original_x attr_reader :original_y attr_reader :x attr_reader :y attr_reader :real_x attr_reader :real_y attr_accessor :sprite_size attr_reader :tile_id attr_accessor :character_name attr_accessor :character_hue attr_reader :opacity attr_reader :blend_type attr_reader :direction attr_accessor :pattern attr_reader :pattern_surf attr_accessor :lock_pattern attr_reader :move_route_forcing attr_accessor :through attr_accessor :animation_id attr_accessor :transparent attr_reader :move_speed attr_accessor :walk_anime attr_accessor :bob_height def initialize(map=nil) @map = map @id = 0 @original_x = 0 @original_y = 0 @x = 0 @y = 0 @real_x = 0 @real_y = 0 @sprite_size = [Game_Map::TILE_WIDTH,Game_Map::TILE_HEIGHT] @tile_id = 0 @character_name = "" @character_hue = 0 @opacity = 255 @blend_type = 0 @direction = 2 @pattern = 0 @pattern_surf = 0 @lock_pattern = false @move_route_forcing = false @through = false @animation_id = 0 @transparent = false @original_direction = 2 @original_pattern = 0 @move_type = 0 self.move_speed = 4 self.move_frequency = 6 @move_route = nil @move_route_index = 0 @original_move_route = nil @original_move_route_index = 0 @walk_anime = true # Whether character should animate while moving @step_anime = false # Whether character should animate while still @direction_fix = false @always_on_top = false @anime_count = 0 @stop_count = 0 @jump_count = 0 @jump_peak = 0 @bob_height = 0 @wait_count = 0 @moved_this_frame = false @locked = false @prelock_direction = 0 end def move_speed=(val) return if val==@move_speed @move_speed = val # @move_speed_real is the number of quarter-pixels to move each frame. There # are 128 quarter-pixels per tile. By default, it is calculated from # @move_speed and has these values (assuming 40 fps): # 1 => 1.6 # 80 frames per tile # 2 => 3.2 # 40 frames per tile # 3 => 6.4 # 20 frames per tile # 4 => 12.8 # 10 frames per tile - walking speed # 5 => 25.6 # 5 frames per tile - running speed (2x walking speed) # 6 => 32 # 4 frames per tile - cycling speed (1.25x running speed) self.move_speed_real = (val == 6) ? 32 : (1 << val) * 0.8 end def move_speed_real self.move_speed = @move_speed if !@move_speed_real return @move_speed_real end def move_speed_real=(val) @move_speed_real = val * 40.0 / Graphics.frame_rate end def move_frequency=(val) return if val==@move_frequency @move_frequency = val # @move_frequency_real is the number of frames to wait between each action # in a move route (not forced). Specifically, this is the number of frames # to wait after the character stops moving because of the previous action. # By default, it is calculated from @move_frequency and has these values # (assuming 40 fps): # 1 => 190 # 4.75 seconds # 2 => 144 # 3.6 seconds # 3 => 102 # 2.55 seconds # 4 => 64 # 1.6 seconds # 5 => 30 # 0.75 seconds # 6 => 0 # 0 seconds, i.e. continuous movement self.move_frequency_real = (40 - val * 2) * (6 - val) end def move_frequency_real self.move_frequency = @move_frequency if !@move_frequency_real return @move_frequency_real end def move_frequency_real=(val) @move_frequency_real = val * Graphics.frame_rate / 40.0 end def bob_height @bob_height = 0 if !@bob_height return @bob_height end def lock return if @locked @prelock_direction = 0 # Was @direction but disabled turn_toward_player @locked = true end def minilock @prelock_direction = 0 # Was @direction but disabled @locked = true end def lock? return @locked end def unlock return unless @locked @locked = false @direction = @prelock_direction if !@direction_fix and @prelock_direction != 0 end #============================================================================= # Information from map data #============================================================================= def map return (@map) ? @map : $game_map end def terrain_tag return self.map.terrain_tag(@x, @y) end def bush_depth return 0 if @tile_id > 0 or @always_on_top or @jump_count > 0 xbehind = @x + (@direction==4 ? 1 : @direction==6 ? -1 : 0) ybehind = @y + (@direction==8 ? 1 : @direction==2 ? -1 : 0) return Game_Map::TILE_HEIGHT if self.map.deepBush?(@x, @y) and self.map.deepBush?(xbehind, ybehind) return 12 if !moving? and self.map.bush?(@x, @y) return 0 end #============================================================================= # Passability #============================================================================= def passableEx?(x, y, d, strict=false) new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0) new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0) return false unless self.map.valid?(new_x, new_y) return true if @through if strict return false unless self.map.passableStrict?(x, y, d, self) return false unless self.map.passableStrict?(new_x, new_y, 10 - d, self) else return false unless self.map.passable?(x, y, d, self) return false unless self.map.passable?(new_x, new_y, 10 - d, self) end for event in self.map.events.values next if event.x != new_x || event.y != new_y || event.through return false if self != $game_player || event.character_name != "" end if $game_player.x == new_x and $game_player.y == new_y return false if !$game_player.through && @character_name != "" end return true end def passable?(x,y,d) return passableEx?(x,y,d,false) end def passableStrict?(x,y,d) return passableEx?(x,y,d,true) end #============================================================================= # Screen position of the character #============================================================================= def screen_x ret = ((@real_x - self.map.display_x) / Game_Map::X_SUBPIXELS).round ret += Game_Map::TILE_WIDTH/2 return ret end def screen_y_ground ret = ((@real_y - self.map.display_y) / Game_Map::Y_SUBPIXELS).round ret += Game_Map::TILE_HEIGHT return ret end def screen_y ret = screen_y_ground if jumping? n = ((2 * @jump_count * 20 / Graphics.frame_rate) - @jump_peak).abs return ret - (@jump_peak * @jump_peak - n * n) / 2 end return ret end def screen_z(height = 0) return 999 if @always_on_top z = screen_y_ground if @tile_id > 0 begin return z + self.map.priorities[@tile_id] * 32 rescue raise "Event's graphic is an out-of-range tile (event #{@id}, map #{self.map.map_id})" end end # Add z if height exceeds 32 return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0) end #============================================================================= # Movement #============================================================================= def moving? return @real_x != @x * Game_Map::REAL_RES_X || @real_y != @y * Game_Map::REAL_RES_Y end def jumping? return @jump_count > 0 end def straighten @pattern = 0 if @walk_anime or @step_anime @anime_count = 0 @prelock_direction = 0 end def force_move_route(move_route) if @original_move_route == nil @original_move_route = @move_route @original_move_route_index = @move_route_index end @move_route = move_route @move_route_index = 0 @move_route_forcing = true @prelock_direction = 0 @wait_count = 0 move_type_custom end def moveto(x, y) @x = x % self.map.width @y = y % self.map.height @real_x = @x * Game_Map::REAL_RES_X @real_y = @y * Game_Map::REAL_RES_Y @prelock_direction = 0 triggerLeaveTile end def triggerLeaveTile if @oldX && @oldY && @oldMap && (@oldX!=self.x || @oldY!=self.y || @oldMap!=self.map.map_id) Events.onLeaveTile.trigger(self,self,@oldMap,@oldX,@oldY) end @oldX = self.x @oldY = self.y @oldMap = self.map.map_id end def increase_steps @stop_count = 0 triggerLeaveTile end #============================================================================= # Movement commands #============================================================================= def move_type_random case rand(6) when 0..3; move_random when 4; move_forward when 5; @stop_count = 0 end end def move_type_toward_player sx = @x - $game_player.x sy = @y - $game_player.y if sx.abs + sy.abs >= 20 move_random return end case rand(6) when 0..3; move_toward_player when 4; move_random when 5; move_forward end end def move_type_custom return if jumping? or moving? while @move_route_index < @move_route.list.size command = @move_route.list[@move_route_index] if command.code == 0 if @move_route.repeat @move_route_index = 0 else if @move_route_forcing @move_route_forcing = false @move_route = @original_move_route @move_route_index = @original_move_route_index @original_move_route = nil end @stop_count = 0 end return end if command.code <= 14 case command.code when 1; move_down when 2; move_left when 3; move_right when 4; move_up when 5; move_lower_left when 6; move_lower_right when 7; move_upper_left when 8; move_upper_right when 9; move_random when 10; move_toward_player when 11; move_away_from_player when 12; move_forward when 13; move_backward when 14; jump(command.parameters[0], command.parameters[1]) end @move_route_index += 1 if @move_route.skippable or moving? or jumping? return end if command.code == 15 # Wait @wait_count = (command.parameters[0] * Graphics.frame_rate / 20) - 1 @move_route_index += 1 return end if command.code >= 16 and command.code <= 26 case command.code when 16; turn_down when 17; turn_left when 18; turn_right when 19; turn_up when 20; turn_right_90 when 21; turn_left_90 when 22; turn_180 when 23; turn_right_or_left_90 when 24; turn_random when 25; turn_toward_player when 26; turn_away_from_player end @move_route_index += 1 return end if command.code >= 27 case command.code when 27 $game_switches[command.parameters[0]] = true self.map.need_refresh = true when 28 $game_switches[command.parameters[0]] = false self.map.need_refresh = true when 29; self.move_speed = command.parameters[0] when 30; self.move_frequency = command.parameters[0] when 31; @walk_anime = true when 32; @walk_anime = false when 33; @step_anime = true when 34; @step_anime = false when 35; @direction_fix = true when 36; @direction_fix = false when 37; @through = true when 38; @through = false when 39; @always_on_top = true when 40; @always_on_top = false when 41 @tile_id = 0 @character_name = command.parameters[0] @character_hue = command.parameters[1] if @original_direction != command.parameters[2] @direction = command.parameters[2] @original_direction = @direction @prelock_direction = 0 end if @original_pattern != command.parameters[3] @pattern = command.parameters[3] @original_pattern = @pattern end when 42; @opacity = command.parameters[0] when 43; @blend_type = command.parameters[0] when 44; pbSEPlay(command.parameters[0]) when 45; result = eval(command.parameters[0]) end @move_route_index += 1 end end end def move_up(turn_enabled = true) turn_up if turn_enabled if passable?(@x, @y, 8) turn_up @y -= 1 increase_steps else check_event_trigger_touch(@x, @y-1) end end def move_down(turn_enabled = true) turn_down if turn_enabled if passable?(@x, @y, 2) turn_down @y += 1 increase_steps else check_event_trigger_touch(@x, @y+1) end end def move_left(turn_enabled = true) turn_left if turn_enabled if passable?(@x, @y, 4) turn_left @x -= 1 increase_steps else check_event_trigger_touch(@x-1, @y) end end def move_right(turn_enabled = true) turn_right if turn_enabled if passable?(@x, @y, 6) turn_right @x += 1 increase_steps else check_event_trigger_touch(@x+1, @y) end end def move_upper_left unless @direction_fix @direction = (@direction == 6 ? 4 : @direction == 2 ? 8 : @direction) end if (passable?(@x, @y, 8) and passable?(@x, @y - 1, 4)) or (passable?(@x, @y, 4) and passable?(@x - 1, @y, 8)) @x -= 1 @y -= 1 increase_steps end end def move_upper_right unless @direction_fix @direction = (@direction == 4 ? 6 : @direction == 2 ? 8 : @direction) end if (passable?(@x, @y, 8) and passable?(@x, @y - 1, 6)) or (passable?(@x, @y, 6) and passable?(@x + 1, @y, 8)) @x += 1 @y -= 1 increase_steps end end def move_lower_left unless @direction_fix @direction = (@direction == 6 ? 4 : @direction == 8 ? 2 : @direction) end if (passable?(@x, @y, 2) and passable?(@x, @y + 1, 4)) or (passable?(@x, @y, 4) and passable?(@x - 1, @y, 2)) @x -= 1 @y += 1 increase_steps end end def move_lower_right unless @direction_fix @direction = (@direction == 4 ? 6 : @direction == 8 ? 2 : @direction) end if (passable?(@x, @y, 2) and passable?(@x, @y + 1, 6)) or (passable?(@x, @y, 6) and passable?(@x + 1, @y, 2)) @x += 1 @y += 1 increase_steps end end def moveLeft90 # anticlockwise case self.direction when 2; move_right # down when 4; move_down # left when 6; move_up # right when 8; move_left # up end end def moveRight90 # clockwise case self.direction when 2; move_left # down when 4; move_up # left when 6; move_down # right when 8; move_right # up end end def move_random case rand(4) when 0; move_down(false) when 1; move_left(false) when 2; move_right(false) when 3; move_up(false) end end def move_random_range(xrange=-1,yrange=-1) dirs = [] # 0=down, 1=left, 2=right, 3=up if xrange<0 dirs.push(1); dirs.push(2) elsif xrange>0 dirs.push(1) if @x > @original_x - xrange dirs.push(2) if @x < @original_x + xrange end if yrange<0 dirs.push(0); dirs.push(3) elsif yrange>0 dirs.push(0) if @y < @original_y + yrange dirs.push(3) if @y > @original_y - yrange end return if dirs.length==0 case dirs[rand(dirs.length)] when 0; move_down(false) when 1; move_left(false) when 2; move_right(false) when 3; move_up(false) end end def move_random_UD(range=-1) move_random_range(0,range) end def move_random_LR(range=-1) move_random_range(range,0) end def move_toward_player sx = @x - $game_player.x sy = @y - $game_player.y return if sx == 0 and sy == 0 abs_sx = sx.abs abs_sy = sy.abs if abs_sx == abs_sy (rand(2) == 0) ? abs_sx += 1 : abs_sy += 1 end if abs_sx > abs_sy (sx > 0) ? move_left : move_right if not moving? and sy != 0 (sy > 0) ? move_up : move_down end else (sy > 0) ? move_up : move_down if not moving? and sx != 0 (sx > 0) ? move_left : move_right end end end def move_away_from_player sx = @x - $game_player.x sy = @y - $game_player.y return if sx == 0 and sy == 0 abs_sx = sx.abs abs_sy = sy.abs if abs_sx == abs_sy (rand(2) == 0) ? abs_sx += 1 : abs_sy += 1 end if abs_sx > abs_sy (sx > 0) ? move_right : move_left if not moving? and sy != 0 (sy > 0) ? move_down : move_up end else (sy > 0) ? move_down : move_up if not moving? and sx != 0 (sx > 0) ? move_right : move_left end end end def move_forward case @direction when 2; move_down(false) when 4; move_left(false) when 6; move_right(false) when 8; move_up(false) end end def move_backward last_direction_fix = @direction_fix @direction_fix = true case @direction when 2; move_up(false) when 4; move_right(false) when 6; move_left(false) when 8; move_down(false) end @direction_fix = last_direction_fix end def jump(x_plus, y_plus) if x_plus != 0 or y_plus != 0 if x_plus.abs > y_plus.abs (x_plus < 0) ? turn_left : turn_right else (y_plus < 0) ? turn_up : turn_down end end new_x = @x + x_plus new_y = @y + y_plus if (x_plus == 0 and y_plus == 0) || passable?(new_x, new_y, 0) straighten @x = new_x @y = new_y distance = [4, x_plus * x_plus + y_plus * y_plus].max @jump_peak = (6 + distance - move_speed).floor @jump_count = @jump_peak * Graphics.frame_rate / 20 @stop_count = 0 if self.is_a?(Game_Player) $PokemonTemp.dependentEvents.pbMoveDependentEvents end triggerLeaveTile end end def jumpForward case self.direction when 2; jump(0,1) # down when 4; jump(-1,0) # left when 6; jump(1,0) # right when 8; jump(0,-1) # up end end def jumpBackward case self.direction when 2; jump(0,-1) # down when 4; jump(1,0) # left when 6; jump(-1,0) # right when 8; jump(0,1) # up end end def turnGeneric(dir) return if @direction_fix oldDirection = @direction @direction = dir @stop_count = 0 pbCheckEventTriggerAfterTurning if dir != oldDirection end def turn_up; turnGeneric(8); end def turn_down; turnGeneric(2); end def turn_left; turnGeneric(4); end def turn_right; turnGeneric(6); end def turn_right_90 case @direction when 2; turn_left when 4; turn_up when 6; turn_down when 8; turn_right end end def turn_left_90 case @direction when 2; turn_right when 4; turn_down when 6; turn_up when 8; turn_left end end def turn_180 case @direction when 2; turn_up when 4; turn_right when 6; turn_left when 8; turn_down end end def turn_right_or_left_90 (rand(2) == 0) ? turn_right_90 : turn_left_90 end def turn_random case rand(4) when 0; turn_up when 1; turn_right when 2; turn_left when 3; turn_down end end def turn_toward_player sx = @x - $game_player.x sy = @y - $game_player.y return if sx == 0 and sy == 0 if sx.abs > sy.abs (sx > 0) ? turn_left : turn_right else (sy > 0) ? turn_up : turn_down end end def turn_away_from_player sx = @x - $game_player.x sy = @y - $game_player.y return if sx == 0 and sy == 0 if sx.abs > sy.abs (sx > 0) ? turn_right : turn_left else (sy > 0) ? turn_down : turn_up end end #============================================================================= # Updating #============================================================================= def update @moved_last_frame = @moved_this_frame if !$game_temp.in_menu # Update command update_command # Update movement if jumping?; update_jump elsif moving?; update_move else; update_stop end end # Update animation update_pattern end def update_command if @wait_count > 0 @wait_count -= 1 elsif @move_route_forcing move_type_custom elsif !@starting && !lock? && !moving? && !jumping? update_command_new end end def update_command_new # @stop_count is the number of frames since the last movement finished. # @move_frequency has these values: # 1 => @stop_count > 190 # 4.75 seconds # 2 => @stop_count > 144 # 3.6 seconds # 3 => @stop_count > 102 # 2.55 seconds # 4 => @stop_count > 64 # 1.6 seconds # 5 => @stop_count > 30 # 0.75 seconds # 6 => @stop_count > 0 # 0 seconds if @stop_count >= self.move_frequency_real case @move_type when 1; move_type_random when 2; move_type_toward_player when 3; move_type_custom end end end def update_jump @jump_count -= 1 @real_x = (@real_x * @jump_count + @x * Game_Map::REAL_RES_X) / (@jump_count + 1) @real_y = (@real_y * @jump_count + @y * Game_Map::REAL_RES_Y) / (@jump_count + 1) @moved_this_frame = true # End of a jump, so perform events that happen at this time Events.onStepTakenFieldMovement.trigger(self,self) if !jumping? && !moving? end def update_move # Move the character (the 0.1 catches rounding errors) distance = move_speed_real dest_x = @x * Game_Map::REAL_RES_X dest_y = @y * Game_Map::REAL_RES_Y if @real_x < dest_x @real_x += distance @real_x = dest_x if @real_x > dest_x - 0.1 else @real_x -= distance @real_x = dest_x if @real_x < dest_x + 0.1 end if @real_y < dest_y @real_y += distance @real_y = dest_y if @real_y > dest_y - 0.1 else @real_y -= distance @real_y = dest_y if @real_y < dest_y + 0.1 end # End of a step, so perform events that happen at this time Events.onStepTakenFieldMovement.trigger(self,self) if !jumping? && !moving? # Increment animation counter @anime_count += 1 if @walk_anime || @step_anime @moved_this_frame = true end def update_stop @anime_count += 1 if @step_anime @stop_count += 1 if !@starting && !lock? @moved_this_frame = false end def update_pattern return if @lock_pattern # Character has stopped moving, return to original pattern if @moved_last_frame && !@moved_this_frame && !@step_anime @pattern = @original_pattern @anime_count = 0 return end # Character has started to move, change pattern immediately if !@moved_last_frame && @moved_this_frame && !jumping? && !@step_anime @pattern = (@pattern + 1) % 4 if @walk_anime @anime_count = 0 return end # Calculate how many frames each pattern should display for, i.e. the time # it takes to move half a tile (or a whole tile if cycling). We assume the # game uses square tiles. frames_per_pattern = Game_Map::REAL_RES_X / (move_speed_real * 2.0) frames_per_pattern *= 2 if move_speed == 6 # Cycling/fastest speed return if @anime_count < frames_per_pattern # Advance to the next animation frame @pattern = (@pattern + 1) % 4 @anime_count -= frames_per_pattern end end