module AdventureRL
# The Mask is basically a bounding box or rectangle.
# It has a position (Point) and a size.
class Mask < Point
include Helpers::MethodHelper
# This array will be filled with any created Masks.
# Just so they won't get garbage collected
# (not sure how garbage collection works).
MASKS = []
# Default settings for Mask.
# Are superseded by settings passed to #initialize.
DEFAULT_SETTINGS = Settings.new({
position: {
x: 0,
y: 0
},
size: {
width: 64,
height: 64
},
origin: {
x: :left,
y: :top
},
assign_to: nil
})
# Pass settings Hash or AdventureRL::Settings as argument.
# Supersedes DEFAULT_SETTINGS.
def initialize settings_arg = {}
MASKS << self
settings = DEFAULT_SETTINGS.merge settings_arg
super *settings.get(:position).values
@size = settings.get(:size)
@origin = settings.get(:origin)
@assigned_to = []
assign_to settings.get(:assign_to) if (settings.get(:assign_to))
@layer = nil
call_setup_method settings_arg
end
# Assign this Mask to an instance.
# This will make all Mask methods available as
# a fallback on the instance itself.
# This also gives the possibility to define event methods
# on the object.
def assign_to object
Helpers::PipeMethods.pipe_methods_from object, to: self
@assigned_to << object
end
# Returns all objects this Mask was assigned to.
def get_assigned
return @assigned_to
end
# Returns true if the Mask has been
# assigned to the passed object.
def assigned_to? object
return @assigned_to.include? object
end
# Returns self.
# Used to get an instance's mask when it has been assigned to it.
def get_mask
return self
end
# Returns true.
# Used to verify if an instance has a Mask assigned to it.
def has_mask?
return true
end
# Returns the size of the Mask.
# Can pass an optional target argument,
# which can be either an axis (:width or :height),
# or :all, which returns a Hash with both values.
def get_size target = :all
target = target.to_sym
return @size if (target == :all)
return @size[target] if (@size.key?(target))
return nil
end
# Set a new Mask size.
# args are very similar to Point#set_position,
# only that the axes (:x and :y) are replaced with
# :width and :height.
def set_size *args
new_size = parse_size(*args)
@size[:width] = new_size[:width] if (new_size.key? :width)
@size[:height] = new_size[:height] if (new_size.key? :height)
end
alias_method :resize, :set_size
# Returns the set origin.
# Can pass an optional target argument,
# which can be either an axis (:x or :y),
# or :all, which returns a Hash with both values.
def get_origin target = :all
target = target.to_sym
return @origin if (target == :all)
return @origin[target] if (@origin.key?(target))
return nil
end
# Returns the position Integer of a specific side.
# Takes one mandatory argument, side,
# which can be one of the following:
# :left or :right::
# Returns the x position of the left or right side/border, respectively.
# :top or :bottom::
# Returns the y position of the top or bottom side/border, respectively.
def get_side side
side = side.to_sym
case side
when :left
return get_side_left
when :right
return get_side_right
when :top
return get_side_top
when :bottom
return get_side_bottom
else
return nil
end
end
# Returns the real window position of side.
# See #get_side for usage.
def get_real_side side
axis = :x if ([:left, :right].include? side)
axis = :y if ([:top, :bottom].include? side)
side_pos = get_side(side) * get_scale(axis)
return side_pos unless (has_layer?)
case side
when :left, :right
return get_layer.get_real_side(:left) + side_pos
when :top, :bottom
return get_layer.get_real_side(:top) + side_pos
else
return nil
end
end
# Returns the positions of all four sides.
def get_sides
return {
left: get_side(:left),
right: get_side(:right),
top: get_side(:top),
bottom: get_side(:bottom)
}
end
# Returns the real window positions of all four sides.
def get_real_sides
return {
left: get_real_side(:left),
right: get_real_side(:right),
top: get_real_side(:top),
bottom: get_real_side(:bottom)
}
end
# Returns a Point with the position of a specific corner.
# Takes two mandatory arguments:
# side_x:: Either :left or :right.
# side_y:: Either :top or :bottom.
def get_corner side_x, side_y
side_x = side_x.to_sym
side_y = side_y.to_sym
return Point.new(
get_side(side_x),
get_side(side_y)
) unless ([side_x, side_y].include? :center)
if (side_x == side_y)
center = get_center.values
return Point.new(*center)
elsif (side_x == :center)
return Point.new(
get_center(:x),
get_side(side_y)
)
elsif (side_y == :center)
return Point.new(
get_side(side_x),
get_center(:y)
)
end
return nil
end
# Returns the real window position of the corner.
# See #get_corner for usage.
def get_real_corner side_x, side_y
side_x = side_x.to_sym
side_y = side_y.to_sym
return Point.new(
get_real_side(side_x),
get_real_side(side_y)
) unless ([side_x, side_y].include? :center)
if (side_x == side_y)
center = get_real_center.values
return Point.new(*center)
elsif (side_x == :center)
return Point.new(
get_real_center(:x),
get_real_side(side_y)
)
elsif (side_y == :center)
return Point.new(
get_real_side(side_x),
get_real_center(:y)
)
end
return nil
end
# Returns the center Point of the Mask.
# An optional target argument can be passed,
# which can either be an axis (:x or :y),
# or :all, which returns a Hash with both values.
def get_center target = :all
target = target.to_sym
return Point.new(
get_center_x,
get_center_y
) if (target == :all)
return method("get_center_#{target.to_s}".to_sym).call if (get_point.keys.include? target)
return nil
end
# Returns the real window position of this center.
# See #get_center for usage.
def get_real_center target = :all
scale = get_scale
side = :left if (target == :x)
side = :top if (target == :y)
return Point.new(
(get_real_side(:left) + (get_center_x * scale[:x])),
(get_real_side(:top) + (get_center_y * scale[:y]))
) if (target == :all)
return (
get_real_side(side) + (method("get_center_#{target.to_s}".to_sym).call * scale[target])
) if (get_point.keys.include? target)
return nil
end
# Returns true if this Mask collides with other ...
# - Mask,
# - Point,
# - or Hash with keys :x and :y.
def collides_with? other
return collides_with_mask? other if (other.has_mask? rescue false)
return collides_with_point? other if (other.has_point? rescue false)
return collides_with_hash? other if (other.is_a?(Hash))
end
# Returns true if this Mask collides with other Mask.
def collides_with_mask? mask, checked = false
this_sides = get_real_sides
other_sides = mask.get_real_sides
return (
(
(
other_sides[:left] >= this_sides[:left] &&
other_sides[:left] <= this_sides[:right]
) || (
other_sides[:right] >= this_sides[:left] &&
other_sides[:right] <= this_sides[:right]
) || (
other_sides[:left] <= this_sides[:left] &&
other_sides[:right] >= this_sides[:right]
)
) && (
(
other_sides[:top] >= this_sides[:top] &&
other_sides[:top] <= this_sides[:bottom]
) || (
other_sides[:bottom] >= this_sides[:top] &&
other_sides[:bottom] <= this_sides[:bottom]
) || (
other_sides[:top] <= this_sides[:top] &&
other_sides[:bottom] >= this_sides[:bottom]
)
)
) || (!checked && mask.collides_with_mask?(self, true))
end
# Returns true if this Mask collides with other Point.
def collides_with_point? point
real_point = point.get_real_point
real_sides = get_real_sides
return (
real_point.x >= real_sides[:left] &&
real_point.x <= real_sides[:right] &&
real_point.y >= real_sides[:top] &&
real_point.y <= real_sides[:bottom]
)
end
# Returns true if this Mask collides with other Hash.
def collides_with_hash? hash
if (hash.keys.include_all?(:x, :y))
point = Point.new hash[:x], hash[:y]
return collides_with_point? point
end
return nil
end
# Set the parent Layer.
def set_layer layer
error(
"Passed argument `layer' must be an instance of `Layer', but got",
"`#{layer.inspect}:#{layer.class.name}'."
) unless (layer.is_a? Layer)
@layer = layer
end
# Returns the parent Layer.
def get_layer
return @layer
end
# Returns true if this Mask has a parent Layer.
def has_layer?
return !!@layer
end
private
def call_setup_method args
return unless (method_exists?(:setup))
if (method_takes_arguments?(:setup))
setup args
else
setup
end
end
def call_method_on_assigned method_name, *args
get_assigned.each do |assigned_to|
meth = nil
meth = assigned_to.method(method_name) if (assigned_to.methods.include?(method_name))
meth.call(*args) if (meth)
end
end
def parse_size *args
size = {}
case args.size
when 2
size[:width] = args[0]
size[:height] = args[1]
when 1
Helpers::Error.error(
"Ambiguous argument `#{args[0]}' for Mask##{__method__}"
) unless (args[0].is_a?(Hash))
Helpers::Error.error(
"Hash must include either :width, :height, or both keys for Mask##{__method__}"
) unless (args[0].keys.include_any?(:width, :height))
size[:width] = args[0][:width] if (args[0][:width])
size[:height] = args[0][:height] if (args[0][:height])
else
Helpers::Error.error(
"Invalid amount of arguments for Mask##{__method__}.",
"Pass either two arguments representing the width and height axes, respectively, or",
"pass a single hash with the keys :width and :height with their respective axes values."
)
end
return size
end
# Returns this Masks Layer scale, if it has one.
def get_scale target = :all
return get_layer.get_real_scale target if (has_layer?)
scale = { x: 1.0, y: 1.0 }
return scale[target] if (scale.key? target)
return scale if (target == :all)
return nil
end
def get_side_left
case get_origin(:x)
when :left
return get_position(:x)
when :right
return get_position(:x) - get_size(:width)
when :center
return get_position(:x) - (get_size(:width) * 0.5)
else
return nil
end
end
def get_side_right
case get_origin(:x)
when :left
return get_position(:x) + get_size(:width)
when :right
return get_position(:x)
when :center
return get_position(:x) + (get_size(:width) * 0.5)
else
return nil
end
end
def get_side_top
case get_origin(:y)
when :top
return get_position(:y)
when :bottom
return get_position(:y) - get_size(:height)
when :center
return get_position(:y) - (get_size(:height) * 0.5)
else
return nil
end
end
def get_side_bottom
case get_origin(:y)
when :top
return get_position(:y) + get_size(:height)
when :bottom
return get_position(:y)
when :center
return get_position(:y) + (get_size(:height) * 0.5)
else
return nil
end
end
def get_center_x
case get_origin(:x)
when :left
return get_position(:x) + (get_size(:width) * 0.5)
when :right
return get_position(:x) - (get_size(:width) * 0.5)
when :center
return get_position(:x)
end
end
def get_center_y
case get_origin(:y)
when :top
return get_position(:y) + (get_size(:height) * 0.5)
when :bottom
return get_position(:y) - (get_size(:height) * 0.5)
when :center
return get_position(:y)
end
end
end
end