summaryrefslogtreecommitdiffhomepage
path: root/system_manager.rb
blob: 4bd37b2d5567ece1a3e0e2cbdf651c674792c047 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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_accessor :const_name

    # How many frames need to pass before this System is executed when controlled by {FelFlame::Stage}
    attr_accessor :frame

    attr_writer :addition_triggers

    def addition_triggers
      @addition_triggers ||= []
    end

    attr_writer :removal_triggers

    def removal_triggers
      @removal_triggers ||= []
    end

    class <<self
      include Enumerable

      # Iterate over all Systems, sorted by their priority. You also call other enumerable methods instead of each, such as +each_with_index+ or +select+
      # @return [Enumerator]
      def each(&block)
        constants.map { |sym| const_get(sym) }.sort_by(&:priority).reverse.each(&block)
      end
    end

    # Creates a new System which can be accessed as a constant under the namespace {FelFlame::Systems}.
    # The name given is what constant the system is assigned to
    #
    # @example
    #   FelFlame::Systems.new('PassiveHeal', priority: -2, frame: 2) do
    #     FelFlame::Components::Health.each do |component|
    #       component.hp += 5
    #     end
    #   end
    #   # Only heal all characters with health every other frame.
    #   # Give it a low priority so other systems such as a
    #   #   Poison system would kill the player first
    #
    # @param name [String] The name this system will use. Needs to to be in the Ruby Constant format.
    # @param priority [Integer] Which priority order this system should be executed in relative to other systems. Higher means executed earlier.
    # @param block [Proc] The code you wish to be executed when the system is triggered. Can be defined by using a +do end+ block or using +{ }+ braces.
    def initialize(name, priority: 0, &block)
      FelFlame::Systems.const_set(name, self)
      @const_name = name
      @priority = priority
      @block = block
    end

    # Manually execute the system a single time
    def call
      @block.call
    end

    # Attempt to execute the system following the frame parameter set on this system.
    # @return [Boolean] +true+ if the frame of the {FelFlame::Stage} is a multiple of this System's frame setting and this system has executed, +false+ otherwise where the system has not executed.
    # For example if a System has its frame set to 3, it will only execute once every third frame that is called in FelFlame::Stage
    #
    def step; end

    # Redefine what code is executed by this System when it is called upon.
    # @param block [Proc] The code you wish to be executed when the system is triggered. Can be defined by using a +do end+ block or using +{ }+ braces.
    def redefine(&block)
      @block = block
    end

    # Removes triggers from this system
    # @param component_or_manager [Component or ComponentManager] The object to clear triggers from. Use Nil to clear triggers from all components associated with this system.
    # @return [Boolean] true
    def clear_triggers(*trigger_types, component_or_manager: nil)
      trigger_types = [:addition_triggers, :removal_triggers] if trigger_types.empty?
      trigger_types.each do |trigger_type|
        if component_or_manager.nil?
          send(trigger_type).each do |trigger|
            trigger.send(trigger_type).delete self
          end
          self.addition_triggers = []
        else
          send(trigger_type).delete component_or_manager
          component_or_manager.send(trigger_type).delete self
        end
      end
      true
    end

    # Add a component or component manager so that it triggers this system when the component or a component from the component manager is added to an entity
    # @param component_or_manager [Component or ComponentManager] The component or component manager to trigger this system when added
    # @return [Boolean] true
    def trigger_when_added(component_or_manager)
      self.addition_triggers |= [component_or_manager]
      component_or_manager.addition_triggers |= [self]
      true
    end

    # Add a component or component manager so that it triggers this system when the component or a component from the component manager is removed from an entity
    # @param component_or_manager [Component or ComponentManager] The component or component manager to trigger this system when removed
    # @return [Boolean] true
    def trigger_when_removed(component_or_manager)
      self.removal_triggers |= [component_or_manager]
      component_or_manager.removal_triggers |= [self]
      true
    end
  end
end