#===============================================================================# Stealth System
# By Jet10985 (Jet)
# Inspired By: Touchfuzzy
#===============================================================================
# This script will implement a realistic stealth detection system for use
# with events. All measurements are interpreted as metres.
# This script has: 20 customization options.
#===============================================================================
# Overwritten Methods:
# None
#-------------------------------------------------------------------------------
# Aliased methods:
# Game_System: initialize
#===============================================================================
=begin
To set an event's height, use this Comment in their event page:
HEIGHT = number
--------------------------------------------------------------------------------
To set the player's default camo rating, use this in an event "Script...":
change_player_camo(number)
--------------------------------------------------------------------------------
To give an equipment a bonus to camo rating, use this in the armor's notebox:
<camo = number>
--------------------------------------------------------------------------------
To change the player's height, use this in an event "Script...":
change_player_height(number)
--------------------------------------------------------------------------------
To manually set a region or terrain tag's camo or height for a map, use these in
an event's "Script..." command:
set_region_height(region_id, number)
set_terrain_height(terrain_id, number)
set_region_camo(region_id, number)
set_terrain_camo(terrain_id, number)
--------------------------------------------------------------------------------
You can check for sighting of any event or the player using this
in an event's Conditional Branch "Script...":
can_see?(char_id)
char_id by default is -1, which means the Player
0 is the calling event
anything above 0 is the id of an event on the map
=end
module Jet
module Stealth
# This is the button used for the player to enter a "ducking" position.
DUCK_BUTTON = :ALT
# This is the suffix of the sprite that the player's will be changed to when
# ducking. If the player's sprite name is "Jimmy" and the suffix is "_duck"
# then the game will look for "Jimmy_duck"
DUCK_SUFFIX = ""
# This is how many metres a single tile represents.
TILE_METRE_DIST = 75
# this is how tall events are by default.
DEFAULT_EVENT_HEIGHT = 1.7
# This is how tall the player is by default.
PLAYER_HEIGHT = 1.7
# this is how much "camo" rating is gained from ducking.
DUCKING_CAMO_GAINED = 20
# This is how much "camo" rating is lost when moving while dashing.
DASHING_CAMO_LOST = 15
# This is how much "camo" is lost when just moving without dashing.
MOVEMENT_CAMO_LOST = 10
# This is how much movement speed is lost when ducking.
DUCKING_SPEED_LOST = 2
# This is how many metres away a character has to be to have the
# detector begin to lose some vision per metre.
POINT_OF_VISION_LOSS = TILE_METRE_DIST * 5
# This is how much "camo" is gained per metre once distance has passed the
# POINT_OF_VISION_LOSS mark.
VISION_LOSS_PER_METRE = 0.00271
# How tall are unpassable tiles by default? This only applies if there are
# no events on the tile.
UNPASSABLE_HEIGHT = (1.0 / 0.0)
# These are the default heights of Region IDs for every map.
# A map's specific region height can be changed by using a script command
# found in the instructions above.
# This follows the format: region_id => height
REGION_HEIGHTS = {
#1 => 0,
#2 => 2,
#3 => 0.2
}
# This is the default height of any undefined region above.
REGION_HEIGHTS.default = 0
# These are the default heights of Terrains for every map.
# A map's specific terrain height can be changed by using a script command
# found in the instructions above.
# This follows the format: terrain_id => height
TERRAIN_HEIGHTS = {
#1 => 0,
#2 => 2,
#3 => 0.2
}
# This is the default height of any undefined terrain above.
TERRAIN_HEIGHTS.default = 0
# These are the default camo of Region IDs for every map.
# A map's specific region camo can be changed by using a script command
# found in the instructions above.
# This follows the format: region_id => camo
REGION_CAMOS = {}
# This is the default camo of any undefined region above.
REGION_CAMOS.default = 0
# These are the default camo of Terrains for every map.
# A map's specific terrain camo can be changed by using a script command
# found in the instructions above.
# This follows the format: terrain_id => camo
TERRAIN_CAMOS = {}
# This is the default camo of any undefined terrain above.
TERRAIN_CAMOS.default = 0
end
end
#===============================================================================
# DON'T EDIT FURTHER UNLESS YOU KNOW WHAT TO DO.
#===============================================================================
module Math
module_function
def dist_form(x, y, x2, y2)
sqrt((x2 - x).abs ** 2 + (y2 - y).abs ** 2)
end
def horizon(metres)
3.856 * sqrt(metres) * 1000
end
def rad_to_deg(rads)
rads * 180 / PI
end
end
class RPG::Armor
def camo
@camoflague ||= (
f = self.note.match(/<CAMO[ ]*(\

=)[ ]*(\d+)>/i)
f.nil? ? 0 : f[2].to_f
)
end
end
class Game_Actor
def character_name
@character_name + ($game_player.ducking? ? Jet::Stealth:

UCK_SUFFIX : "")
end
end
class Game_Event
def check_for_comment(regexp)
return false if empty?
@list.select {|a|
[108, 408].include?(a.code)
}.collect {|a|
a.parameters[0]
}.each {|a|
if !a[regexp].nil?
return a.match(regexp)
end
}
return false
end
def height
f = check_for_comment(/HEIGHT[ ]*(\

=)[ ]*(\d+)/i)
f == false ? Jet::Stealth:

