diff options
| author | _Tradam <[email protected]> | 2022-01-03 08:26:24 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-01-03 08:26:24 -0500 |
| commit | a0f792d8feadf919290b8349dbc0eac143545927 (patch) | |
| tree | ee70c5357d5969caaed08446c32746e656b223e6 /lib/felflame/component_manager.rb | |
| parent | b535a6b1bd8019dbeba17f3853b338383208c9b3 (diff) | |
| download | FelECS-v4.0.0.tar.gz FelECS-v4.0.0.zip | |
See Changelog
Diffstat (limited to 'lib/felflame/component_manager.rb')
| -rw-r--r-- | lib/felflame/component_manager.rb | 169 |
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 |
