RapidGameFramework
Reusable Godot managers for data-driven small games
Loading...
Searching...
No Matches
entity_renderer.gd
Go to the documentation of this file.
1extends RefCounted
2
3
6
7const AnimationStateManager = preload("res://scripts/systems/animationStateManager/animation_state_manager.gd")
8const RenderNodePool = preload("res://scripts/systems/renderNodePool/render_node_pool.gd")
9const ResourceBar = preload("res://scripts/systems/resourceBar/resource_bar.gd")
10
11var animation_state_manager = AnimationStateManager.new()
12var render_node_pool = RenderNodePool.new()
13var resource_bar = ResourceBar.new()
14
15
16
17func render(parent:Control, entities:Array, sprite_manager, options:Dictionary = {}) -> Dictionary:
18 if parent == null:
19 return {}
20 var tile_size = float(options.get("tile_size", 40.0))
21 var entity_size = Vector2(float(options.get("entity_width", tile_size * 0.75)), float(options.get("entity_height", tile_size * 0.75)))
22 var rendered := 0
23 var sprites := 0
24 var fallbacks := 0
25 render_node_pool.begin(parent)
26 for entity in entities:
27 if not entity is Dictionary:
28 continue
29 if bool(options.get("alive_only", false)) and not bool(entity.get("alive", true)):
30 continue
31 var rect = _entity_rect(entity, tile_size, entity_size, options)
32 var candidates = animation_state_manager.get_sprite_candidates(entity, options.get("animation", {}))
33 var sprite_node = _draw_sprite(parent, sprite_manager, candidates, rect, _node_prefix(entity), options)
34 if sprite_node != null:
35 sprites += 1
36 else:
37 render_node_pool.rect(rect, _fallback_color(entity, options), _node_prefix(entity))
38 fallbacks += 1
39 if bool(options.get("show_health", false)):
40 _draw_health(parent, entity, rect, options)
41 rendered += 1
42 render_node_pool.end()
43 return {
44 "rendered": rendered,
45 "sprites": sprites,
46 "fallbacks": fallbacks,
47 "pool": render_node_pool.get_stats()
48 }
49
50
51
52func clear() -> void:
53 render_node_pool.clear()
54
55
56
57func entity_rect(entity:Dictionary, options:Dictionary = {}) -> Rect2:
58 return _entity_rect(
59 entity,
60 float(options.get("tile_size", 40.0)),
61 Vector2(float(options.get("entity_width", 30.0)), float(options.get("entity_height", 30.0))),
62 options
63 )
64
65
66func _draw_sprite(parent:Control, sprite_manager, candidates:Array, rect:Rect2, prefix:String, options:Dictionary) -> TextureRect:
67 if sprite_manager == null:
68 return null
69 for sprite_id in candidates:
70 var id = str(sprite_id)
71 if id == "":
72 continue
73 if sprite_manager.has_method("record_sprite_request"):
74 sprite_manager.record_sprite_request(id, {
75 "category": "entity",
76 "tags": ["entity", "runtime"]
77 })
78 if sprite_manager.has_method("get_texture") and sprite_manager.get_texture(id) == null:
79 continue
80 var node = render_node_pool.sprite(sprite_manager, id, rect, prefix, options.get("sprite_options", {}))
81 if node != null:
82 return node
83 return null
84
85
86func _draw_health(parent:Control, entity:Dictionary, rect:Rect2, options:Dictionary) -> void:
87 var hp = int(entity.get("hp", 0))
88 var max_hp = int(entity.get("max_hp", hp))
89 if max_hp <= 0:
90 return
91 var bar_height = float(options.get("health_height", 7.0))
92 var bar_rect = Rect2(Vector2(rect.position.x, rect.position.y - bar_height - 2.0), Vector2(rect.size.x, bar_height))
93 resource_bar.render(parent, bar_rect, {"value": hp, "max": max_hp}, {
94 "name": "%sHealth" % _node_prefix(entity),
95 "label": "" if not bool(options.get("health_text", false)) else "%d" % hp,
96 "border_width": 1.0,
97 "font_size": int(options.get("health_font_size", 9))
98 })
99
100
101func _entity_rect(entity:Dictionary, tile_size:float, entity_size:Vector2, options:Dictionary) -> Rect2:
102 var origin = options.get("origin", Vector2.ZERO)
103 if not origin is Vector2:
104 origin = Vector2.ZERO
105 var center = _position_from_entity(entity, tile_size) + origin
106 return Rect2(center - entity_size * 0.5, entity_size)
107
108
109func _position_from_entity(entity:Dictionary, tile_size:float) -> Vector2:
110 if entity.has("position"):
111 return _vector_from_any(entity.get("position"))
112 var cell = _cell_from_any(entity.get("cell", [0, 0]))
113 return Vector2(float(cell.x) * tile_size + tile_size * 0.5, float(cell.y) * tile_size + tile_size * 0.5)
114
115
116func _fallback_color(entity:Dictionary, options:Dictionary) -> Color:
117 var colors = options.get("fallback_colors", {})
118 if colors is Dictionary:
119 var team = str(entity.get("team", ""))
120 if colors.has(team):
121 return Color(str(colors[team]))
122 var kind = str(entity.get("kind", ""))
123 if colors.has(kind):
124 return Color(str(colors[kind]))
125 if not bool(entity.get("alive", true)):
126 return Color("#777777")
127 match str(entity.get("team", "neutral")):
128 "player":
129 return Color("#5da9ff")
130 "enemy":
131 return Color("#e85d75")
132 _:
133 return Color("#f2c86b")
134
135
136func _node_prefix(entity:Dictionary) -> String:
137 var raw = str(entity.get("id", entity.get("kind", "Entity")))
138 var cleaned = raw.replace("-", "_").replace(" ", "_")
139 return cleaned.capitalize().replace("_", "")
140
141
142func _cell_from_any(value) -> Vector2i:
143 if value is Vector2i:
144 return value
145 if value is Vector2:
146 return Vector2i(int(value.x), int(value.y))
147 if value is Array and value.size() >= 2:
148 return Vector2i(int(value[0]), int(value[1]))
149 if value is Dictionary and value.has("cell"):
150 return _cell_from_any(value.get("cell"))
151 if value is Dictionary and value.has("x") and value.has("y"):
152 return Vector2i(int(value.get("x", 0)), int(value.get("y", 0)))
153 return Vector2i.ZERO
154
155
156func _vector_from_any(value) -> Vector2:
157 if value is Vector2:
158 return value
159 if value is Vector2i:
160 return Vector2(float(value.x), float(value.y))
161 if value is Array and value.size() >= 2:
162 return Vector2(float(value[0]), float(value[1]))
163 if value is Dictionary and value.has("position"):
164 return _vector_from_any(value.get("position"))
165 if value is Dictionary and value.has("x") and value.has("y"):
166 return Vector2(float(value.get("x", 0.0)), float(value.get("y", 0.0)))
167 return Vector2.ZERO