EFAULT_EVENT_HEIGHT : f[2].to_f
end
def movable?
return false if moving?
return false if @move_route_forcing
return false if $game_message.busy? || $game_message.visible
return true
end
end
class Game_Player
def height
if ducking?
if moving?
f = (2.0 / 3.0)
else
f = (1.0 / 3.0)
end
else
f = 1.0
end
$game_system.player_height * f
end
def ducking?
Input.press?(Jet::Stealth:

UCK_BUTTON)
end
def camoflague
camo = 0
camo += $game_system.player_camo
camo += $game_map.xy_camo(self.x, self.y)
camo += $game_party.members[0].armors.inject {|a, b| a.camo + b.camo }
if moving?
if dash?
camo -= Jet::Stealth:

ASHING_CAMO_LOST
else
camo -= Jet::Stealth::MOVEMENT_CAMO_LOST
end
end
camo += Jet::Stealth:

UCKING_CAMO_GAINED if ducking?
[camo, 0].max
end
def real_move_speed
super - (ducking? ? Jet::Stealth:

UCKING_SPEED_LOST : 0)
end
end
class Game_Character
attr_writer :sighted
def camoflague
camo = 0
camo += $game_map.xy_camo(self.x, self.y)
camo -= Jet::Stealth::MOVEMENT_CAMO_LOST if moving?
[camo, 0].max
end
def sight_range
map_height = $game_map.xy_height(self.x, self.y)
Math.horizon(self.height + map_height)
end
def true_height
self.height + $game_map.xy_height(self.x, self.y)
end
def horizontal_angle_from(char)
x_dist = self.x - char.x
y_dist = self.y - char.y
hypo = Math.hypot(x_dist, y_dist)
(Math.rad_to_deg(Math.atan2(y_dist, x_dist)) - 180).abs
end
def viewing_angle_horizontal
case self.direction
when 2
[210, 330]
when 4
[120, 240]
when 6
10
when 8
[30, 150]
end
end
def vertical_angle_from(char)
height = (self.true_height - $game_map.xy_height(char.x, char.y)).abs
distance = Math.dist_form(self.x, self.y, char.x, char.y)
distance *= Jet::Stealth::TILE_METRE_DIST
hypo = Math.hypot(height, distance)
Math.rad_to_deg(Math.asin(height / hypo.to_f))
end
def tiles_seen(x, y, x1, y1, char = nil)
tiles = {}
sx = x - x1
sy = y - y1
if sx.abs > sy.abs
direction = sx > 0 ? 4 : 6
elsif sy != 0
direction = sy > 0 ? 8 : 2
end
x_distance = x1 - x
y_distance = y1 - y
total_distance = Math.dist_form(x, y, x1, y1)
x_per_tile = x_distance / total_distance
y_per_tile = y_distance / total_distance
x_dist_t = 0.0
y_dist_t = 0.0
if !char.nil?
vert_angle = vertical_angle_from(char)
end
self_height = self.true_height
until x_dist_t.abs >= x_distance.abs && y_dist_t.abs >= y_distance.abs
x_dist_t += x_per_tile
y_dist_t += y_per_tile
x_tile = (x + x_dist_t).to_i
y_tile = (y + y_dist_t).to_i
height = $game_map.total_height(x_tile, y_tile, direction)
tiles[[x_tile, y_tile]] = [height]
if !char.nil?
dist = (x_dist_t.abs + y_dist_t.abs) - total_distance
dist = dist.abs
viewing_height = self.height * dist - height
tiles[[x_tile, y_tile]].push(viewing_height)
end
end
tiles
end
def sighted?(char)
distance = Math.dist_form(self.x, self.y, char.x, char.y)
distance *= Jet::Stealth::TILE_METRE_DIST
return false if distance > sight_range
h_angle = horizontal_angle_from(char)
case viewing_angle_horizontal
when Array
h_viewing_angle = viewing_angle_horizontal
return false if !h_angle.between?(*h_viewing_angle)
when 10
return false unless h_angle.between?(0, 60) || h_angle.between?(320, 360)
end
v_angle = vertical_angle_from(char)
if self.true_height >= $game_map.xy_height(char.x, char.y)
return false unless v_angle <= 75
v_viewing_angle = 75
else
return false unless v_angle <= 60
v_viewing_angle = 60
end
camo = char.camoflague
distance += (self.true_height - $game_map.xy_height(char.x, char.y)).abs
if distance.between?(Jet::Stealth:

OINT_OF_VISION_LOSS, sight_range)
add = (distance - Jet::Stealth:

OINT_OF_VISION_LOSS)
add *= Jet::Stealth::VISION_LOSS_PER_METRE
camo += add
elsif distance > sight_range
return false
elsif distance < Jet::Stealth:

