summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorrealtradam <[email protected]>2021-05-26 01:24:30 -0400
committerrealtradam <[email protected]>2021-05-26 01:24:30 -0400
commit7bec71db2680e0503f39c31047f5f90ca89433df (patch)
tree4d987b479b0670256af5c1a4ffe5ab7b75fb23b4
downloadFelECS-7bec71db2680e0503f39c31047f5f90ca89433df.tar.gz
FelECS-7bec71db2680e0503f39c31047f5f90ca89433df.zip
init
-rw-r--r--README.mdown284
-rw-r--r--component_manager.rb35
-rw-r--r--components/00_renderable.rb16
-rw-r--r--components/01_sprite.rb44
-rw-r--r--components/02_label.rb27
-rw-r--r--components/03_player_control.rb21
-rw-r--r--components/04_map.rb21
-rw-r--r--components/05_interactable.rb16
-rw-r--r--components/06_collidable.rb22
-rw-r--r--components/07_battle.rb4
-rw-r--r--components/07_indoor.rb4
-rw-r--r--components/07_overworld.rb16
-rw-r--r--components/debug_singleton.rb13
-rw-r--r--entity_manager.rb63
-rw-r--r--helpers/00_tileset.rb49
-rw-r--r--helpers/01_component.rb102
-rw-r--r--logos/felflame-logo-text.pngbin0 -> 38979 bytes
-rw-r--r--logos/felflame-logo-text.svg172
-rw-r--r--logos/felflame-logo.pngbin0 -> 25459 bytes
-rw-r--r--logos/felflame-logo.svg97
-rw-r--r--signatures.rb54
-rw-r--r--system_manager.rb5
-rw-r--r--systems/00_update_levels.rb34
-rw-r--r--systems/10_player.rb41
-rw-r--r--systems/99_render.rb37
-rw-r--r--test.rb18
26 files changed, 1195 insertions, 0 deletions
diff --git a/README.mdown b/README.mdown
new file mode 100644
index 0000000..a8f3b46
--- /dev/null
+++ b/README.mdown
@@ -0,0 +1,284 @@
+
+![FelFlame](https://filestorage.catgirls.rodeo/images/felflame-logo-full.png)
+
+# What is this?
+
+This is a Ruby ECS Framework for developing games. It is still a work in progress in the early stages, is not fit for use, and does not work.
+It is originally designed with DragonRuby in mind but should work with anything that runs on Ruby.
+
+I wanted a reusable framework so I could quickly and effectively develop games. I also want it to be more complete, as opposed to barebones or boilerplate.
+I plan to eventually add functionality outside of just ECS such as loading tilesets, a camera system, etc. that are built into the framework to work seamlessly.
+
+Below are the specifications I have imagined for this framework and have been written out. They are subject to change as FelFlame is developed and used.
+
+# Specification
+
+## Aliases:
+```ruby
+FF = FelFlame
+FF::Ent = FelFlame::Entities
+FF::Cmp = FelFlame::Components
+FF::Sys = FelFlame::Systems
+FF::Sce = FelFlame::Scenes
+FF::Stg = FelFlame::Stage
+```
+## Classes:
+
+### FF::Entities
+```ruby
+FF::Ent.new(@component1, @component2)
+@entity = FF::Ent.get(entity_id)
+FF::Ent.delete(entity_id)
[email protected] # => unique entity id
+
[email protected] # => [:id, :scenes, :components]
+FF::Ent.load @entity_dump
+```
+
+### FF::Components
+```ruby
+FF::Cmp.new('Name', 'param1', param2: 'default')
+FF::Cmp::Name.new(param1: value1)
+@component = FF::Cmp::Name.get(component_id)
+FF::Cmp::Name.get_by_entity(entity_id) # gets array of components
[email protected](param2: 'not default')
[email protected] = 'different not default'
+FF::Cmp::Name.delete(component_id)
+FF::Cmp::Name.delete_by_entity(entity_id) # deletes all related components attached to entity
[email protected] # returns hash of all variables!(and the id)
+FF::Cmp::Name.load @component_dump
+
+FF::Cmp::Health.added # => returns values for sys to setup
+FF::Cmp::Health.removed # => returns values for sys to setup
+FF::Cmp::Health.is_set('var') # => returns values for sys to setup
+```
+
+### FF::Systems
+```ruby
+FF::Sys.new(name: 'Render', position: 5, frame: 1) do
+ @component.each do
+ # functionality
+ end
+end
+FF::Sys::Render.trigger_when FF::Cmp::Health.added
+FF::Sys::Render.trigger_when FF::Cmp::Health.removed
+FF::Sys::Render.trigger_when FF::Cmp::Health.is_set('var')
+```
+
+### FF::Scenes
+```ruby
+FF::Scn.new(name: 'Scene1', position: 1)
+FF::Scn::Scene1.add @entity
+FF::Scn::Scene1.add FF::Sys::Render
+FF::Scn::Scene1.entities # => [id_1, id_7, etc]
+FF::Scn::Scene1.systems # => [:Render, :Damage, etc]
+FF::Scn::Scene1.dump # => [:name, :entities, :systems]
+FF::Scn::Scene1.load @scene_dump
+```
+
+### FF::Stage
+```ruby
+FF::Stg.add FF::Scn::Scene1
+FF::Stg.remove FF::Scn::Scene1
+FF::Stg.scene # => [:Scene1, :Scene2, etc]
+FF::Stg.dump # => [:Scene1, :Scene2, etc]
+FF::Stg.load @stage_dump
+FF::Stg.clear
+```
+
+### FelFlame
+```ruby
+FF.dump # => all data
+FF.load @felflame_dump
+```
+---
+![blank](https://filestorage.catgirls.rodeo/images/whitespace.png)
+![blank](https://filestorage.catgirls.rodeo/images/whitespace.png)
+![blank](https://filestorage.catgirls.rodeo/images/whitespace.png)
+---
+# Ramblings:
+Was my originally written up specs. Rewrote it as what is written above to be more clear.
+The below are more verbose but not as helpful for me for implementation. Once the framework is
+complete I will use a more verbose explanation as below to help users of the framework.
+
+---
+
+Creating Entities:
+
+```ruby
+# Plain:
+@my_entity = FF:Ent.new
+
+# With components:
+FF::Ent.new(FF::Cmp::Position.new(var1: 'val', var3: 'change_default'),
+ FF::Cmp::Health.new(hp: 20),
+ FF::Cmp::Poison.new(dps: 3),
+ FF::Cmp::Poison.new(dps: 2), # This entity has 2 of the same component in it!
+ FF::Cmp::Selection.get(component_id)) # Components can belong to multiple entities, this component already existed elsewhere!
+```
+
+Adding and Removing Components from Entities:
+```ruby
+@my_entity = FF::Ent.get(entity_id)
+
+@my_entity.remove(component_id)
+# Remove the specific component with that id
+
+@my_entity.remove(FF::Cmp::Poison)
+# Removes all components of that type
+
+@my_entity.add(FF::Cmp::Selection.new)
+# Adds a new Component
+```
+
+Creating Component Class:
+
+```ruby
+# Creating a component 'factory':
+FF::Cmp.new('Name', 'var1', 'var2', var3: 'default') # Name, *no_default, **with_default
+```
+
+And then using those components:
+```ruby
+@new_cmp = FF::Cmp::Name.new(var1: 'oke') # var3 will be 'default', var2 will be nil
+@new_cmp.var2 = 3 # now var2 is set
+@new_cmp.set(var1: 'nope', var3: 'different') # var1 and var3 are now changed!
+@new_cmp.to_hash # returns the hash of all variables!
+```
+
+Referencing Components:
+```ruby
+FF::Cmp::Name.get(component_id) # gets component by their unique id
+FF::Cmp::Name.get_by_entity(entity_id) # gets component that is attached to the entitry
+# will return array of components if there is multiple of same component
+# if it returns array, see the `dump` section. Need to add custom method to the array
+```
+
+Creating Systems:
+```ruby
+FF::Sys.new(name: 'Render', position: 5, frame: 1) do
+# position is the order in which systems get executed, can be overlapping but then order is unexpected between same positions
+# frame is on which frame the system will be called. 0 is never, 1 is on each frame, 2 is every other frame, etc
+ FF::Cmp::Position.each do
+ #render your sprite
+ end
+end
+```
+
+Enabling Systems:
+```ruby
+# By default systems are not enabled. You need to add them to a scene
+FF::Scn::Scene1.add FF::Sys::Render
+
+# They can also be disabled by removing them
+FF::Scn::Scene1.remove FF::Sys::Render
+```
+
+Custom Hooks:
+```ruby
+# Systems can be configured to be called whenever a certain component is added, removed or set
+FF::Sys::Damage.trigger_when FF::Cmp::Health.added
+FF::Sys::Revive.trigger_when FF::Cmp::Health.removed
+FF::Sys::Healup.trigger_when FF::Cmp::Health.is_set
+
+# Systems can also be manually called so you can code custom hooks!
+FF::Sys::Restore.run
+```
+
+Scenes contain Entities and Systems
+```ruby
+FF::Scn.new(name: 'Scene1', position: 1)
+# Multiple scenes could be ran at once, if they do then they will run according
+# to their positions as explained above
+
+FF::Scn::Scene1.add FF::Entity.get(entity_id)
+# You can also just pass the id on its own => FF::Scn::Scene1.add entity_id
+FF::Scn::Scene1.add FF::Entity.new
+
+FF::Scn::Scene1.add FF::Sys::Render
+```
+
+To List Systems and Enties
+```ruby
+FF::Scn::Scene1.get_entities # => [id_1, id_7, etc]
+FF::Scn::Scene1.get_systems # => [:Render, :Damage, etc]
+```
+
+To run a Scene it must be added to the Stage
+```ruby
+FF::Stg.add FF::Scn::Scene1
+```
+
+Or remove it:
+```ruby
+FF::Stg.remove FF::Scn::Scene1
+```
+
+Show all Scenes on the Stage:
+```ruby
+FF::Stg.scenes # => [:Scene1, :Scene2, etc]
+```
+
+Remove all Scenes from the Stage:
+```ruby
+FF::Stg.clear
+```
+
+You can save the current game state:
+```ruby
+@json_save_data = FF.dump
+```
+
+You can also specifically choose what you want to save:
+```ruby
+@hash_entity = FF::Ent.get(entity_id).dump
+# to save all components related to a player
+
+@hash_component_single = FF::Cmp::Health.get(component_id).dump
+# save all data in the single component(needs to handle arrays of components)
+
+@hash_component = FF::Cmp::Health.dump
+# save all components of a certain component type
+
+@hash_components_all = FF::Cmp.dump
+# save all components
+
+@hash_scene = FF::Scn::Scene1.dump
+# save all entities, components, and activated systems in a given scene
+```
+
+And then they can be loaded back in:
+```ruby
+FF::Ent.load(@hash_entity)
+FF::Cmp::Health.load(@hash_component)
+FF::Cmp.load(@hash_component)
+FF::Cmp.load(@hash_components_all)
+FF::Scn.load(@Hash_scene)
+```
+
+### To Do List(old)
+
+---
+
+* [ ] Some level of hierarchical support?
+* [ ] One Component to many entities.
+* [ ] Multiple of same component to one entity
+* [ ] State Machine?
+* [ ] Systems execute code on an event
+ * [ ] Adding/removing/setting component is event
+ * [ ] Frame is an event
+* [ ] System Execution Order
+* [ ] Save Dump/Load
+
+
+
+
+
+
+
+
+
+
+
diff --git a/component_manager.rb b/component_manager.rb
new file mode 100644
index 0000000..f54c5f6
--- /dev/null
+++ b/component_manager.rb
@@ -0,0 +1,35 @@
+#require 'app/ECS/base_component.rb'
+
+#require 'app/ECS/components/00_test_component.rb'
+#require 'app/ECS/components/01_based.rb'
+
+class Components
+ class <<self
+ def entity_destroyed(entity_id)
+ constants.each do |component|
+ component.delete(entity_id) unless (component.id & Entity.signatures[entity_id]).zero?
+ end
+ end
+
+ def entity_created(entity_id)
+ constants.each do |component|
+ const_get(component.to_s).add(entity_id) unless (const_get(component.to_s).id & Entity.signatures[entity_id]).zero?
+ end
+ end
+
+ def new(component_name, *attrs, **attrs_with_defaults)
+ const_set(component_name, Class.new(Helper::BaseComponent) {})
+ attrs.each do |attr|
+ Components.const_get(component_name).attr_accessor attr
+ end
+ attrs_with_defaults.each do |attr, default|
+ Components.const_get(component_name).attr_writer attr
+ Components.const_get(component_name).define_method(attr) do
+ return default unless instance_variable_defined? "@#{attr}"
+
+ instance_variable_get "@#{attr}"
+ end
+ end
+ end
+ end
+end
diff --git a/components/00_renderable.rb b/components/00_renderable.rb
new file mode 100644
index 0000000..3971c8c
--- /dev/null
+++ b/components/00_renderable.rb
@@ -0,0 +1,16 @@
+class Components
+ # If an entity can be rendered on screen
+ class Renderable < Helper::BaseComponent
+ attr_accessor :z
+
+ def initialize
+ @z = 0
+ end
+
+ def set(**opts)
+ opts.each do |key, value|
+ self.send "#{key}=", value
+ end
+ end
+ end
+end
diff --git a/components/01_sprite.rb b/components/01_sprite.rb
new file mode 100644
index 0000000..8d30cdf
--- /dev/null
+++ b/components/01_sprite.rb
@@ -0,0 +1,44 @@
+class Components
+ # If an entity can be rendered on screen
+ class Sprite < Helper::BaseComponent
+
+ attr_accessor :x, :y, :w, :h, :path, :angle, :a, :r, :g, :b,
+ :source_x, :source_y, :source_w, :source_h,
+ :tile_x, :tile_y, :tile_w, :tile_h,
+ :flip_horizontally, :flip_vertically,
+ :angle_anchor_x, :angle_anchor_y
+
+ def set(x: @x, y: @y, w: @w, h: @h, path: @path, angle: @angle, a: @a, r: @r, g: @g, b: @b,
+ source_x: @source_x, source_y: @source_y, source_w: @source_w, source_h: @source_h,
+ tile_x: @tile_x, tile_y: @tile_y, tile_w: @tile_w, tile_h: @tile_h,
+ flip_horizontally: @flip_horizontally, flip_vertically: @flip_vertically,
+ angle_anchor_x: @angle_anchor_x, angle_anchor_y: @angle_anchor_y)
+ {x: @x = x,
+ y: @y = y,
+ w: @w = w,
+ h: @h = h,
+ path: @path = path,
+ angle: @angle = angle,
+ a: @a = a,
+ r: @r = r,
+ g: @g = g,
+ b: @b = b,
+ source_x: @source_x = source_x,
+ source_y: @source_y = source_y,
+ source_w: @source_w = source_w,
+ source_h: @source_h = source_h,
+ tile_x: @tile_x = tile_x,
+ tile_y: @tile_y = tile_y,
+ tile_w: @tile_w = tile_w,
+ tile_h: @tile_h = tile_h,
+ flip_horizontally: @flip_horizontally = flip_horizontally,
+ flip_vertically: @flip_vertically = flip_vertically,
+ angle_anchor_x: @angle_anchor_x = angle_anchor_x,
+ angle_anchor_y: @angle_anchor_y = angle_anchor_y}
+ end
+
+ def primative_marker
+ :sprite
+ end
+ end
+end
diff --git a/components/02_label.rb b/components/02_label.rb
new file mode 100644
index 0000000..13544b7
--- /dev/null
+++ b/components/02_label.rb
@@ -0,0 +1,27 @@
+class Components
+ # A dragonruby label wrapper
+ class Label < Helper::BaseComponent
+
+ attr_accessor :x, :y, :text, :size_enum, :alignment_enum,
+ :a, :r, :g, :b, :font, :vertical_alignment_enum
+
+ def set(x: @x, y: @y, text: @text, size_enum: @size_enum, alignment_enum: @alignment_enum,
+ a: @a, r: @r, g: @g, b: @b, font: @font, vertical_alignment_enum: @vertical_alignment_enum)
+ {x: @x = x,
+ y: @y = y,
+ text: @text = text,
+ size_enum: @size_enum = size_enum,
+ alignment_enum: @alignment_enum = alignment_enum,
+ r: @r = r,
+ g: @g = g,
+ b: @b = b,
+ a: @a = a,
+ font: @font = font,
+ vertical_alignment_enum: @vertical_alignment_enum = vertical_alignment_enum }
+ end
+
+ def primative_marker
+ :label
+ end
+ end
+end
diff --git a/components/03_player_control.rb b/components/03_player_control.rb
new file mode 100644
index 0000000..d5d8f7a
--- /dev/null
+++ b/components/03_player_control.rb
@@ -0,0 +1,21 @@
+class Components
+ # Gives control(keyboard or otherwise) over an object
+ class PlayerControl < Helper::BaseComponent
+ attr_accessor :north, :south, :east, :west, :interact, :menu
+
+ def initialize
+ @north = 'up'
+ @south = 'down'
+ @east = 'right'
+ @west = 'left'
+ @interact = 'space'
+ @menu = 'enter'
+ end
+
+ def set(**opts)
+ opts.each do |key, value|
+ send "#{key}=", value
+ end
+ end
+ end
+end
diff --git a/components/04_map.rb b/components/04_map.rb
new file mode 100644
index 0000000..7700e9f
--- /dev/null
+++ b/components/04_map.rb
@@ -0,0 +1,21 @@
+class Components
+ # dragonruby label wrapper
+ class Map < Helper::BaseComponent
+
+ attr_accessor :json_name, :json, :x, :y, :tilewidth, :tileheight, :a, :r, :g, :b
+
+ def set(json_name: @json_name, x: @x, y: @y, tilewidth: @tilewidth,
+ tileheight: @tileheight, a: @a, r: @r, g: @g, b: @b)
+ { json_name: @json_name = json_name,
+ json: @json = Helper.get_json_tiles(json_name),
+ x: @x = x,
+ y: @y = y,
+ tilewidth: @tilewidth = tilewidth,
+ tileheight: @tileheight = tileheight,
+ r: @r = r,
+ g: @g = g,
+ b: @b = b,
+ a: @a = a }
+ end
+ end
+end
diff --git a/components/05_interactable.rb b/components/05_interactable.rb
new file mode 100644
index 0000000..636a216
--- /dev/null
+++ b/components/05_interactable.rb
@@ -0,0 +1,16 @@
+class Components
+ # If an entity can be rendered on screen
+ class Interactable < Helper::BaseComponent
+ attr_accessor :z
+
+ def initialize
+ @z = z
+ end
+
+ def set(**opts)
+ opts.each do |key, value|
+ self.send "#{key}=", value
+ end
+ end
+ end
+end
diff --git a/components/06_collidable.rb b/components/06_collidable.rb
new file mode 100644
index 0000000..76ce51e
--- /dev/null
+++ b/components/06_collidable.rb
@@ -0,0 +1,22 @@
+class Components
+ # If an entity can be rendered on screen
+ class Collidable < Helper::BaseComponent
+ class <<self
+ def add(entity_id)
+ super(entity_id)
+ #add to grid?
+ end
+ end
+ attr_accessor :grid
+
+ def initialize
+ @grid = [[]]
+ end
+
+ def set(**opts)
+ opts.each do |key, value|
+ self.send "#{key}=", value
+ end
+ end
+ end
+end
diff --git a/components/07_battle.rb b/components/07_battle.rb
new file mode 100644
index 0000000..b4ef622
--- /dev/null
+++ b/components/07_battle.rb
@@ -0,0 +1,4 @@
+class Components
+ class Battle < Helper::Level
+ end
+end
diff --git a/components/07_indoor.rb b/components/07_indoor.rb
new file mode 100644
index 0000000..e409da8
--- /dev/null
+++ b/components/07_indoor.rb
@@ -0,0 +1,4 @@
+class Components
+ class Indoor < Helper::Level
+ end
+end
diff --git a/components/07_overworld.rb b/components/07_overworld.rb
new file mode 100644
index 0000000..55ab38a
--- /dev/null
+++ b/components/07_overworld.rb
@@ -0,0 +1,16 @@
+class Components
+ class Overworld < Helper::Level
+ attr_accessor :x, :y
+
+ def initialize
+ @x = 0
+ @y = 0
+ end
+
+ def set(**opts)
+ opts.each do |key, value|
+ self.send "#{key}=", value
+ end
+ end
+ end
+end
diff --git a/components/debug_singleton.rb b/components/debug_singleton.rb
new file mode 100644
index 0000000..f298172
--- /dev/null
+++ b/components/debug_singleton.rb
@@ -0,0 +1,13 @@
+class Components
+ # If an entity can be rendered on screen
+ class DebugSingleton
+ class <<self
+ @data = false
+ attr_accessor :data
+
+ def id
+ 0
+ end
+ end
+ end
+end
diff --git a/entity_manager.rb b/entity_manager.rb
new file mode 100644
index 0000000..7d0d1ff
--- /dev/null
+++ b/entity_manager.rb
@@ -0,0 +1,63 @@
+class Entity
+ attr_accessor :id
+
+ def initialize(*signature)
+ final_signature = 0
+ signature.each do |sig|
+ final_signature += sig
+ end
+ @id = Entity.generate_new_id
+ self.class.all.push self
+ self.class.signatures.push final_signature
+ Components.entity_created(@id)
+ end
+
+ class <<self
+ # All entities that exist
+ def all
+ @all ||= []
+ end
+
+ def id_queue
+ @id_queue ||= []
+ end
+
+ def generate_new_id
+ if id_queue.empty?
+ all.size
+ else
+ id_queue.shift
+ end
+ end
+
+ # What components a given entity uses
+ def signatures
+ @signatures ||= []
+ end
+
+ def destroy_entity(entity_id)
+ if all[entity_id].nil?
+ puts 'Entity can not be destroyed, id out of bounds'
+ elsif entity_id < all.size - 1
+ Components.constants.each do |constant|
+ unless (signatures[entity_id] & Components::const_get(constant).id).zero?
+ Components::const_get(constant).delete(entity_id)
+ end
+ end
+ all[entity_id] = nil
+ signatures[entity_id] = nil
+ id_queue.push entity_id
+ elsif entity_id == all.size - 1
+ Components.constants.each do |constant|
+ unless (signatures[entity_id] & Components::const_get(constant).id).zero?
+ Components::const_get(constant).delete(entity_id)
+ end
+ end
+ all.pop
+ signatures.pop
+ else
+ puts 'Unknown error with destroy_entity, entity not destroyed'
+ end
+ end
+ end
+end
diff --git a/helpers/00_tileset.rb b/helpers/00_tileset.rb
new file mode 100644
index 0000000..3890550
--- /dev/null
+++ b/helpers/00_tileset.rb
@@ -0,0 +1,49 @@
+class Helper
+ # Returns a loaded map and its dependecies(images,json)
+ # If any are missing then it will load them from files
+
+ @json_data = {}
+ class <<self
+ attr_accessor :json_data
+
+ def get_json_tiles(json_name, hitbox: false)
+ unless hitbox
+ return nil if json_name == 'hitbox' && !Components::DebugSingleton.data
+ end
+
+ if self.json_data[json_name].nil?
+ self.json_data[json_name] = $gtk.parse_json_file "assets/json/#{json_name}.json"
+ raise Exception.new "#{json_name} is null and not loaded. Cannot get json tile" if self.json_data[json_name].nil?
+
+ if self.json_data[json_name]['type'] == 'map' #json_name.split("_").first == 'map'
+ self.json_data[json_name]['tilesets'].each do |tileset|
+ tileset = Helper.get_json_tiles(tileset['source'].split('/').last.delete_suffix('.tsx'))
+ # download tileset here
+ # $gtk.args.gtk.http_get 'https://mysite.net/#{tileset['name']}.png'
+ end
+ end
+ end
+ self.json_data[json_name]
+ end
+
+ def get_tile(json_name:, tile_index:)
+ if json_name == 'hitbox' && !Components::DebugSingleton.data
+ return tile_index - 1 if tile_index > 1
+ return {}
+ end
+
+ json_tiles = self.get_json_tiles(json_name)
+ raise Exception.new "Error, json file not a tileset" unless json_tiles['type'] == 'tileset'
+ return tile_index - json_tiles['tilecount'] if tile_index > json_tiles['tilecount']
+ source_height_tiles = (tile_index.to_i / json_tiles['columns'].to_i).to_i# * json_tiles['tileheight']
+ { w: json_tiles['tilewidth'],
+ h: json_tiles['tileheight'],
+ path: json_tiles['image'].split('mygame/').last.delete('\\'),
+ source_x: [((tile_index % json_tiles['columns']) - 1) * json_tiles['tilewidth'], 0].max,
+ # source_y gets special treatment
+ source_y: [json_tiles['imageheight'] - ((source_height_tiles + 1) * json_tiles['tileheight']), 0].max,
+ source_w: json_tiles['tilewidth'],
+ source_h: json_tiles['tileheight'] }
+ end
+ end
+end
diff --git a/helpers/01_component.rb b/helpers/01_component.rb
new file mode 100644
index 0000000..ca4f86d
--- /dev/null
+++ b/helpers/01_component.rb
@@ -0,0 +1,102 @@
+class Helper
+ class BaseComponent
+ class <<self
+ def id
+ @id ||= ID.create_new_id Helper::ComponentHelper.underscore(ancestors[0].name.split('::').last)
+ #@id ||= ID.send(Helper::ComponentHelper.underscore(ancestors[0].name.split('::').last))
+ end
+
+ def data
+ @data ||= {}
+ end
+
+ def add(entity_id)
+ data[entity_id] = new
+ end
+
+ def delete(entity_id)
+ data.delete entity_id
+ end
+ end
+
+ def self.inherited(subclass) #TODO this should automagically create the ID(handle it with ID)
+ #@id = ID.send(subclass)
+ # maybe overwrite this?
+ end
+
+ def set(**opts)
+ opts.each do |key, value|
+ send "#{key}=", value
+ end
+ end
+
+ def create_data(name, default = nil)
+ #TODO fill this out
+ end
+
+ def get #TODO maybe optimize removeing the @ symbol | doesnt get defaults
+ instance_variables.each_with_object({}) do |key, final|
+ final[key.to_s.delete_prefix('@').to_sym] = instance_variable_get(key)
+ end
+ end
+
+ def dump #TODO already does what get does?
+ # should return a json or hash of all data in this component
+ end
+ end
+
+ class Level < Helper::BaseComponent
+ class <<self
+ def data
+ @data ||= { add: [], remove: [], grid: Helper::Array2D.new }
+ end
+
+ def add(entity_id)
+ super
+ data[:add].push entity_id
+ end
+
+ def remove(entity_id)
+ data[:remove].push entity_id
+ super
+ end
+ end
+ end
+
+ class Array2D < Array
+ def [](val)
+ unless val.nil?
+ return self[val] = [] if super.nil?
+ end
+ super
+ end
+ end
+
+
+ module ComponentHelper
+ class <<self
+ def up? char
+ char == char.upcase
+ end
+
+ def down? char
+ char == char.downcase
+ end
+
+ def underscore(input)
+ output = input[0].downcase
+ (1...(input.length - 1)).each do |iter|
+ if down?(input[iter]) && up?(input[iter + 1])
+ output += "#{input[iter].downcase}_"
+ elsif up?(input[iter - 1]) && up?(input[iter]) && down?(input[iter + 1])
+ output += "_#{input[iter].downcase}"
+ else
+ output += input[iter].downcase
+ end
+ end
+ output += input[-1].downcase unless input.length == 1
+ output
+ end
+ end
+ end
+end
diff --git a/logos/felflame-logo-text.png b/logos/felflame-logo-text.png
new file mode 100644
index 0000000..a274c1f
--- /dev/null
+++ b/logos/felflame-logo-text.png
Binary files differ
diff --git a/logos/felflame-logo-text.svg b/logos/felflame-logo-text.svg
new file mode 100644
index 0000000..b4dac61
--- /dev/null
+++ b/logos/felflame-logo-text.svg
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="408.86401mm"
+ height="179.90401mm"
+ viewBox="0 0 408.86401 179.90401"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.2-dev (25cba68356, 2021-05-16, custom)"
+ sodipodi:docname="felflame-logo-text.svg"
+ inkscape:export-filename="/home/tradam/Documents/felflame-logo-text.png"
+ inkscape:export-xdpi="67.093208"
+ inkscape:export-ydpi="67.093208"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#505050"
+ bordercolor="#eeeeee"
+ borderopacity="1"
+ inkscape:pageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="mm"
+ showgrid="false"
+ showborder="true"
+ inkscape:object-paths="true"
+ inkscape:snap-smooth-nodes="false"
+ inkscape:zoom="0.5"
+ inkscape:cx="885"
+ inkscape:cy="100"
+ inkscape:window-width="1670"
+ inkscape:window-height="1029"
+ inkscape:window-x="670"
+ inkscape:window-y="1425"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g7470"
+ fit-margin-top="15"
+ fit-margin-left="15"
+ fit-margin-right="0"
+ fit-margin-bottom="15"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-global="true" />
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-5.925697,16.359175)">
+ <g
+ id="g5942"
+ transform="matrix(0.7150444,0,0,0.7150444,7.9079079,19.106173)"
+ style="fill:#005522" />
+ <g
+ id="g7470"
+ transform="translate(45.740503,25.047998)">
+ <ellipse
+ style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1c241c;fill-opacity:0.7;fill-rule:evenodd;stroke:none;stroke-width:1.24602;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000;stop-opacity:1"
+ id="path6921"
+ ry="11.676038"
+ rx="54.259499"
+ cy="111.82079"
+ cx="29.444693" />
+ <g
+ id="g5900"
+ transform="matrix(0.94670406,0,0,0.94670406,-1.3225283,-1.4073951)">
+ <path
+ id="path846"
+ style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#00ff66;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4.79999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000;stop-opacity:1"
+ d="m 48.043994,-26.407173 c -35.640626,21.8403454 -59.4465,56.770237 -59.4465,101.334381 3e-5,44.723632 35.927436,44.724922 44.724917,44.724922 8.797481,0 44.72492,-0.0806 44.72492,-44.724922 0,-43.365499 -48.544546,-56.571942 -30.003337,-101.334381 z"
+ sodipodi:nodetypes="cczcc" />
+ <path
+ id="path4245"
+ style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#00d455;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:18.1417;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000;stop-opacity:1"
+ d="M 181.58398,-99.806641 C 46.879256,-17.260453 -43.095703,114.75804 -43.095703,283.18945 c 1.13e-4,169.0342 135.78874,169.03907 169.039063,169.03907 3.33332,0 7.73446,-0.012 12.88867,-0.1875 -46.385395,-1.56991 -155.927631,-17.00003 -155.927733,-168.85157 0,-153.77238 79.303367,-290.0927365 194.863233,-373.226336 3.81607,-9.769753 0,0 3.81645,-9.769755 z"
+ transform="scale(0.26458333)"
+ sodipodi:nodetypes="ccscccc" />
+ </g>
+ <g
+ id="g5904"
+ transform="matrix(0.94111257,0,0,0.94111257,3.0800803,7.5916104)">
+ <path
+ id="path846-3"
+ style="font-variation-settings:normal;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.61297;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000"
+ d="M 52.149066,-3.2818086 C 70.471317,22.295981 76.75521,53.486612 63.710707,84.389882 56.651708,101.11305 36.733365,109.54332 19.604384,102.31307 2.4754112,95.082796 -5.5490668,75.335735 1.6811862,58.206753 14.374829,28.134691 51.904029,33.186223 52.149066,-3.2818086 Z"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ id="path4266"
+ style="font-variation-settings:normal;vector-effect:none;fill:#1c241c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:13.6553;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000"
+ d="M 197.09961,-12.404297 C 196.17349,125.42763 54.329488,106.33595 6.3535156,219.99414 -20.973425,284.7336 9.3562784,359.36829 74.095703,386.69531 c 20.124509,8.49468 41.266977,11.26604 61.652347,9.15821 -11.99594,-1.24491 -23.99476,-4.23747 -35.65235,-9.15821 C 35.356265,359.36829 5.0265759,284.7336 32.353516,219.99414 75.872098,116.89584 196.62125,123.02178 219.375,22.177734 212.5918,10.387797 205.1587,-1.1538421 197.09961,-12.404297 Z"
+ transform="scale(0.26458333)" />
+ </g>
+ <g
+ id="g10504"
+ transform="translate(-67.885213,43.329639)">
+ <g
+ id="g10530">
+ <text
+ xml:space="preserve"
+ style="font-size:70.5556px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583"
+ id="text16309"><textPath
+ xlink:href="#path8946"
+ startOffset="50%"
+ id="textPath16307"><tspan
+ id="tspan16305"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle;stroke-width:0.264583"><tspan
+ style="fill:#00ff66;fill-opacity:1"
+ id="tspan16301">F</tspan>el<tspan
+ style="fill:#00ff66;fill-opacity:1"
+ id="tspan16303">F</tspan>lame</tspan></textPath></text>
+ <g
+ aria-label="FelFlame"
+ id="text2900"
+ style="font-size:70.5556px;line-height:1.25;letter-spacing:0px;stroke-width:0.264583">
+ <path
+ d="m 163.3819,-24.820664 q -3.75224,0.869143 -7.23224,2.237747 -3.41434,1.342781 -5.72646,2.403719 l 2.96961,7.55094 q 3.66682,9.3237686 6.3184,12.98154649 l 0.18076,0.4596224 -8.86415,3.48606591 -0.18076,-0.4596224 q -0.55044,-4.48410465 -4.19144,-13.7422131 l -7.20454,-18.3192363 q -3.641,-9.258108 -6.29258,-12.915886 l -0.18076,-0.459622 26.51498,-10.655179 0.32101,5.635754 -0.45962,0.180759 q -4.54976,0.576265 -9.93391,2.693727 -5.38415,2.117462 -6.84269,2.842705 0.51204,2.073102 6.2705,16.715358 l 15.23541,-6.370816 -0.2419,5.553872 z"
+ style="fill:#00ff66"
+ id="path19193" />
+ <path
+ d="m 197.32692,-10.137557 q -7.20751,1.7132745 -12.31804,-1.785819 -5.11053,-3.499094 -6.84012,-10.775244 -1.74591,-7.344794 1.47917,-13.913144 3.22508,-6.568349 9.95209,-8.167405 12.28709,-2.920725 15.89312,12.249363 l -1.95238,3.074871 -18.25902,4.340294 q 1.37062,5.766006 4.52708,8.061601 3.15645,2.295595 7.34367,1.300264 1.64743,-0.391605 4.43928,-2.07055 2.77554,-1.747587 4.78306,-3.67522 l 2.33611,2.200509 q -0.99419,1.614236 -4.81625,5.06102 -3.75341,3.430468 -6.56777,4.09946 z m -8.14663,-30.915762 q -2.883,0.68531 -4.15177,2.364814 -0.87997,2.094738 -0.98237,5.019941 -0.10239,2.925203 0.38711,4.984491 l 13.94127,-3.894107 q -2.39859,-10.090511 -9.19424,-8.475139 z"
+ style="font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle"
+ id="path19195" />
+ <path
+ d="m 227.39129,-16.019152 -9.03046,1.136292 -0.1057,-0.840043 q 0.74964,-3.649924 -0.20168,-11.210313 l -3.67313,-29.191501 q -0.70468,-5.600288 -4.31241,-8.275257 l -0.10571,-0.840043 9.37909,-2.886846 0.50811,0.647185 q 0.0454,2.05653 1.48121,13.467117 l 3.37318,26.242541 q 0.95132,7.560389 2.5818,10.910825 z"
+ style="font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle"
+ id="path19197" />
+ <path
+ d="m 260.90643,-39.860583 q -3.81425,-0.534992 -7.55358,-0.506101 -3.66878,0.02835 -6.20762,0.189076 l 0.0627,8.113652 q 0.0774,10.018597 1.24005,14.384192 l 0.004,0.493875 -9.52472,0.07359 -0.004,-0.493875 q 1.09506,-4.383038 1.0182,-14.331081 l -0.15208,-19.684426 q -0.0769,-9.948043 -1.23952,-14.313638 l -0.004,-0.493875 28.57253,-0.432428 -1.72241,5.375693 -0.49387,0.0038 q -4.4536,-1.094514 -10.23898,-1.049816 -5.78539,0.0447 -7.40703,0.198344 -0.26586,2.118785 -0.1443,17.852215 l 16.50679,-0.480323 -2.21846,5.097296 z"
+ style="fill:#00ff66"
+ id="path19199" />
+ <path
+ d="m 283.31547,-15.436956 -9.06607,-0.804226 0.0748,-0.843356 q 1.50647,-3.407998 2.17977,-10.998198 l 2.59971,-29.306606 q 0.49874,-5.622371 -2.45981,-9.001453 l 0.0748,-0.843355 9.77793,-0.832611 0.35934,0.740202 q -0.39164,2.019406 -1.40783,13.474986 l -2.26758,26.361096 q -0.67331,7.5902 0.20973,11.210165 z"
+ style="font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle"
+ id="path19201" />
+ <path
+ d="m 312.35784,-25.845813 q -2.70925,-1.465672 -6.47918,-2.016827 -3.70011,-0.540949 -5.54915,0.400922 -1.28195,1.452611 -1.68001,4.175335 -0.38785,2.652912 0.85369,4.403146 1.24154,1.750234 3.40576,2.066638 2.23403,0.326611 4.70118,-0.453587 2.47735,-0.850012 3.78829,-2.013162 z m -0.35273,10.216443 q -4.94246,4.0549 -10.3181,3.268993 -5.30582,-0.7757 -8.0408,-3.528633 -2.66516,-2.742727 -1.92008,-7.83911 0.75529,-5.166196 4.64239,-6.879691 3.95691,-1.703288 10.51937,-0.74387 l 6.11297,1.10762 0.58177,-3.979367 q 0.54095,-3.700114 -0.93738,-6.269329 -1.47834,-2.569215 -5.17845,-3.110164 -2.73293,-0.328243 -9.49217,3.960187 -1.64695,-2.879088 -1.88252,-7.120561 7.23814,-1.22358 13.03266,-0.376434 5.79451,0.847147 8.8463,4.359454 3.05179,3.512308 2.24547,9.027571 l -1.7147,11.728661 q -0.38949,3.151812 0.12003,6.007222 0.58954,2.795804 1.97234,4.06755 l 0.0376,0.718548 -7.73541,0.723046 q -0.58097,-2.366716 -0.89125,-5.121693 z"
+ style="font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle"
+ id="path19203" />
+ <path
+ d="m 361.21774,-25.21257 -2.67715,12.486799 q -1.18328,5.5190273 -0.93238,9.3972464 l -0.10353,0.4829149 -8.5545,-1.8340723 0.10354,-0.4829148 q 1.81874,-3.434492 3.00201,-8.9535192 l 2.36654,-11.038055 q 0.84308,-3.932307 0.19283,-5.947854 -0.63546,-2.084535 -3.73992,-2.750126 -3.10445,-0.665591 -9.10186,1.72868 l -2.79548,13.038702 q -1.43472,6.691821 -0.41027,11.6739545 l -0.10354,0.4829149 -9.6583,-2.0707268 0.10354,-0.4829149 q 1.91234,-3.1979467 3.13998,-8.9239377 l 2.51446,-11.727933 q 0.63601,-2.966477 6.5e-4,-6.061215 -0.55157,-3.148934 -1.93122,-4.454955 l 0.1085,-0.842645 9.49593,-0.201014 0.26602,0.778623 q 0.005,2.670878 -0.28147,5.351579 2.94208,-1.60615 4.15439,-2.212141 1.29609,-0.660187 3.43482,-1.211872 2.20772,-0.536893 3.93242,-0.16712 7.10574,1.523463 7.85899,8.107108 8.42214,-4.616455 12.42343,-3.758582 4.82915,1.035363 6.8393,4.785649 2.01014,3.750286 0.73812,9.683241 l -2.12989,9.934249 q -1.40513,6.5538446 -0.34128,11.688745 l -0.10354,0.4829149 -9.72728,-2.08551772 0.10353,-0.48291489 q 1.91234,-3.19794669 3.13999,-8.92393749 l 2.26301,-10.5551398 q 1.73053,-8.071577 -3.30558,-9.151313 -4.20826,-0.902246 -9.87552,1.634968 -0.21701,1.68529 -0.40929,2.582132 z"
+ style="font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle"
+ id="path19205" />
+ <path
+ d="m 400.76163,8.5291819 q -7.16225,-1.8935634 -10.01455,-7.3913425 -2.8523,-5.4977791 -0.9407,-12.7282454 1.92963,-7.298679 7.87433,-11.565395 5.94471,-4.266717 12.62948,-2.499391 12.20994,3.228075 8.22444,18.3029161 l -3.17307,1.7883745 -18.14438,-4.7970276 q -1.51485,5.7298042 0.18336,9.2439279 1.6982,3.5141237 5.85913,4.6141938 1.63709,0.4328145 4.89089,0.2713404 3.27183,-0.229686 5.95172,-0.9807711 l 1.02001,3.0429025 q -1.63867,0.9533823 -6.63543,2.1866286 -4.92854,1.2512802 -7.72523,0.5118888 z m 7.41904,-31.0983869 q -2.8649,-0.757425 -4.77642,0.123821 -1.76491,1.430866 -3.2366,3.960971 -1.47168,2.530104 -2.0127,4.576463 l 14.12772,3.151261 q 2.65099,-10.027156 -4.102,-11.812516 z"
+ style="font-size:70.5556px;font-family:Asul;-inkscape-font-specification:Asul;text-align:center;text-anchor:middle"
+ id="path19207" />
+ </g>
+ <path
+ id="path838"
+ style="font-size:70.5556px;line-height:1.25;letter-spacing:0px;fill:#00d455;fill-opacity:1;stroke-width:0.264583"
+ d="m 159.49288,-52.253005 -26.51518,10.655162 0.18087,0.45992 c 1.76772,2.438519 3.86531,6.743935 6.29264,12.916008 l 7.20421,18.3192947 c 2.42734,6.1720721 3.82452,10.75239313 4.19148,13.7417959 l 0.18086,0.4599199 1.14567,-0.4506171 c -0.58682,-2.93751539 -1.89545,-6.988974 -3.93051,-12.1635995 l -7.20421,-18.3192939 c -2.42733,-6.172073 -4.52492,-10.477489 -6.29264,-12.916008 l -0.18087,-0.45992 25.0517,-10.067086 z m 4.59042,21.697899 -15.23529,6.370627 1.58754,1.587545 13.54801,-5.665288 z"
+ sodipodi:nodetypes="ccccccccccccccccccc" />
+ <path
+ style="fill:none;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 125.46431,18.025536 c 0,0 52.56445,-35.227404 125.89097,-35.227404 73.32652,0 185.57913,35.227404 185.57913,35.227404"
+ id="path8946"
+ sodipodi:nodetypes="czc" />
+ <path
+ id="path844"
+ style="font-size:70.5556px;line-height:1.25;letter-spacing:0px;fill:#00d455;fill-opacity:1;stroke-width:0.264583"
+ d="m 267.11835,-66.861416 -28.57241,0.432014 0.004,0.494027 c 0.77508,2.910397 1.18793,7.681826 1.2392,14.313856 l 0.15245,19.684068 c 0.0512,6.63203 -0.28851,11.4094 -1.01855,14.331426 l 0.004,0.493509 1.82417,-0.01395 c 0.56344,-2.963405 0.82296,-7.369797 0.77774,-13.223484 l -0.15245,-19.684068 c -0.0513,-6.63203 -0.46412,-11.403459 -1.2392,-14.313856 l -0.004,-0.494027 26.46608,-0.399975 z m -3.49953,21.899954 -16.50702,0.480076 c 0.004,0.548283 1.58267,1.01188 1.58757,1.587497 l 14.19913,-0.412893 z"
+ sodipodi:nodetypes="ccccccccccccccccccc" />
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/logos/felflame-logo.png b/logos/felflame-logo.png
new file mode 100644
index 0000000..a3d08a4
--- /dev/null
+++ b/logos/felflame-logo.png
Binary files differ
diff --git a/logos/felflame-logo.svg b/logos/felflame-logo.svg
new file mode 100644
index 0000000..8990883
--- /dev/null
+++ b/logos/felflame-logo.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="200mm"
+ height="200mm"
+ viewBox="0 0 200 200"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.2-dev (25cba68356, 2021-05-16, custom)"
+ sodipodi:docname="felflame-logo.svg"
+ inkscape:export-filename="/home/tradam/Documents/felflame-logo.png"
+ inkscape:export-xdpi="88.900002"
+ inkscape:export-ydpi="88.900002"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#505050"
+ bordercolor="#eeeeee"
+ borderopacity="1"
+ inkscape:pageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="mm"
+ showgrid="false"
+ showborder="true"
+ inkscape:object-paths="true"
+ inkscape:snap-smooth-nodes="false"
+ inkscape:zoom="1"
+ inkscape:cx="245.5"
+ inkscape:cy="317.5"
+ inkscape:window-width="1682"
+ inkscape:window-height="1078"
+ inkscape:window-x="338"
+ inkscape:window-y="1518"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g7470"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(24.814804,26.407173)">
+ <g
+ id="g5942"
+ transform="matrix(0.7150444,0,0,0.7150444,7.9079079,19.106173)"
+ style="fill:#005522" />
+ <g
+ id="g7470"
+ transform="translate(45.740503,25.047998)">
+ <ellipse
+ style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1c241c;fill-opacity:0.69999999;fill-rule:evenodd;stroke:none;stroke-width:1.24602;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000;stop-opacity:1"
+ id="path6921"
+ ry="11.676038"
+ rx="54.259499"
+ cy="111.82079"
+ cx="29.444693" />
+ <g
+ id="g5900"
+ transform="matrix(0.94670406,0,0,0.94670406,-1.3225283,-1.4073951)">
+ <path
+ id="path846"
+ style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#00ff66;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4.79999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000;stop-opacity:1"
+ d="m 48.043994,-26.407173 c -35.640626,21.8403454 -59.4465,56.770237 -59.4465,101.334381 3e-5,44.723632 35.927436,44.724922 44.724917,44.724922 8.797481,0 44.72492,-0.0806 44.72492,-44.724922 0,-43.365499 -48.544546,-56.571942 -30.003337,-101.334381 z"
+ sodipodi:nodetypes="cczcc" />
+ <path
+ id="path4245"
+ style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#00d455;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:18.1417;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000;stop-opacity:1"
+ d="M 181.58398,-99.806641 C 46.879256,-17.260453 -43.095703,114.75804 -43.095703,283.18945 c 1.13e-4,169.0342 135.78874,169.03907 169.039063,169.03907 3.33332,0 7.73446,-0.012 12.88867,-0.1875 -46.385395,-1.56991 -155.927631,-17.00003 -155.927733,-168.85157 0,-153.77238 79.303367,-290.0927365 194.863233,-373.226336 3.81607,-9.769753 0,0 3.81645,-9.769755 z"
+ transform="scale(0.26458333)"
+ sodipodi:nodetypes="ccscccc" />
+ </g>
+ <g
+ id="g5904"
+ transform="matrix(0.94111257,0,0,0.94111257,3.0800803,7.5916104)">
+ <path
+ id="path846-3"
+ style="font-variation-settings:normal;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.61297;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000"
+ d="M 52.149066,-3.2818086 C 70.471317,22.295981 76.75521,53.486612 63.710707,84.389882 56.651708,101.11305 36.733365,109.54332 19.604384,102.31307 2.4754112,95.082796 -5.5490668,75.335735 1.6811862,58.206753 14.374829,28.134691 51.904029,33.186223 52.149066,-3.2818086 Z"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ id="path4266"
+ style="font-variation-settings:normal;vector-effect:none;fill:#1c241c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:13.6553;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:7.2;stroke-dasharray:none;stroke-dashoffset:3.8766;stroke-opacity:1;paint-order:normal;stop-color:#000000"
+ d="M 197.09961,-12.404297 C 196.17349,125.42763 54.329488,106.33595 6.3535156,219.99414 -20.973425,284.7336 9.3562784,359.36829 74.095703,386.69531 c 20.124509,8.49468 41.266977,11.26604 61.652347,9.15821 -11.99594,-1.24491 -23.99476,-4.23747 -35.65235,-9.15821 C 35.356265,359.36829 5.0265759,284.7336 32.353516,219.99414 75.872098,116.89584 196.62125,123.02178 219.375,22.177734 212.5918,10.387797 205.1587,-1.1538421 197.09961,-12.404297 Z"
+ transform="scale(0.26458333)" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/signatures.rb b/signatures.rb
new file mode 100644
index 0000000..c11c048
--- /dev/null
+++ b/signatures.rb
@@ -0,0 +1,54 @@
+class ID
+ class <<self
+ @next_id = 0b0_010_000_000_000
+
+ def create_new_id(name)
+ temp = @next_id
+ @next_id *= 2
+ define_singleton_method(name) do
+ temp
+ end
+ send(name)
+ end
+
+ def renderable
+ 0b0_001
+ end
+
+ def sprite
+ 0b0_010
+ end
+
+ def label
+ 0b0_100
+ end
+
+ def player_control
+ 0b0_001_000
+ end
+
+ def map
+ 0b0_010_000
+ end
+
+ def interactable
+ 0b0_100_000
+ end
+
+ def collidable
+ 0b0_001_000_000
+ end
+
+ def overworld
+ 0b0_010_000_000
+ end
+
+ def indoor
+ 0b0_100_000_000
+ end
+
+ def battle
+ 0b0_001_000_000_000
+ end
+ end
+end
diff --git a/system_manager.rb b/system_manager.rb
new file mode 100644
index 0000000..e63375c
--- /dev/null
+++ b/system_manager.rb
@@ -0,0 +1,5 @@
+#require 'app/ECS/systems/00_movement.rb'
+#require 'app/ECS/systems/01_flying.rb'
+
+class Systems
+end
diff --git a/systems/00_update_levels.rb b/systems/00_update_levels.rb
new file mode 100644
index 0000000..6f3a056
--- /dev/null
+++ b/systems/00_update_levels.rb
@@ -0,0 +1,34 @@
+class Systems
+ class UpdateLevels
+ @co = Components::Overworld
+ def self.run
+ @co.data[:add].each do |id|
+ @co.data[:add].delete(id)
+ if !(Components::Sprite.id & Entity.signatures[id]).zero?
+ @co.data[:grid][@co.data[id].x][@co.data[id].y] = {} if @co.data[:grid][@co.data[id].x][@co.data[id].y].nil?
+ #@co.data[:grid][@co.data[id].x][@co.data[id].y].merge!({ player: true })
+ puts @co.data[:grid][@co.data[id].x][@co.data[id].y].inspect
+ elsif !(Components::Map.id & Entity.signatures[id]).zero?
+ if Components::Map.data[id].json['tilesets'].last['source'].split('/').last.delete('\\').delete_suffix('.tsx') == 'hitbox'
+ Components::Map.data[id].json['layers'].each do |layer|
+ layer['chunks'].each do |chunk|
+ chunk['data'].each_slice(chunk['width']).with_index do |row, row_index|
+ row.each_with_index do |tile, column_index|
+ if tile.to_i == Components::Map.data[id].json['tilesets'].last['firstgid'].to_i
+ @co.data[:grid][column_index][row_index] = {} if @co.data[:grid][column_index][row_index].nil?
+ @co.data[:grid][column_index][row_index].merge!({ hitbox: true })
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ puts @co.data[:grid]
+ end
+ Components::Overworld.data[:remove].each do |id|
+ Components::Overworld.data[:remove].delete(id)
+ end
+ end
+ end
+end
diff --git a/systems/10_player.rb b/systems/10_player.rb
new file mode 100644
index 0000000..307731a
--- /dev/null
+++ b/systems/10_player.rb
@@ -0,0 +1,41 @@
+class Systems
+ class Player
+ @co = Components::Overworld
+ def self.run
+ Components::PlayerControl.data.each do |id, data|
+ puts6 "Right: #{@co.data[:grid][@co.data[id].x+1][@co.data[id].y]}"
+ puts6 "Left #{@co.data[:grid][@co.data[id].x-1][@co.data[id].y]}"
+ puts6 "Down #{@co.data[:grid][@co.data[id].x][@co.data[id].y+1]}"
+ puts6 "Up #{@co.data[:grid][@co.data[id].x][@co.data[id].y-1]}"
+ #puts6 @co.data[:grid][@co.data[id].x + 1][@co.data[id].y][:hitbox].nil?
+
+ if !(Components::Sprite.id & Entity.signatures[id]).zero?
+ if $gtk.args.inputs.keyboard.key_down.send(data.north) &&\
+ (@co.data[:grid][@co.data[id].x][@co.data[id].y - 1].nil? ||\
+ @co.data[:grid][@co.data[id].x][@co.data[id].y - 1][:hitbox].nil?)
+ Components::Sprite.data[id].y -= 64
+ @co.data[id].y -= 1
+ elsif $gtk.args.inputs.keyboard.key_down.send(data.south) &&\
+ (@co.data[:grid][@co.data[id].x][@co.data[id].y + 1].nil? ||\
+ @co.data[:grid][@co.data[id].x][@co.data[id].y + 1][:hitbox].nil?)
+ Components::Sprite.data[id].y += 64
+ @co.data[id].y += 1
+ elsif $gtk.args.inputs.keyboard.key_down.send(data.east) &&\
+ (@co.data[:grid][@co.data[id].x + 1][@co.data[id].y].nil? ||\
+ @co.data[:grid][@co.data[id].x + 1][@co.data[id].y][:hitbox].nil?)
+ Components::Sprite.data[id].x += 64
+ @co.data[id].x += 1
+ elsif $gtk.args.inputs.keyboard.key_down.send(data.west) &&\
+ (@co.data[:grid][@co.data[id].x - 1][@co.data[id].y].nil? || @co.data[:grid][@co.data[id].x - 1][@co.data[id].y][:hitbox].nil?)
+ Components::Sprite.data[id].x -= 64
+ @co.data[id].x -= 1
+ end
+ #Components::Sprite.data[id].y -= 64 if $gtk.args.inputs.keyboard.key_down.send(data.north)
+ #Components::Sprite.data[id].y += 64 if $gtk.args.inputs.keyboard.key_down.send(data.south)
+ #Components::Sprite.data[id].x += 64 if $gtk.args.inputs.keyboard.key_down.send(data.east)
+ #Components::Sprite.data[id].x -= 64 if $gtk.args.inputs.keyboard.key_down.send(data.west)
+ end
+ end
+ end
+ end
+end
diff --git a/systems/99_render.rb b/systems/99_render.rb
new file mode 100644
index 0000000..c71c6fc
--- /dev/null
+++ b/systems/99_render.rb
@@ -0,0 +1,37 @@
+class Systems
+ class Render
+ def self.run
+ Components::Renderable.data.sort_by { |v| v[1].z }.each do |key, data|
+ if !(Components::Sprite.id & Entity.signatures[key]).zero?
+ $gtk.args.outputs.sprites << Components::Sprite.data[key].set
+ elsif !(Components::Label.id & Entity.signatures[key]).zero?
+ $gtk.args.outputs.labels << Components::Label.data[key].set
+ elsif !(Components::Map.id & Entity.signatures[key]).zero?
+ Components::Map.data[key].json['layers'].each do |layer|
+ layer['chunks'].each do |chunk|
+ chunk['data'].each_slice(chunk['width']).with_index do |row, row_index|
+ row.each_with_index do |tile, column_index|
+ unless tile.zero?
+ iter = 0
+ loop do
+ tile = Helper.get_tile(json_name: Components::Map.data[key].json['tilesets'][iter]['source'].split('/').last.delete('\\').delete_suffix('.tsx'), tile_index: tile)
+ break if tile.is_a?(Hash)
+ raise Exception.new "#{Components::Map.data[key].json['json_name']} not valid map, exceeded tile range" if (iter += 1) >= Components::Map.data[key].json['tilesets'].count
+ end
+ unless tile.empty?
+ tile[:x] = Components::Map.data[key].x + (Components::Map.data[key].tilewidth * column_index) + chunk['x']
+ tile[:y] = Components::Map.data[key].y + (Components::Map.data[key].tileheight * row_index) + chunk['y']
+ tile[:w] = Components::Map.data[key].tilewidth
+ tile[:h] = Components::Map.data[key].tileheight
+ $gtk.args.outputs.sprites << tile
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/test.rb b/test.rb
new file mode 100644
index 0000000..13fd401
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,18 @@
+require_relative './entity_manager.rb'
+require_relative './component_manager.rb'
+require_relative './system_manager.rb'
+
+move = '0001'.to_i(2)
+base = '0010'.to_i(2)
+both = '0011'.to_i(2)
+Entity.new(move)
+Entity.new(base)
+Entity.new(both)
+
+3.times do
+ Systems.constants.each do |constant|
+ puts "|----#{constant.to_s.upcase}----|"
+ Systems::const_get(constant).run
+ end
+ #ECS::Entity.destroy_entity(ECS::Entity.all.last.id) unless ECS::Entity.all.empty?
+end