summaryrefslogtreecommitdiffhomepage
path: root/lib/felflame/component_manager.rb
diff options
context:
space:
mode:
author_Tradam <[email protected]>2022-01-03 08:26:24 -0500
committerGitHub <[email protected]>2022-01-03 08:26:24 -0500
commita0f792d8feadf919290b8349dbc0eac143545927 (patch)
treeee70c5357d5969caaed08446c32746e656b223e6 /lib/felflame/component_manager.rb
parentb535a6b1bd8019dbeba17f3853b338383208c9b3 (diff)
downloadFelECS-a0f792d8feadf919290b8349dbc0eac143545927.tar.gz
FelECS-a0f792d8feadf919290b8349dbc0eac143545927.zip
Major 4.0 Update (#16)v4.0.04.0.0
See Changelog
Diffstat (limited to 'lib/felflame/component_manager.rb')
-rw-r--r--lib/felflame/component_manager.rb169
1 files changed, 98 insertions, 71 deletions
diff --git a/lib/felflame/component_manager.rb b/lib/felflame/component_manager.rb
index 43932d2..6ac7463 100644
--- a/lib/felflame/component_manager.rb
+++ b/lib/felflame/component_manager.rb
@@ -1,8 +1,9 @@
-class FelFlame
- class Components
+# frozen_string_literal: true
+
+module FelFlame
+ module Components
@component_map = []
- class <<self
- include Enumerable
+ class << self
# Creates a new {FelFlame::ComponentManager component manager}.
#
# @example
@@ -21,13 +22,14 @@ class FelFlame
raise(NameError.new, "Component Manager '#{component_name}' is already defined")
end
-
const_set(component_name, Class.new(FelFlame::ComponentManager) {})
+ update_const_cache
attrs.each do |attr|
- if FelFlame::Components.const_get(component_name).method_defined?("#{attr}") || FelFlame::Components.const_get(component_name).method_defined?("#{attr}=")
- raise NameError.new "The attribute name \"#{attr}\" is already a method"
+ if FelFlame::Components.const_get(component_name).method_defined?(attr.to_s) || FelFlame::Components.const_get(component_name).method_defined?("#{attr}=")
+ raise NameError, "The attribute name \"#{attr}\" is already a method"
end
+
FelFlame::Components.const_get(component_name).attr_accessor attr
end
attrs_with_defaults.each do |attr, _default|
@@ -46,28 +48,54 @@ class FelFlame
FelFlame::Components.const_get(component_name)
end
- # Iterate over all existing component managers. You also call other enumerable methods instead of each, such as +each_with_index+ or +select+
- # @return [Enumerator]
- def each(&block)
- constants.each(&block)
+ # Stores the components managers in {FelFlame::Components}. This
+ # is needed because calling `FelFlame::Components.constants`
+ # will not let you iterate over the value of the constants
+ # but will instead give you an array of symbols. This caches
+ # the convertion of those symbols to the actual value of the
+ # constants
+ # @!visibility private
+ def const_cache
+ @const_cache || update_const_cache
+ end
+
+ # Updates the array that stores the constants.
+ # Used internally by FelFlame
+ # @!visibility private
+ def update_const_cache
+ @const_cache = constants.map do |constant|
+ const_get constant
+ end
+ end
+
+ # Forwards undefined methods to the array of constants
+ # if the array can handle the request. Otherwise tells
+ # the programmer their code errored
+ # @!visibility private
+ def respond_to_missing?(method, *)
+ if const_cache.respond_to? method
+ true
+ else
+ super
+ end
+ end
+
+ # Makes component module behave like arrays with additional
+ # methods for managing the array
+ # @!visibility private
+ def method_missing(method, *args, **kwargs, &block)
+ if const_cache.respond_to? method
+ const_cache.send(method, *args, **kwargs, &block)
+ else
+ super
+ end
end
end
end
# Component Managers are what is used to create individual components which can be attached to entities.
- # When a Component is created from a Component Manager that has accessors given to it, you can set or get the values of those accessors using standard ruby message sending (e.g [email protected] = 5+), or by using the {#attrs} and {#update_attrs} methods instead.
+ # When a Component is created from a Component Manager that has accessors given to it, you can set or get the values of those accessors using standard ruby message sending (e.g [email protected] = 5+), or by using the {#to_h} and {#update_attrs} methods instead.
class ComponentManager
-
- # Holds the {id unique ID} of a component. The {id ID} is only unique within the scope of the component manager it was created from.
- # @return [Integer]
- attr_reader :id
-
- # A seperate attr_writer was made for documentation readability reasons.
- # Yard will list attr_reader is readonly which is my intention.
- # This value needs to be changable as it is set by other functions.
- # @!visibility private
- attr_writer :id
-
# Allows overwriting the storage of triggers, such as for clearing.
# This method should generally only need to be used internally and
# not by a game developer.
@@ -104,24 +132,40 @@ class FelFlame
def initialize(**attrs)
# Prepare the object
# (this is a function created with metaprogramming
- # in FelFlame::Components
+ # in FelFlame::Components)
set_defaults
- # Generate ID
- new_id = self.class.data.find_index { |i| i.nil? }
- new_id = self.class.data.size if new_id.nil?
- @id = new_id
-
# Fill params
attrs.each do |key, value|
send "#{key}=", value
end
# Save Component
- self.class.data[new_id] = self
+ self.class.push self
end
- class <<self
+ class << self
+ # Makes component managers behave like arrays with additional
+ # methods for managing the array
+ # @!visibility private
+ def respond_to_missing?(method, *)
+ if _data.respond_to? method
+ true
+ else
+ super
+ end
+ end
+
+ # Makes component managers behave like arrays with additional
+ # methods for managing the array
+ # @!visibility private
+ def method_missing(method, *args, **kwargs, &block)
+ if _data.respond_to? method
+ _data.send(method, *args, **kwargs, &block)
+ else
+ super
+ end
+ end
# Allows overwriting the storage of triggers, such as for clearing.
# This method should generally only need to be used internally and
@@ -155,41 +199,28 @@ class FelFlame
# @return [Array<Component>] Array of all Components that belong to a given component manager
# @!visibility private
- def data
+ def _data
@data ||= []
end
-
- # Gets a Component from the given {id unique ID}. Usage is simular to how an Array lookup works.
- #
- # @example
- # # this gets the 'Health' Component with ID 7
- # FelFlame::Components::Health[7]
- # @param component_id [Integer]
- # @return [Component] Returns the Component that uses the given unique {id ID}, nil if there is no Component associated with the given {id ID}
- def [](component_id)
- data[component_id]
- end
-
- # Iterates over all components within the component manager.
- # Special Enumerable methods like +map+ or +each_with_index+ are not implemented
- # @return [Enumerator]
- def each(&block)
- data.compact.each(&block)
- end
- end
-
- # An alias for the {id ID Reader}
- # @return [Integer]
- def to_i
- id
end
- # A list of entity ids that are linked to the component
- # @return [Array<Integer>]
+ # Entities that have this component
+ # @return [Array<Component>]
def entities
@entities ||= []
end
+ # A single entity. Use this if you expect the component to only belong to one entity and you want to access it.
+ # @return [Component]
+ def entity
+ if entities.empty?
+ Warning.warn("This component belongs to NO entities but you called the method that is intended for components belonging to a single entity.\nYou may have a bug in your logic.")
+ elsif entities.length > 1
+ Warning.warn("This component belongs to MANY entities but you called the method that is intended for components belonging to a single entity.\nYou may have a bug in your logic.")
+ end
+ entities.first
+ end
+
# Update attribute values using a hash or keywords.
# @return [Hash<Symbol, Value>] Hash of updated attributes
def update_attrs(**opts)
@@ -200,39 +231,35 @@ class FelFlame
# Execute systems that have been added to execute on variable change
# @return [Boolean] +true+
+ # @!visibility private
def attr_changed_trigger_systems(attr)
systems_to_execute = self.class.attr_triggers[attr]
systems_to_execute = [] if systems_to_execute.nil?
systems_to_execute |= attr_triggers[attr] unless attr_triggers[attr].nil?
- systems_to_execute.sort_by(&:priority).reverse.each(&:call)
+ systems_to_execute.sort_by(&:priority).reverse_each(&:call)
true
end
- # Removes this component from the list and purges all references to this Component from other Entities, as well as its {id ID} and data.
+ # Removes this component from the list and purges all references to this Component from other Entities, as well as its data.
# @return [Boolean] +true+.
def delete
addition_triggers.each do |system|
system.clear_triggers component_or_manager: self
end
- # This needs to be cloned because indices get deleted as
- # the remove command is called, breaking the loop if it
- # wasn't referencing a clone(will get Nil errors)
- iter = entities.map(&:clone)
- iter.each do |entity|
- #FelFlame::Entities[entity_id].remove self #unless FelFlame::Entities[entity_id].nil?
+ entities.reverse_each do |entity|
entity.remove self
end
- self.class.data[id] = nil
+ self.class._data.delete self
instance_variables.each do |var|
instance_variable_set(var, nil)
end
true
end
- # @return [Hash<Symbol, Value>] A hash, where all the keys are attributes linked to their respective values.
- def attrs
+ # @return [Hash<Symbol, Value>] A hash, where all the keys are attributes storing their respective values.
+ def to_h
return_hash = instance_variables.each_with_object({}) do |key, final|
final[key.to_s.delete_prefix('@').to_sym] = instance_variable_get(key)
end
@@ -243,8 +270,8 @@ class FelFlame
# Export all data into a JSON String, which could then later be loaded or saved to a file
# TODO: This function is not yet complete
# @return [String] a JSON formatted String
- #def to_json
+ # def to_json
# # should return a json or hash of all data in this component
- #end
+ # end
end
end