class FelFlame class Entities # Holds the unique ID of this entity # @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 # Creating a new Entity # @param components [Components] Can be any number of components, identical duplicates will be automatically purged however different components from the same component manager are allowed. # @return [Entity] def initialize(*components) # Assign new unique ID new_id = self.class.data.find_index(&:nil?) new_id = self.class.data.size if new_id.nil? self.id = new_id # Add each component add(*components) self.class.data[id] = self end # A hash that uses component manager constant names as keys, and where the values of those keys are arrays that contain the {FelFlame::ComponentManager#id IDs} of the components attached to this entity. # @return [Hash>] def components @components ||= {} end # An alias for the {#id ID reader} # @return [Integer] def to_i id end # Removes this Entity from the list and purges all references to this Entity from other Components, as well as its {id ID} and data. # @return [Boolean] +true+ def delete components.each do |component_manager, component_array| component_array.each do |component| component.entities.delete(self) end end FelFlame::Entities.data[id] = nil @components = {} @id = nil true end # Add any number components to the Entity. # @param components_to_add [Component] Any number of components created from any component manager # @return [Boolean] +true+ def add(*components_to_add) components_to_add.each do |component| if components[component.class].nil? components[component.class] = [component] component.entities.push self check_systems component, :addition_triggers elsif !components[component.class].include? component components[component.class].push component component.entities.push self check_systems component, :addition_triggers end end true end # triggers every system associated with this component's trigger # @return [Boolean] +true+ # @!visibility private def check_systems(component, trigger_type) component_calls = component.class.send(trigger_type) component.send(trigger_type).each do |system| component_calls |= [system] end component_calls.sort_by(&:priority).reverse.each(&:call) true end # Remove a component from the Entity # @param components_to_remove [Component] A component created from any component manager # @return [Boolean] +true+ def remove(*components_to_remove) components_to_remove.each do |component| check_systems component, :removal_triggers if component.entities.include? self component.entities.delete self components[component.class].delete component end true end # Export all data into a JSON String which can then be saved into a file # TODO: This function is not yet complete # @return [String] A JSON formatted String #def to_json() end class <] Array of all Entities that exist # @!visibility private def data @data ||= [] end # Gets an Entity from the given {id unique ID}. Usage is simular to how an Array lookup works # # @example # # This gets the Entity with ID 7 # FelFlame::Entities[7] # @param entity_id [Integer] # @return [Entity] returns the Entity that uses the given unique ID, nil if there is no Entity associated with the given ID def [](entity_id) data[entity_id] end # Iterates over all entities. The data is compacted so that means index does not correlate to ID. # You also call other enumerable methods instead of each, such as +each_with_index+ or +select+ # @return [Enumerator] def each(&block) data.compact.each(&block) end # Creates a new entity using the data from a JSON string # TODO: This function is not yet complete # @param json_string [String] A string that was exported originally using the {FelFlame::Entities#to_json to_json} function # @param opts [Keywords] What values(its {FelFlame::Entities#id ID} or the {FelFlame::ComponentManager#id component IDs}) should be overwritten TODO: this might change #def from_json(json_string, **opts) end end end end class FelFlame class Components @component_map = [] class <] def addition_triggers @addition_triggers ||= [] end # Stores references to systems that should be triggered when a # component from this manager is removed. # Do not edit this array as it is managed by FelFlame automatically. # @return [Array] def removal_triggers @removal_triggers ||= [] end # Stores references to systems that should be triggered when an # attribute from this manager is changed. # Do not edit this hash as it is managed by FelFlame automatically. # @return [Hash>] def attr_triggers @attr_triggers ||= {} end # Creates a new component and sets the values of the attributes given to it. If an attritbute is not passed then it will remain as the default. # @param attrs [Keyword: Value] You can pass any number of Keyword-Value pairs # @return [Component] def initialize(**attrs) # Prepare the object # (this is a function created with metaprogramming # 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 end class <] def addition_triggers @addition_triggers ||= [] end # Stores references to systems that should be triggered when this # component is removed from an enitity. # Do not edit this array as it is managed by FelFlame automatically. # @return [Array] def removal_triggers @removal_triggers ||= [] end # Stores references to systems that should be triggered when an # attribute from this component changed. # Do not edit this hash as it is managed by FelFlame automatically. # @return [Hash] def attr_triggers @attr_triggers ||= {} end # @return [Array] Array of all Components that belong to a given component manager # @!visibility private 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] def entities @entities ||= [] end # Update attribute values using a hash or keywords. # @return [Hash] Hash of updated attributes def update_attrs(**opts) opts.each do |key, value| send "#{key}=", value end end # Execute systems that have been added to execute on variable change # @return [Boolean] +true+ 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) 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. # @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? entity.remove self end self.class.data[id] = nil instance_variables.each do |var| instance_variable_set(var, nil) end true end # @return [Hash] A hash, where all the keys are attributes linked to their respective values. def attrs return_hash = instance_variables.each_with_object({}) do |key, final| final[key.to_s.delete_prefix('@').to_sym] = instance_variable_get(key) end return_hash.delete(:attr_triggers) return_hash end # 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 # # should return a json or hash of all data in this component #end end end class FelFlame class Systems # How early this System should be executed in a list of Systems attr_accessor :priority # The Constant name assigned to this System attr_reader :const_name # 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. # @!visibility private attr_writer :addition_triggers, :removal_triggers, :attr_triggers def priority=(priority) @priority = priority FelFlame::Stage.systems = FelFlame::Stage.systems.sort_by(&:priority) end # Stores references to components or their managers that trigger # this component when a component or component from that manager # is added to an entity. # Do not edit this hash as it is managed by FelFlame automatically. # @return [Array] def addition_triggers @addition_triggers ||= [] end # Stores references to components or their managers that trigger # this component when a component or component from that manager # is removed from an entity. # Do not edit this hash as it is managed by FelFlame automatically. # @return [Array] def removal_triggers @removal_triggers ||= [] end # Stores references to systems that should be triggered when an # attribute from this manager is changed # Do not edit this hash as it is managed by FelFlame automatically. # @return [Hash>] def attr_triggers @attr_triggers ||= {} end class <