OINT_OF_VISION_LOSS
camo /= 4
end
camo = [camo, 0].max
return false if rand(100) + 1 <= camo
tiles = tiles_seen(self.x, self.y, char.x, char.y, char)
tiles.each {|coords, heights|
next if coords == [self.x, self.y]
if heights[1] <= 0
return false
end
}
return true
end
end
class Game_System
attr_accessor

layer_height,

layer_camo
alias jet3745_initialize initialize
def initialize(*args, &block)
jet3745_initialize(*args, &block)
@player_height = Jet::Stealth:

LAYER_HEIGHT
@player_camo = 0
end
end
class Game_Map
def xy_height(x, y, dir = 0)
reg = region_id(x, y)
reg_h = region_heights(reg)
ter = terrain_tag(x, y)
ter_h = terrain_heights(ter)
pas_h = $game_map.passable?(x, y, dir) ? 0 : Jet::Stealth::UNPASSABLE_HEIGHT
reg_h + ter_h + pas_h
end
def xy_camo(x, y)
reg = region_id(x, y)
reg_c = region_camos(reg)
ter = terrain_tag(x, y)
ter_c = terrain_camos(ter)
reg_c + ter_c
end
def total_height(x, y, dir = 0)
xy = xy_height(x, y, dir)
evs = $game_map.events_xy(x, y)
ev_height = evs.sort {|a, b| a.height <=> b.height }
ev_height = (ev_height[-1].true_height rescue 0)
xy + (ev_height || 0)
end
def region_heights(reg)
(@map_region_heights ||= {})[@map_id] ||= Jet::Stealth::REGION_HEIGHTS
@map_region_heights[@map_id][reg]
end
def set_region_height(reg, height)
(@map_region_heights ||= {})[@map_id] ||= Jet::Stealth::REGION_HEIGHTS
@map_region_heights[@map_id][reg] = height
end
def terrain_heights(ter)
(@map_terrain_heights ||= {})[@map_id] ||= Jet::Stealth::TERRAIN_HEIGHTS
@map_terrain_heights[@map_id][ter]
end
def set_region_height(ter, height)
(@map_terrain_heights ||= {})[@map_id] ||= Jet::Stealth::TERRAIN_HEIGHTS
@map_terrain_heights[@map_id][ter] = height
end
def region_camos(reg)
(@map_region_camos ||= {})[@map_id] ||= Jet::Stealth::REGION_CAMOS
@map_region_camos[@map_id][reg]
end
def set_region_camo(reg, camo)
(@map_region_camos ||= {})[@map_id] ||= Jet::Stealth::REGION_CAMOS
@map_region_camos[@map_id][reg] = camo
end
def terrain_camos(ter)
(@map_terrain_camos ||= {})[@map_id] ||= Jet::Stealth::TERRAIN_CAMOS
@map_terrain_camos[@map_id][ter]
end
def set_region_camo(ter, camo)
(@map_terrain_camos ||= {})[@map_id] ||= Jet::Stealth::TERRAIN_CAMOS
@map_terrain_camos[@map_id][ter] = camo
end
end
class Game_Interpreter
def set_region_height(reg, height)
$game_map.set_right_height(reg, height)
end
def set_terrain_height(ter, height)
$game_map.set_terrain_height(ter, height)
end
def set_region_camo(reg, camo)
$game_map.set_right_height(reg, camo)
end
def set_terrain_camo(ter, camo)
$game_map.set_terrain_height(ter, camo)
end
def change_player_height(height)
$game_system.player_height = height
end
def change_player_camo(camo)
$game_system.player_camo = camo
end
def can_see?(ev = -1)
get_character(0).sighted?(get_character(ev))
end
end