Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Zeus Lights & Shadows v1.3 for XP, VX and VXace by Zeus81
- # €30 for commercial use
- # Licence : http://creativecommons.org/licenses/by-nc-nd/3.0/
- # Contact : zeusex81@gmail.com
- # (fr) Manuel d'utilisation : http://pastebin.com/raw.php?i=xfu8yG0q
- # (en) User Guide : http://pastebin.com/raw.php?i=9bnzSHCw
- # Demo : https://www.dropbox.com/sh/cajvk3wf6ue0ivf/QA9zgrm2Vx
- module Zeus_Lights_Shadows
- extend self
- Disable_Lights = false
- Disable_Shadows = false
- Disable_AutoShadows = false
- def light(key = "event#@event_id")
- $game_map.lights[key] ||= Game_Light.new
- end
- def shadow(key = "event#@event_id")
- $game_map.shadows[key] ||= Game_Shadow.new
- end
- def set_shadowable(value, chara_id=@event_id)
- chara = zls_get_character(chara_id)
- chara.shadowable = value if chara
- end
- def zls_get_character(id)
- case id
- when 0 ; nil
- when -1 ; $game_player
- when -2, -3, -4; $game_player.followers[-id-2] if RPG_VERSION == :vxace
- when -5, -6, -7; $game_map.vehicles[-id-5] if RPG_VERSION != :xp
- else $game_map.events[id]
- end
- end
- end
- $imported ||= {}
- $imported[:Zeus_Lights_Shadows] = __FILE__
- RPG_VERSION = RUBY_VERSION == '1.8.1' ? defined?(Hangup) ? :xp : :vx : :vxace
- module Zeus_Animation
- def animate(variable, target_value, duration=0, ext=nil)
- @za_animations ||= {}
- if duration < 1
- update_animation_value(variable, target_value, 1, ext)
- @za_animations.delete(variable)
- else
- @za_animations[variable] = [target_value, duration.to_i, ext]
- end
- end
- private
- def update_animations
- @za_animations ||= {}
- @za_animations.delete_if do |variable, data|
- update_animation_value(variable, *data)
- (data[1] -= 1) == 0
- end
- end
- def calculate_next_value(value, target_value, duration)
- (value * (duration - 1) + target_value) / duration
- end
- def update_animation_value(variable, target_value, duration, ext)
- value = instance_variable_get(variable)
- method_name = "update_animation_variable_#{variable.to_s[1..-1]}"
- method_name = "update_animation_#{value.class}" unless respond_to?(method_name)
- send(method_name, variable, value, target_value, duration, ext)
- end
- def update_animation_Color(variable, value, target_value, duration, ext)
- value.red = calculate_next_value(value.red , target_value.red , duration)
- value.green = calculate_next_value(value.green, target_value.green, duration)
- value.blue = calculate_next_value(value.blue , target_value.blue , duration)
- value.alpha = calculate_next_value(value.alpha, target_value.alpha, duration)
- end
- def update_animation_Float(variable, value, target_value, duration, ext)
- value = calculate_next_value(value, target_value, duration)
- instance_variable_set(variable, value)
- end
- alias update_animation_Fixnum update_animation_Float
- alias update_animation_Bignum update_animation_Float
- end
- class Game_Light_Shadow_Base
- include Zeus_Animation
- attr_accessor :chara_id, :active, :visible, :filename, :opacity, :color,
- :x, :y, :ox, :oy, :zoom_x, :zoom_y, :parallax_x, :parallax_y,
- :direction, :directions, :pattern, :patterns, :anime_rate
- def initialize
- clear
- end
- def clear
- @chara_id = 0
- @active = false
- @visible = true
- @filename = ""
- @opacity = 255
- @color ||= Color.new(0, 0, 0)
- @color.set(0, 0, 0, 255)
- @x = 0
- @y = 0
- @ox = 0.5
- @oy = 0.5
- @zoom_x = 1.0
- @zoom_y = 1.0
- @zoom2 = Math.sqrt(100.0)
- @parallax_x = 1.0
- @parallax_y = 1.0
- @direction = 0
- @directions = 1
- @pattern = 0
- @patterns = 1
- @anime_rate = 0.0
- end
- def setup(filename)
- @active = true
- @filename = filename
- end
- def update
- update_animations
- update_pattern
- end
- def update_pattern
- if @anime_rate > 0 and Graphics.frame_count % @anime_rate < 1
- @pattern += 1
- @pattern %= @patterns
- end
- end
- def set_pos(x, y, duration=0)
- animate(:@x, x, duration)
- animate(:@y, y, duration)
- end
- def set_origin(ox, oy, duration=0)
- animate(:@ox, ox / 100.0, duration)
- animate(:@oy, oy / 100.0, duration)
- end
- def set_parallax(x, y, duration=0)
- animate(:@parallax_x, x, duration)
- animate(:@parallax_y, y, duration)
- end
- def set_opacity(opacity, duration=0)
- opacity = opacity * 255 / 100
- animate(:@opacity, opacity, duration)
- end
- def set_color(red, green, blue, alpha, duration=0)
- animate(:@color, Color.new(red, green, blue, alpha), duration)
- end
- def set_zoom(zoom, duration=0)
- zoom = Math.sqrt([1, zoom].max)
- animate(:@zoom2, zoom, duration)
- end
- def update_animation_variable_zoom2(variable, value, target_value, duration, ext)
- @zoom2 = calculate_next_value(value, target_value, duration)
- @zoom_y = @zoom_x = @zoom2 ** 2 / 100.0
- end
- end
- class Game_Shadow < Game_Light_Shadow_Base
- attr_accessor :size, :shadowable
- def clear
- super
- @size = nil
- @shadowable = true
- end
- def setup(filename_or_width, height=0)
- if filename_or_width.is_a?(String)
- @size = nil
- super(filename_or_width)
- else
- super("")
- @size ||= Rect.new(0, 0, 0, 0)
- @size.set(0, 0, filename_or_width.to_i, height.to_i)
- end
- end
- end
- class Game_Light < Game_Light_Shadow_Base
- attr_accessor :z, :angle, :mirror, :blend_type, :flicker,
- :wave_amp, :wave_length, :wave_speed, :wave_phase
- def clear
- super
- @z = 0xC001
- @angle = 0.0
- @mirror = false
- @blend_type = 1
- @wave_amp = 0
- @wave_length = 180
- @wave_speed = 360
- @wave_phase = 0.0
- @flicker = 1.0
- @flicker_variance = 0.0
- @flicker_rate = 4.0
- end
- def update
- super
- update_flicker
- end
- def update_flicker
- if @flicker_variance == 0
- @flicker = 1
- elsif @flicker_rate == 0 or Graphics.frame_count % @flicker_rate < 1
- case rand(100)
- when 33; value = 1 - @flicker_variance*2
- when 66; value = 1 + @flicker_variance*2
- else value = 1 - @flicker_variance + @flicker_variance*2*rand
- end
- animate(:@flicker, value, @flicker_rate.to_i)
- end
- end
- def set_angle(angle, duration=0)
- animate(:@angle, angle, duration)
- end
- def set_wave(amp, length, speed, duration=0)
- animate(:@wave_amp , amp , duration)
- animate(:@wave_length, length, duration)
- animate(:@wave_speed , speed , duration)
- end
- def set_flicker(variance, refresh_rate, duration=0)
- animate(:@flicker_variance , variance / 100.0, duration)
- animate(:@flicker_rate , refresh_rate , duration)
- end
- end
- class Game_Character
- attr_accessor :shadowable
- def shadowable
- @shadowable = true if @shadowable.nil?
- @shadowable
- end
- end
- class Game_Map
- include Zeus_Lights_Shadows
- attr_reader :lights, :shadows, :auto_shadows
- alias zeus_lights_shadows_setup setup
- def setup(map_id)
- @lights ||= {}
- @lights.each_value {|data| data.clear if data.chara_id >= 0}
- @shadows ||= {}
- @shadows.each_value {|data| data.clear if data.chara_id >= 0}
- zeus_lights_shadows_setup(map_id)
- @auto_shadows ||= []
- @auto_shadows.clear
- init_auto_shadows if RPG_VERSION != :xp and (!Disable_Shadows or Disable_AutoShadows)
- end
- alias zeus_lights_shadows_update update
- def update(*args)
- zeus_lights_shadows_update(*args)
- @lights.each_value {|data| data.update}
- @shadows.each_value {|data| data.update}
- end
- case RPG_VERSION
- when :vx
- def init_auto_shadows
- grounds = [1552...1664, 2816...3008, 3200...3392, 3584...3776, 3968...4160]
- is_ground = Proc.new {|id| grounds.any? {|range| range.include?(id)}}
- is_wall = Proc.new {|id| id >= 4352}
- data.xsize.times do |x|
- data.ysize.times do |y|
- tile_id = data[x, y, 0]
- if is_wall.call(tile_id)
- data[x, y, 1] = tile_id
- data[x, y, 0] = 0
- elsif !Disable_Shadows and !Disable_AutoShadows and
- x > 0 and y > 0 and is_ground.call(tile_id) and
- is_wall.call(data[x-1, y, 1]) and is_wall.call(data[x-1, y-1, 1])
- then
- @auto_shadows << [x*32, y*32, 16, 32]
- end
- end
- end
- end
- when :vxace
- def init_auto_shadows
- data.xsize.times do |x|
- data.ysize.times do |y|
- shadow_id = data[x, y, 3] & 0b1111
- data[x, y, 3] -= shadow_id
- next if Disable_Shadows or Disable_AutoShadows or shadow_id == 0
- case shadow_id
- when 3; @auto_shadows << [x*32, y*32, 32, 16]
- when 5; @auto_shadows << [x*32, y*32, 16, 32]
- when 10; @auto_shadows << [x*32+16, y*32, 16, 32]
- when 12; @auto_shadows << [x*32, y*32+16, 32, 16]
- when 15; @auto_shadows << [x*32, y*32, 32, 32]
- else
- 4.times do |i|
- if shadow_id[i] == 1
- @auto_shadows << [x*32 + i%2*16, y*32 + i/2*16, 16, 16]
- end
- end
- end
- end
- end
- end
- end # case RPG_VERSION
- end
- if RPG_VERSION == :xp
- Cache = RPG::Cache
- Game_Interpreter = Interpreter
- end
- class Game_Interpreter
- include Zeus_Lights_Shadows
- end
- class Spriteset_Lights_Shadows
- include Zeus_Lights_Shadows
- ShadowData = Struct.new(:bitmap, :opacity, :src_rect, :color,
- :x, :y, :ox, :oy, :zoom_x, :zoom_y)
- def initialize(viewport)
- @viewport = viewport
- @luminosity = 0
- unless Disable_Lights
- @night_layer = Sprite.new(@viewport)
- @night_layer.z = 0xC000
- @night_layer.blend_type = 2
- @night_layer.visible = false
- @night_color = Color.new(0, 0, 0)
- @lights = {}
- end
- unless Disable_Shadows
- @shadows_layer = Sprite.new(@viewport)
- @shadows_layer.visible = false
- @auto_shadows_color = Color.new(0, 0, 0, 255)
- @shadow_data = ShadowData.new
- @shadow_data.src_rect = Rect.new(0, 0, 0, 0)
- end
- refresh_bitmaps
- end
- def dispose
- unless Disable_Lights
- @night_layer.bitmap.dispose
- @night_layer.dispose
- @lights.each_value {|sprite| sprite.dispose}
- end
- unless Disable_Shadows
- @shadows_layer.bitmap.dispose
- @shadows_layer.dispose
- end
- end
- def update(character_sprites)
- refresh_bitmaps if bitmaps_need_refresh?
- update_luminosity
- update_lights unless Disable_Lights
- update_shadows(character_sprites) unless Disable_Shadows
- end
- def refresh_bitmaps
- unless Disable_Lights
- @night_layer.bitmap.dispose if @night_layer.bitmap
- @night_layer.bitmap = Bitmap.new(@viewport.rect.width, @viewport.rect.height)
- end
- unless Disable_Shadows
- @shadows_layer.bitmap.dispose if @shadows_layer.bitmap
- @shadows_layer.bitmap = Bitmap.new(@viewport.rect.width, @viewport.rect.height)
- end
- end
- def bitmaps_need_refresh?
- unless Disable_Lights
- return true if @night_layer.bitmap.width != @viewport.rect.width or
- @night_layer.bitmap.height != @viewport.rect.height
- end
- unless Disable_Shadows
- return true if @shadows_layer.bitmap.width != @viewport.rect.width or
- @shadows_layer.bitmap.height != @viewport.rect.height
- end
- return false
- end
- def update_luminosity
- r, g, b = @viewport.tone.red, @viewport.tone.green, @viewport.tone.blue
- @luminosity = (30*r.to_i + 59*g.to_i + 11*b.to_i) / 100
- end
- def update_lights
- $game_map.lights.delete_if do |key, data|
- if data.active
- update_light_sprite(key, data)
- else
- sprite = @lights.delete(key) and sprite.dispose
- end
- !data.active
- end
- @night_color.set(0, 0, 0)
- unless no_lights?
- r, g, b = @viewport.tone.red, @viewport.tone.green, @viewport.tone.blue
- @viewport.tone.red += @night_color.red = -r if r < 0
- @viewport.tone.green += @night_color.green = -g if g < 0
- @viewport.tone.blue += @night_color.blue = -b if b < 0
- end
- if @night_color.red + @night_color.green + @night_color.blue == 0
- @night_layer.visible = false
- return
- end
- @night_layer.visible = true
- @night_layer.x = @viewport.ox
- @night_layer.y = @viewport.oy
- @night_layer.bitmap.fill_rect(@night_layer.bitmap.rect, @night_color)
- @lights.each_value do |sprite|
- draw_layer_sprite(@night_layer.bitmap, sprite) if sprite.visible
- end
- end
- def no_lights?
- @lights.all? {|key,sprite| !sprite.visible}
- end
- def update_light_sprite(key, data)
- sprite = @lights[key] ||= Sprite.new(@viewport)
- chara = zls_get_character(data.chara_id)
- return unless sprite.visible = calculate_visible(data, chara)
- sprite.bitmap = Cache.picture(data.filename)
- w = sprite.bitmap.width / data.patterns
- h = sprite.bitmap.height / data.directions
- synchronize_direction_pattern(data, chara) if chara
- sprite.src_rect.set(data.pattern*w, data.direction*h, w, h)
- sprite.color = data.color
- sprite.x = calculate_x(data.x, data.parallax_x, chara)
- sprite.y = calculate_y(data.y, data.parallax_y, chara)
- sprite.z = data.z
- sprite.ox = data.ox * w
- sprite.oy = data.oy * h
- sprite.zoom_x = data.zoom_x * data.flicker
- sprite.zoom_y = data.zoom_y * data.flicker
- sprite.angle = data.angle
- sprite.mirror = data.mirror
- sprite.opacity = data.opacity
- sprite.blend_type = data.blend_type
- if RPG_VERSION != :xp
- sprite.wave_amp = data.wave_amp
- sprite.wave_length = data.wave_length
- sprite.wave_speed = data.wave_speed
- sprite.wave_phase = data.wave_phase
- sprite.update
- data.wave_phase = sprite.wave_phase
- end
- end
- def draw_layer_sprite(layer, sprite)
- if sprite.zoom_x == 1 and sprite.zoom_y == 1
- x = sprite.x - sprite.ox - @viewport.ox
- y = sprite.y - sprite.oy - @viewport.oy
- layer.blt(x, y, sprite.bitmap, sprite.src_rect, sprite.opacity)
- else
- dest_rect = Rect.new(
- sprite.x - sprite.ox * sprite.zoom_x - @viewport.ox,
- sprite.y - sprite.oy * sprite.zoom_y - @viewport.oy,
- sprite.src_rect.width * sprite.zoom_x,
- sprite.src_rect.height * sprite.zoom_y)
- layer.stretch_blt(dest_rect, sprite.bitmap, sprite.src_rect, sprite.opacity)
- end
- end
- def update_shadows(character_sprites)
- $game_map.shadows.delete_if {|key, data| !data.active}
- if @luminosity <= -64 or no_shadows?
- if @shadows_layer.visible
- @shadows_layer.visible = false
- character_sprites.each {|chara| chara.color.alpha = 0}
- end
- return
- end
- @shadows_layer.visible = true
- @shadows_layer.x = @viewport.ox
- @shadows_layer.y = @viewport.oy
- @shadows_layer.opacity = 128 + (@luminosity>0 ? @luminosity/2 : @luminosity*2)
- @shadows_layer.bitmap.clear
- $game_map.shadows.each_value do |data|
- update_shadow_data(data) if data.shadowable
- end
- draw_auto_shadows
- draw_airship_shadow if RPG_VERSION != :xp
- for sprite in character_sprites
- if !shadowable_character?(sprite.character)
- sprite.color.alpha = 0
- elsif sprite.x >= 0 and sprite.x < @shadows_layer.bitmap.width and
- sprite.y >= 4 and sprite.y < @shadows_layer.bitmap.height+4
- then
- sprite.color = @shadows_layer.bitmap.get_pixel(sprite.x, sprite.y-4)
- sprite.color.alpha = sprite.color.alpha * @shadows_layer.opacity / 255
- end
- end
- $game_map.shadows.each_value do |data|
- update_shadow_data(data) if !data.shadowable
- end
- end
- def no_shadows?
- $game_map.auto_shadows.empty? and
- $game_map.shadows.all? do |key,data|
- !calculate_visible(data, zls_get_character(data.chara_id))
- end
- end
- def shadowable_character?(chara)
- if RPG_VERSION != :xp and chara == $game_map.airship
- then chara.altitude < 16
- else chara.shadowable
- end
- end
- def draw_auto_shadows
- for x, y, w, h in $game_map.auto_shadows
- x = calculate_x(x, 1, nil) - @viewport.ox
- y = calculate_y(y, 1, nil) - @viewport.oy
- @shadows_layer.bitmap.fill_rect(x, y, w, h, @auto_shadows_color)
- end
- end
- def draw_airship_shadow
- return if $game_map.airship.transparent or $game_map.airship.altitude == 0
- bmp = Cache.system("Shadow")
- opacity = [$game_map.airship.altitude * 8, 255].min
- x = $game_map.airship.screen_x - bmp.width / 2 - @viewport.ox
- y = $game_map.airship.screen_y - bmp.height - @viewport.oy +
- $game_map.airship.altitude + 4
- @shadows_layer.bitmap.blt(x, y, bmp, bmp.rect, opacity)
- end
- def update_shadow_data(data)
- chara = zls_get_character(data.chara_id)
- return unless calculate_visible(data, chara)
- if data.size
- @shadow_data.src_rect.set(0, 0, w=data.size.width, h=data.size.height)
- @shadow_data.color = data.color
- @shadow_data.bitmap = nil
- else
- @shadow_data.bitmap = Cache.picture(data.filename)
- @shadow_data.opacity = data.opacity
- w = @shadow_data.bitmap.width / data.patterns
- h = @shadow_data.bitmap.height / data.directions
- synchronize_direction_pattern(data, chara) if chara
- @shadow_data.src_rect.set(data.pattern*w, data.direction*h, w, h)
- end
- @shadow_data.x = calculate_x(data.x, data.parallax_x, chara)
- @shadow_data.y = calculate_y(data.y, data.parallax_y, chara)
- @shadow_data.ox = data.ox * w
- @shadow_data.oy = data.oy * h
- @shadow_data.zoom_x = data.zoom_x
- @shadow_data.zoom_y = data.zoom_y
- draw_layer_shadow(@shadows_layer.bitmap, @shadow_data)
- end
- def draw_layer_shadow(layer, shadow)
- if shadow.bitmap
- draw_layer_sprite(layer, shadow)
- else
- dest_rect = Rect.new(
- shadow.x - shadow.ox * shadow.zoom_x - @viewport.ox,
- shadow.y - shadow.oy * shadow.zoom_y - @viewport.oy,
- shadow.src_rect.width * shadow.zoom_x,
- shadow.src_rect.height * shadow.zoom_y)
- layer.fill_rect(dest_rect, shadow.color)
- end
- end
- def synchronize_direction_pattern(data, chara)
- case data.directions
- when 2; data.direction = (chara.direction - 1) / 4
- when 4; data.direction = (chara.direction - 1) / 2
- when 8; data.direction = chara.direction - 1 - chara.direction / 5
- end
- if data.anime_rate == 0
- data.pattern = chara.pattern < 3 || RPG_VERSION == :xp ? chara.pattern : 1
- data.pattern %= data.patterns
- end
- end
- def calculate_visible(data, chara)
- if chara
- return false if chara.transparent
- if chara.is_a?(Game_Event)
- return false unless chara.list
- elsif RPG_VERSION == :vxace and chara.is_a?(Game_Follower)
- return false unless chara.visible?
- end
- end
- return false unless data.visible and data.opacity > 0
- return true if !data.filename.empty?
- return true if data.is_a?(Game_Shadow) and data.size
- return false
- end
- def calculate_x(x, parallax_x, chara)
- if chara
- x + chara.screen_x
- elsif RPG_VERSION == :xp or parallax_x != 1 or !$game_map.loop_horizontal?
- case RPG_VERSION
- when :xp ; x - $game_map.display_x * parallax_x / 4
- when :vx ; x - $game_map.display_x * parallax_x / 8
- when :vxace; x - $game_map.display_x * parallax_x * 32
- end
- else
- case RPG_VERSION
- when :vx ; $game_map.adjust_x(x * 8) / 8
- when :vxace; $game_map.adjust_x(x / 32.0) * 32
- end
- end
- end
- def calculate_y(y, parallax_y, chara)
- if chara
- y + chara.screen_y
- elsif RPG_VERSION == :xp or parallax_y != 1 or !$game_map.loop_vertical?
- case RPG_VERSION
- when :xp ; y - $game_map.display_y * parallax_y / 4
- when :vx ; y - $game_map.display_y * parallax_y / 8
- when :vxace; y - $game_map.display_y * parallax_y * 32
- end
- else
- case RPG_VERSION
- when :vx ; $game_map.adjust_y(y * 8) / 8
- when :vxace; $game_map.adjust_y(y / 32.0) * 32
- end
- end
- end
- end
- class Spriteset_Map
- alias zeus_lights_shadows_dispose dispose
- def dispose
- zeus_lights_shadows_dispose
- @lights_shadows.dispose
- end
- alias zeus_lights_shadows_update update
- def update
- zeus_lights_shadows_update
- @lights_shadows ||= Spriteset_Lights_Shadows.new(@viewport1)
- @lights_shadows.update(@character_sprites)
- end
- if RPG_VERSION != :xp and !Zeus_Lights_Shadows::Disable_Shadows
- def create_shadow() end
- def update_shadow() end
- def dispose_shadow() end
- end
- end
- $imported[:Zeus_Weather_Viewport] ||= __FILE__ if RPG_VERSION != :xp
- if $imported[:Zeus_Weather_Viewport] == __FILE__
- class Spriteset_Map
- alias zeus_weather_viewport_create_weather create_weather
- def create_weather
- zeus_weather_viewport_create_weather
- @weather.weather_viewport = @viewport1
- end
- end
- class Spriteset_Weather
- case RPG_VERSION
- when :vx
- def weather_viewport=(viewport)
- for sprite in @sprites
- sprite.viewport = viewport
- sprite.z = 0x8000
- end
- end
- when :vxace
- attr_accessor :weather_viewport
- alias zeus_weather_viewport_add_sprite add_sprite
- def add_sprite
- zeus_weather_viewport_add_sprite
- @sprites[-1].viewport = @weather_viewport
- @sprites[-1].z = 0x8000
- end
- end
- end
- end
- $imported[:Zeus_Event_Auto_Setup] ||= __FILE__
- if $imported[:Zeus_Event_Auto_Setup] == __FILE__
- class Game_Map
- alias zeus_auto_setup setup
- def setup(map_id)
- zeus_auto_setup(map_id)
- @events.each_value {|event| event.auto_setup}
- end
- end
- class Game_Event
- alias zeus_auto_setup_refresh refresh
- def refresh
- zeus_auto_setup_refresh
- auto_setup if $game_map.events[@id]
- end
- def auto_setup
- @auto_setup ||= {}
- return unless @auto_setup[:list] != @list and @auto_setup[:list] = @list
- unless @auto_setup.has_key?(@list)
- flag_a, flag_b, a, b = '<setup>', '</setup>', nil, nil
- @list.each_with_index do |command, id|
- if command.code % 300 == 108 # comment
- b = id if a and command.parameters[0].include?(flag_b)
- a = id if !a and command.parameters[0].include?(flag_a)
- break if a and b
- end
- end
- @auto_setup[@list] = a && b && @list.slice!(a, b-a+1).push(@list[-1])
- end
- if @auto_setup[@list]
- interpreter = Game_Interpreter.new
- interpreter.setup(@auto_setup[@list], @id)
- interpreter.update while interpreter.running?
- end
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement