rgame 0.4.0-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +251 -0
  3. data/LICENSE +26 -0
  4. data/README.md +325 -0
  5. data/docs/api/README.md +224 -0
  6. data/docs/api/app.md +249 -0
  7. data/docs/api/assets.md +463 -0
  8. data/docs/api/audio.md +249 -0
  9. data/docs/api/cli.md +259 -0
  10. data/docs/api/components.md +1112 -0
  11. data/docs/api/drawing.md +398 -0
  12. data/docs/api/examples.md +263 -0
  13. data/docs/api/game.md +219 -0
  14. data/docs/api/images.md +118 -0
  15. data/docs/api/input.md +476 -0
  16. data/docs/api/internals.md +289 -0
  17. data/docs/api/localization.md +285 -0
  18. data/docs/api/scene_graph.md +539 -0
  19. data/docs/api/signals.md +147 -0
  20. data/docs/api/systems.md +231 -0
  21. data/docs/api/text.md +133 -0
  22. data/docs/api/tile_maps.md +237 -0
  23. data/docs/api/toolbox.md +493 -0
  24. data/docs/api/ui.md +927 -0
  25. data/docs/api/values.md +316 -0
  26. data/examples/assets/README.md +322 -0
  27. data/examples/assets/blip.ogg +0 -0
  28. data/examples/assets/glyphs.json +5 -0
  29. data/examples/assets/glyphs.png +0 -0
  30. data/examples/assets/hero.json +12 -0
  31. data/examples/assets/hero.png +0 -0
  32. data/examples/assets/icons.json +13 -0
  33. data/examples/assets/icons.png +0 -0
  34. data/examples/assets/music.ogg +0 -0
  35. data/examples/assets/skills.json +10 -0
  36. data/examples/assets/skills.png +0 -0
  37. data/examples/assets/tileset.png +0 -0
  38. data/examples/assets/tileset.tsx +65 -0
  39. data/examples/assets/town.tmx +26 -0
  40. data/examples/assets/ui.json +11 -0
  41. data/examples/assets/ui.png +0 -0
  42. data/examples/collision/locales/en.yml +8 -0
  43. data/examples/collision/main.rb +316 -0
  44. data/examples/collision_tiles/locales/en.yml +9 -0
  45. data/examples/collision_tiles/main.rb +274 -0
  46. data/examples/fullscreen/locales/en.yml +10 -0
  47. data/examples/fullscreen/main.rb +216 -0
  48. data/examples/game_menu/locales/en.yml +8 -0
  49. data/examples/game_menu/main.rb +170 -0
  50. data/examples/input_glyphs/locales/en.yml +14 -0
  51. data/examples/input_glyphs/main.rb +213 -0
  52. data/examples/jump_topdown/locales/en.yml +9 -0
  53. data/examples/jump_topdown/main.rb +178 -0
  54. data/examples/localization/locales/de.yml +12 -0
  55. data/examples/localization/locales/en.yml +13 -0
  56. data/examples/localization/main.rb +158 -0
  57. data/examples/menu_navigation/locales/en.yml +23 -0
  58. data/examples/menu_navigation/main.rb +365 -0
  59. data/examples/music/locales/en.yml +7 -0
  60. data/examples/music/main.rb +134 -0
  61. data/examples/pathfinding/locales/en.yml +17 -0
  62. data/examples/pathfinding/main.rb +298 -0
  63. data/examples/pooling/locales/en.yml +7 -0
  64. data/examples/pooling/main.rb +259 -0
  65. data/examples/quick_wheel/locales/en.yml +16 -0
  66. data/examples/quick_wheel/main.rb +184 -0
  67. data/examples/radial_menu/locales/en.yml +16 -0
  68. data/examples/radial_menu/main.rb +184 -0
  69. data/examples/save_load/locales/en.yml +11 -0
  70. data/examples/save_load/main.rb +207 -0
  71. data/examples/save_load_ids/locales/en.yml +11 -0
  72. data/examples/save_load_ids/main.rb +322 -0
  73. data/examples/scroll_map/locales/en.yml +4 -0
  74. data/examples/scroll_map/main.rb +140 -0
  75. data/examples/signals/locales/en.yml +6 -0
  76. data/examples/signals/main.rb +278 -0
  77. data/examples/skill_bar/locales/en.yml +14 -0
  78. data/examples/skill_bar/main.rb +159 -0
  79. data/examples/sound/locales/en.yml +6 -0
  80. data/examples/sound/main.rb +122 -0
  81. data/examples/split_screen/locales/en.yml +9 -0
  82. data/examples/split_screen/main.rb +304 -0
  83. data/examples/sprite/locales/en.yml +8 -0
  84. data/examples/sprite/main.rb +180 -0
  85. data/examples/timer/locales/en.yml +12 -0
  86. data/examples/timer/main.rb +273 -0
  87. data/examples/velocity/locales/en.yml +6 -0
  88. data/examples/velocity/main.rb +196 -0
  89. data/examples/walk/locales/en.yml +4 -0
  90. data/examples/walk/main.rb +99 -0
  91. data/exe/rgame +9 -0
  92. data/lib/rgame/boot.rb +3 -0
  93. data/lib/rgame/cli/new_project.rb +139 -0
  94. data/lib/rgame/cli/templates/Gemfile.tt +23 -0
  95. data/lib/rgame/cli/templates/README.md.tt +93 -0
  96. data/lib/rgame/cli/templates/Rakefile.tt +9 -0
  97. data/lib/rgame/cli/templates/assets/locales/en.yml.tt +10 -0
  98. data/lib/rgame/cli/templates/game.rb.tt +23 -0
  99. data/lib/rgame/cli/templates/gitignore.tt +12 -0
  100. data/lib/rgame/cli/templates/main.rb.tt +11 -0
  101. data/lib/rgame/cli/templates/nodes/root.rb.tt +24 -0
  102. data/lib/rgame/cli/templates/rspec.tt +2 -0
  103. data/lib/rgame/cli/templates/rubocop.yml.tt +75 -0
  104. data/lib/rgame/cli/templates/ruby-version.tt +1 -0
  105. data/lib/rgame/cli/templates/spec/locales_spec.rb.tt +18 -0
  106. data/lib/rgame/cli/templates/spec/nodes/root_spec.rb.tt +18 -0
  107. data/lib/rgame/cli/templates/spec/spec_helper.rb.tt +37 -0
  108. data/lib/rgame/cli.rb +66 -0
  109. data/lib/rgame/core/app.rb +46 -0
  110. data/lib/rgame/core/asset_manager.rb +206 -0
  111. data/lib/rgame/core/audio.rb +145 -0
  112. data/lib/rgame/core/font.rb +46 -0
  113. data/lib/rgame/core/gamepad.rb +55 -0
  114. data/lib/rgame/core/image.rb +55 -0
  115. data/lib/rgame/core/input.rb +71 -0
  116. data/lib/rgame/core/locale.rb +22 -0
  117. data/lib/rgame/core/nine_slice.rb +142 -0
  118. data/lib/rgame/core/recording.rb +54 -0
  119. data/lib/rgame/core/renderer.rb +354 -0
  120. data/lib/rgame/core/sprite_sheet.rb +105 -0
  121. data/lib/rgame/core/tile_map_renderer.rb +172 -0
  122. data/lib/rgame/core/ui_atlas.rb +101 -0
  123. data/lib/rgame/core/virtual_gamepad.rb +26 -0
  124. data/lib/rgame/core.rb +18 -0
  125. data/lib/rgame/core_ext.so +0 -0
  126. data/lib/rgame/engine/actor_blockers.rb +131 -0
  127. data/lib/rgame/engine/animation_set.rb +50 -0
  128. data/lib/rgame/engine/animator.rb +44 -0
  129. data/lib/rgame/engine/audio_bus.rb +24 -0
  130. data/lib/rgame/engine/audio_director.rb +59 -0
  131. data/lib/rgame/engine/bounds_blockers.rb +74 -0
  132. data/lib/rgame/engine/camera.rb +78 -0
  133. data/lib/rgame/engine/circle_collider.rb +34 -0
  134. data/lib/rgame/engine/collision_box.rb +59 -0
  135. data/lib/rgame/engine/collision_system.rb +132 -0
  136. data/lib/rgame/engine/component.rb +64 -0
  137. data/lib/rgame/engine/components/action_trigger.rb +40 -0
  138. data/lib/rgame/engine/components/animated_sprite.rb +71 -0
  139. data/lib/rgame/engine/components/box_collider.rb +99 -0
  140. data/lib/rgame/engine/components/camera_follow.rb +45 -0
  141. data/lib/rgame/engine/components/character_body.rb +50 -0
  142. data/lib/rgame/engine/components/circle_collider.rb +80 -0
  143. data/lib/rgame/engine/components/collision_world.rb +231 -0
  144. data/lib/rgame/engine/components/despawn_offscreen.rb +42 -0
  145. data/lib/rgame/engine/components/feet_collider.rb +61 -0
  146. data/lib/rgame/engine/components/hop.rb +76 -0
  147. data/lib/rgame/engine/components/identity.rb +73 -0
  148. data/lib/rgame/engine/components/mover.rb +285 -0
  149. data/lib/rgame/engine/components/navigator.rb +145 -0
  150. data/lib/rgame/engine/components/path_follow.rb +176 -0
  151. data/lib/rgame/engine/components/player_controller.rb +27 -0
  152. data/lib/rgame/engine/components/pool.rb +53 -0
  153. data/lib/rgame/engine/components/screen_wrap.rb +49 -0
  154. data/lib/rgame/engine/components/sprite.rb +47 -0
  155. data/lib/rgame/engine/components/targeting.rb +52 -0
  156. data/lib/rgame/engine/components/thrust_controller.rb +65 -0
  157. data/lib/rgame/engine/components/tile_world.rb +109 -0
  158. data/lib/rgame/engine/components/timer.rb +75 -0
  159. data/lib/rgame/engine/components/velocity.rb +43 -0
  160. data/lib/rgame/engine/components/wander_controller.rb +64 -0
  161. data/lib/rgame/engine/components/world.rb +133 -0
  162. data/lib/rgame/engine/contact_set.rb +74 -0
  163. data/lib/rgame/engine/culling.rb +45 -0
  164. data/lib/rgame/engine/debug_overlay.rb +111 -0
  165. data/lib/rgame/engine/i18n/plural.rb +45 -0
  166. data/lib/rgame/engine/i18n/plural_rules.rb +82 -0
  167. data/lib/rgame/engine/i18n/template.rb +59 -0
  168. data/lib/rgame/engine/i18n.rb +322 -0
  169. data/lib/rgame/engine/input/action_mapper.rb +100 -0
  170. data/lib/rgame/engine/input/actions.rb +92 -0
  171. data/lib/rgame/engine/input/input_map.rb +196 -0
  172. data/lib/rgame/engine/layout.rb +82 -0
  173. data/lib/rgame/engine/nav_grid.rb +87 -0
  174. data/lib/rgame/engine/node2d.rb +555 -0
  175. data/lib/rgame/engine/path.rb +76 -0
  176. data/lib/rgame/engine/player.rb +69 -0
  177. data/lib/rgame/engine/player_layer.rb +70 -0
  178. data/lib/rgame/engine/players.rb +205 -0
  179. data/lib/rgame/engine/pool.rb +51 -0
  180. data/lib/rgame/engine/presentation.rb +171 -0
  181. data/lib/rgame/engine/scene/scene_stack.rb +87 -0
  182. data/lib/rgame/engine/sealed_privates.rb +54 -0
  183. data/lib/rgame/engine/signal.rb +75 -0
  184. data/lib/rgame/engine/spatial_hash.rb +116 -0
  185. data/lib/rgame/engine/text.rb +194 -0
  186. data/lib/rgame/engine/tile_blockers.rb +63 -0
  187. data/lib/rgame/engine/tile_map.rb +147 -0
  188. data/lib/rgame/engine/tile_map_layer.rb +82 -0
  189. data/lib/rgame/engine/tileset.rb +99 -0
  190. data/lib/rgame/engine/timer.rb +51 -0
  191. data/lib/rgame/engine/ui/button.rb +248 -0
  192. data/lib/rgame/engine/ui/column.rb +20 -0
  193. data/lib/rgame/engine/ui/icon_button.rb +93 -0
  194. data/lib/rgame/engine/ui/menu.rb +290 -0
  195. data/lib/rgame/engine/ui/navigation.rb +57 -0
  196. data/lib/rgame/engine/ui/nine_slice_style.rb +50 -0
  197. data/lib/rgame/engine/ui/option_button.rb +163 -0
  198. data/lib/rgame/engine/ui/panel_button.rb +32 -0
  199. data/lib/rgame/engine/ui/panel_menu.rb +36 -0
  200. data/lib/rgame/engine/ui/pointing.rb +146 -0
  201. data/lib/rgame/engine/ui/radial_menu.rb +85 -0
  202. data/lib/rgame/engine/ui/ring.rb +55 -0
  203. data/lib/rgame/engine/ui/row.rb +21 -0
  204. data/lib/rgame/engine/ui/shape_style.rb +102 -0
  205. data/lib/rgame/engine/ui/stack.rb +58 -0
  206. data/lib/rgame/engine/ui/stepping.rb +93 -0
  207. data/lib/rgame/engine/ui/text_button.rb +59 -0
  208. data/lib/rgame/engine/view.rb +76 -0
  209. data/lib/rgame/engine/viewports.rb +171 -0
  210. data/lib/rgame/engine/world_view.rb +71 -0
  211. data/lib/rgame/engine.rb +86 -0
  212. data/lib/rgame/fonts/LiberationSans-Regular.ttf +0 -0
  213. data/lib/rgame/fonts/OFL.txt +102 -0
  214. data/lib/rgame/game.rb +271 -0
  215. data/lib/rgame/rubocop/cop/game/draw_in_local_space.rb +103 -0
  216. data/lib/rgame/rubocop/cop/game/hot_path.rb +36 -0
  217. data/lib/rgame/rubocop/cop/game/layer_boundary.rb +43 -0
  218. data/lib/rgame/rubocop/cop/game/no_core_in_engine_layer.rb +100 -0
  219. data/lib/rgame/rubocop/cop/game/no_engine_in_core_layer.rb +84 -0
  220. data/lib/rgame/rubocop/cop/game/no_interpolation_in_hot_path.rb +50 -0
  221. data/lib/rgame/rubocop/cop/game/no_literal_text.rb +41 -0
  222. data/lib/rgame/rubocop/cop/game/no_needless_allocation.rb +112 -0
  223. data/lib/rgame/rubocop/default.yml +39 -0
  224. data/lib/rgame/rubocop/plugin.rb +45 -0
  225. data/lib/rgame/rubocop.rb +11 -0
  226. data/lib/rgame/util/color.rb +23 -0
  227. data/lib/rgame/util/controls.rb +169 -0
  228. data/lib/rgame/util/route_search.rb +27 -0
  229. data/lib/rgame/util/save_file.rb +107 -0
  230. data/lib/rgame/util/solid_grid.rb +37 -0
  231. data/lib/rgame/util/tensor.rb +3 -0
  232. data/lib/rgame/util/tile_sweep.rb +36 -0
  233. data/lib/rgame/util/z.rb +123 -0
  234. data/lib/rgame/util.rb +10 -0
  235. data/lib/rgame/util_ext.so +0 -0
  236. data/lib/rgame/version.rb +12 -0
  237. data/lib/rgame.rb +5 -0
  238. data/licenses/SDL2/LICENSE.txt +18 -0
  239. metadata +309 -0
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RGame
4
+ module Engine
5
+ # One viewport being drawn: a rectangle of the screen, and (for a world
6
+ # view) the camera to look through it with.
7
+ #
8
+ # view.x, view.y, view.width, view.height # the screen rect
9
+ # view.camera # nil in screen space
10
+ # view.player # whose view this is, or nil
11
+ # view.visible?(x, y, w, h) # is this worth drawing
12
+ #
13
+ # ## What a view is for
14
+ #
15
+ # `draw(renderer, view)` hands every node the view it is being drawn into,
16
+ # which answers two questions nothing else can. **Where the edges are**: a
17
+ # HUD laying itself out against the whole window is wrong the moment the
18
+ # window is a player's half of one, and until this existed there was nothing
19
+ # else to ask. And **what is worth drawing**: with the world drawn once per
20
+ # player, culling stops being an optimisation and starts being the
21
+ # difference between one frame's work and four.
22
+ #
23
+ # ## It is reused, not rebuilt
24
+ #
25
+ # Viewports owns one of these per viewport and mutates it in place each
26
+ # frame, the way ActionMapper reuses its Actions. Building fresh ones would
27
+ # allocate a handful of objects every frame — invisible by every measure
28
+ # except the frame that stutters. **Hold the player or the viewports, never
29
+ # this**: the object a node was handed last frame is the same one, with
30
+ # different numbers in it.
31
+ class View
32
+ attr_reader :x, :y, :width, :height, :camera, :player
33
+
34
+ def initialize(x: 0, y: 0, width: 0, height: 0, camera: nil, player: nil)
35
+ set(x, y, width, height, camera: camera, player: player)
36
+ end
37
+
38
+ # Mutated in place by Viewports once per frame. Not for game code.
39
+ def set(x, y, width, height, camera: nil, player: nil)
40
+ @x = x
41
+ @y = y
42
+ @width = width
43
+ @height = height
44
+ @camera = camera
45
+ @player = player
46
+ self
47
+ end
48
+
49
+ # Where this view's contents start, in the space its nodes draw in: the
50
+ # camera's offset for a world view, and the origin for a screen-space one,
51
+ # whose nodes draw relative to the view's own corner.
52
+ # hot-path
53
+ def origin_x = @camera ? @camera.x : 0
54
+ # hot-path
55
+ def origin_y = @camera ? @camera.y : 0
56
+
57
+ # Does a rectangle overlap what this view shows? Coordinates are in the
58
+ # space the caller draws in — world coordinates under a camera, view-local
59
+ # ones in screen space — which is the same space `origin_x` is in.
60
+ # hot-path
61
+ def visible?(x, y, width, height)
62
+ left = origin_x
63
+ top = origin_y
64
+ x + width > left && x < left + @width &&
65
+ y + height > top && y < top + @height
66
+ end
67
+
68
+ # The offset to translate by so that content at `origin` lands at this
69
+ # view's corner on screen. The whole of split-screen, in two numbers.
70
+ # hot-path
71
+ def offset_x = @x - origin_x
72
+ # hot-path
73
+ def offset_y = @y - origin_y
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RGame
4
+ module Engine
5
+ # How the screen is divided, as a root-scoped system.
6
+ #
7
+ # node.system(Viewports).views # one View per active player
8
+ # node.system(Viewports).screen # the whole window, no camera
9
+ # node.system(Viewports).solo!(cam) # collapse to one view — cutscene
10
+ #
11
+ # It holds the mutable half of the question — which mode is current, who is
12
+ # playing, how big the window is — while Layout holds the arithmetic. That
13
+ # split is deliberate: the rect maths is pure and gets specced with no tree
14
+ # and no window, and only the state that genuinely changes lives in a
15
+ # component.
16
+ #
17
+ # It is a Component on the root, so any node reaches it by walking the tree
18
+ # rather than having it threaded through a constructor — the same shape
19
+ # CollisionWorld and Players use.
20
+ #
21
+ # ## Mode changes are deferred
22
+ #
23
+ # `solo!` and `split!` record a request; it is applied in `update`, which
24
+ # runs in the root's component phase. That matters because this is reachable
25
+ # from anywhere, including from a `draw` — and a `draw` now runs once per
26
+ # view, so a mode change made there would fire several times and tear the
27
+ # frame it was made in. Deferring is the same shape `queue_free` uses, and it
28
+ # means a change takes effect on the next tick.
29
+ class Viewports < Component
30
+ # `views` is the list drawn through this frame — one per active player
31
+ # while split, exactly one while solo. Reused, like the Views in it.
32
+ attr_reader :screen, :width, :height, :views
33
+
34
+ def initialize(players, width: 0, height: 0)
35
+ super()
36
+ @players = players
37
+ @width = width
38
+ @height = height
39
+ @solo_camera = nil
40
+ @pending = nil
41
+ @pool = []
42
+ @screen_pool = []
43
+ @views = []
44
+ @screen = View.new
45
+ refresh
46
+ end
47
+
48
+ # The window changed size. Rects are recomputed from it on the next
49
+ # refresh, and every camera reclamps against its new rect — which is why
50
+ # a camera does not carry one.
51
+ def resize(width, height)
52
+ @width = width
53
+ @height = height
54
+ refresh
55
+ end
56
+
57
+ def solo? = !@solo_camera.nil?
58
+
59
+ # The screen-space region belonging to `player`: the same rectangle their
60
+ # world view is drawn into, with **no camera**, so its contents are laid
61
+ # out against their own corner rather than the window's. Their HUD and
62
+ # their menus live here.
63
+ #
64
+ # `nil` when they have no region to draw into, which is two cases and one
65
+ # answer. An **empty seat** has no viewport at all. And while the split is
66
+ # **collapsed**, nobody has a half of the screen to own: a cutscene is
67
+ # everyone looking at one thing, so per-player UI has no place to be, and
68
+ # a game wanting something on screen through it draws in the global
69
+ # overlay band instead.
70
+ #
71
+ # Returning nil rather than an empty rectangle is deliberate: one check at
72
+ # the one caller that needs it beats every caller relying on a zero-sized
73
+ # clip happening to draw nothing.
74
+ def screen_for(player)
75
+ return nil if player.nil? || @solo_camera
76
+
77
+ world = @views.find { |view| view.player.equal?(player) }
78
+ return nil if world.nil?
79
+
80
+ pooled_screen(@players.list.index(player))
81
+ .set(world.x, world.y, world.width, world.height, player: player)
82
+ end
83
+
84
+ # Collapse to a single screen-wide view through `camera`.
85
+ #
86
+ # The camera is **required**: promoting one player's would silently give
87
+ # everyone else their view, and deciding what is on screen is what a
88
+ # cutscene is for. A game points an ordinary Camera wherever it likes —
89
+ # with a CameraFollow on a cutscene actor, or its own component framing
90
+ # every player at once — and hands it here.
91
+ def solo!(camera)
92
+ raise ArgumentError, 'solo! needs a camera to look through' if camera.nil?
93
+
94
+ @pending = camera
95
+ self
96
+ end
97
+
98
+ # Back to one view per player.
99
+ def split! = @pending = :split
100
+
101
+ # Applies a pending mode change, then rebuilds the rects. Runs in the
102
+ # root's component phase, so a change requested during a tick lands on the
103
+ # next one.
104
+ def update(_dt)
105
+ apply_pending
106
+ refresh
107
+ end
108
+
109
+ # Recompute every rect from the current mode and window, and reclamp each
110
+ # camera against the rect it is about to be drawn into.
111
+ #
112
+ # Allocation-free once the pool has grown: Layout yields its rects rather
113
+ # than building them, and the Views are mutated in place.
114
+ #
115
+ # @api private
116
+ def refresh
117
+ @screen.set(0, 0, @width, @height)
118
+ @solo_camera ? refresh_solo : refresh_split
119
+ @views.each { |view| view.camera&.resolve(view.width, view.height) }
120
+ self
121
+ end
122
+
123
+ private
124
+
125
+ def apply_pending
126
+ return if @pending.nil?
127
+
128
+ @solo_camera = @pending == :split ? nil : @pending
129
+ @pending = nil
130
+ end
131
+
132
+ def refresh_solo
133
+ @views.clear
134
+ @views << pooled(0).set(0, 0, @width, @height, camera: @solo_camera)
135
+ end
136
+
137
+ def refresh_split
138
+ @views.clear
139
+ count = @players.active_count
140
+ return if count.zero?
141
+
142
+ index = 0
143
+ Layout.each_rect(count, @width, @height) do |i, x, y, w, h|
144
+ player = active_at(i)
145
+ @views << pooled(index).set(x, y, w, h, camera: player.camera, player: player)
146
+ index += 1
147
+ end
148
+ end
149
+
150
+ def active_at(index)
151
+ found = nil
152
+ i = 0
153
+ @players.each do |player|
154
+ next unless player.active?
155
+
156
+ found = player if i == index
157
+ i += 1
158
+ end
159
+ found
160
+ end
161
+
162
+ def pooled(index)
163
+ @pool[index] ||= View.new
164
+ end
165
+
166
+ def pooled_screen(index)
167
+ @screen_pool[index] ||= View.new
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RGame
4
+ module Engine
5
+ # The node that marks where world space begins.
6
+ #
7
+ # view = scene.add_node(WorldView.new)
8
+ # view.add_node(player) # draws at its own world coordinates
9
+ #
10
+ # Its children live in **world** coordinates and are drawn once per active
11
+ # viewport: for each one, clip to that viewport's rectangle and translate by
12
+ # its camera, then run the same subtree again. Everything outside a
13
+ # WorldView is screen space and draws once.
14
+ #
15
+ # That is the whole of split-screen, and it is why the transform and clip
16
+ # stacks in Core are proper push/pop stacks — a clip always narrows, so a
17
+ # child can never draw outside the region its parent allowed.
18
+ #
19
+ # ## The subtree does not know how many times it is drawn
20
+ #
21
+ # Children draw in their own local space and never see a camera. This node
22
+ # supplies the view — one clip and one translate around the whole subtree —
23
+ # so the same world serves one player or four with nothing below it changing,
24
+ # which is exactly what a camera owned by a node *inside* the world could not
25
+ # do, and why cameras belong to players.
26
+ #
27
+ # ## Only `draw` multiplies, and deliberately
28
+ #
29
+ # `control` and `update` still run **once** for this subtree, however many
30
+ # viewports draw it. That is what keeps simulation cost independent of player
31
+ # count, and it is also a safety property: a world node's `on_update` is
32
+ # where mutation belongs, so per-view updating would move an actor at twice
33
+ # the speed with two players — correctly in single player, silently wrong the
34
+ # moment somebody joins.
35
+ #
36
+ # It does make the standing "draw renders state" rule load-bearing rather
37
+ # than stylistic: a `draw` with a side effect now happens once per player,
38
+ # and an allocation in one costs that many times.
39
+ #
40
+ # A node that genuinely needs per-view work at draw time can read
41
+ # `system(Viewports).views` itself. Anything needing per-view state *over
42
+ # time* — a screen shake, a hit flash — is per-*player* rather than per-view,
43
+ # and belongs on that player's camera or their own subtree, which tick once.
44
+ class WorldView < Node2D
45
+ # World content, which is the default anyway — stated because this is the
46
+ # node that marks where world space begins, and a reader looking for
47
+ # "which band is the world in" should find the answer here.
48
+ def initialize(**)
49
+ super(band: :world, **)
50
+ end
51
+
52
+ # Drawn once per viewport, so this overrides the whole of `draw` rather
53
+ # than just `draw_children`: the node's own visuals belong inside the
54
+ # viewport too, not once outside all of them.
55
+ #
56
+ # `view` is the screen-space view this node was reached with, and is
57
+ # deliberately ignored — a WorldView asks the system which viewports exist
58
+ # rather than being told, so a game can place one anywhere in the tree.
59
+ def draw(renderer, _view = nil)
60
+ viewports = system(Viewports)
61
+ viewports.views.each do |world_view|
62
+ renderer.clipped(world_view.x, world_view.y, world_view.width, world_view.height) do
63
+ renderer.translated(world_view.offset_x, world_view.offset_y) do
64
+ super(renderer, world_view)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'util'
4
+
5
+ require_relative 'engine/culling'
6
+ require_relative 'engine/signal'
7
+ require_relative 'engine/sealed_privates'
8
+ require_relative 'engine/node2d'
9
+ require_relative 'engine/component'
10
+ require_relative 'engine/animation_set'
11
+ require_relative 'engine/animator'
12
+ require_relative 'engine/camera'
13
+ require_relative 'engine/layout'
14
+ require_relative 'engine/presentation'
15
+ require_relative 'engine/view'
16
+ require_relative 'engine/world_view'
17
+ require_relative 'engine/player_layer'
18
+ require_relative 'engine/ui/button'
19
+ require_relative 'engine/ui/nine_slice_style'
20
+ require_relative 'engine/ui/shape_style'
21
+ require_relative 'engine/ui/text_button'
22
+ require_relative 'engine/ui/panel_button'
23
+ require_relative 'engine/ui/option_button'
24
+ require_relative 'engine/ui/icon_button'
25
+ require_relative 'engine/ui/stack'
26
+ require_relative 'engine/ui/column'
27
+ require_relative 'engine/ui/row'
28
+ require_relative 'engine/ui/ring'
29
+ require_relative 'engine/ui/navigation'
30
+ require_relative 'engine/ui/stepping'
31
+ require_relative 'engine/ui/pointing'
32
+ require_relative 'engine/ui/menu'
33
+ require_relative 'engine/ui/panel_menu'
34
+ require_relative 'engine/ui/radial_menu'
35
+ require_relative 'engine/path'
36
+ require_relative 'engine/timer'
37
+ require_relative 'engine/tileset'
38
+ require_relative 'engine/tile_map'
39
+ require_relative 'engine/tile_blockers'
40
+ require_relative 'engine/nav_grid'
41
+ require_relative 'engine/actor_blockers'
42
+ require_relative 'engine/bounds_blockers'
43
+ require_relative 'engine/collision_box'
44
+ require_relative 'engine/collision_system'
45
+ require_relative 'engine/circle_collider'
46
+ require_relative 'engine/spatial_hash'
47
+ require_relative 'engine/contact_set'
48
+ require_relative 'engine/pool'
49
+ require_relative 'engine/audio_bus'
50
+ require_relative 'engine/audio_director'
51
+ require_relative 'engine/i18n'
52
+ require_relative 'engine/text'
53
+ require_relative 'engine/debug_overlay'
54
+ require_relative 'engine/input/actions'
55
+ require_relative 'engine/input/input_map'
56
+ require_relative 'engine/input/action_mapper'
57
+ require_relative 'engine/player'
58
+ require_relative 'engine/players'
59
+ require_relative 'engine/viewports'
60
+ require_relative 'engine/scene/scene_stack'
61
+ require_relative 'engine/components/identity'
62
+ require_relative 'engine/components/mover'
63
+ require_relative 'engine/components/velocity'
64
+ require_relative 'engine/components/pool'
65
+ require_relative 'engine/components/path_follow'
66
+ require_relative 'engine/components/navigator'
67
+ require_relative 'engine/components/timer'
68
+ require_relative 'engine/components/world'
69
+ require_relative 'engine/components/screen_wrap'
70
+ require_relative 'engine/components/despawn_offscreen'
71
+ require_relative 'engine/components/circle_collider'
72
+ require_relative 'engine/components/box_collider'
73
+ require_relative 'engine/components/feet_collider'
74
+ require_relative 'engine/components/collision_world'
75
+ require_relative 'engine/components/targeting'
76
+ require_relative 'engine/components/sprite'
77
+ require_relative 'engine/components/thrust_controller'
78
+ require_relative 'engine/components/action_trigger'
79
+ require_relative 'engine/components/tile_world'
80
+ require_relative 'engine/components/character_body'
81
+ require_relative 'engine/components/player_controller'
82
+ require_relative 'engine/components/hop'
83
+ require_relative 'engine/components/wander_controller'
84
+ require_relative 'engine/components/animated_sprite'
85
+ require_relative 'engine/components/camera_follow'
86
+ require_relative 'engine/tile_map_layer'
@@ -0,0 +1,102 @@
1
+ Digitized data copyright (c) 2010 Google Corporation
2
+ with Reserved Font Arimo, Tinos and Cousine.
3
+ Copyright (c) 2012 Red Hat, Inc.
4
+ with Reserved Font Name Liberation.
5
+
6
+ This Font Software is licensed under the SIL Open Font License,
7
+ Version 1.1.
8
+
9
+ This license is copied below, and is also available with a FAQ at:
10
+ http://scripts.sil.org/OFL
11
+
12
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
13
+
14
+ PREAMBLE The goals of the Open Font License (OFL) are to stimulate
15
+ worldwide development of collaborative font projects, to support the font
16
+ creation efforts of academic and linguistic communities, and to provide
17
+ a free and open framework in which fonts may be shared and improved in
18
+ partnership with others.
19
+
20
+ The OFL allows the licensed fonts to be used, studied, modified and
21
+ redistributed freely as long as they are not sold by themselves.
22
+ The fonts, including any derivative works, can be bundled, embedded,
23
+ redistributed and/or sold with any software provided that any reserved
24
+ names are not used by derivative works. The fonts and derivatives,
25
+ however, cannot be released under any other type of license. The
26
+ requirement for fonts to remain under this license does not apply to
27
+ any document created using the fonts or their derivatives.
28
+
29
+
30
+
31
+ DEFINITIONS
32
+ "Font Software" refers to the set of files released by the Copyright
33
+ Holder(s) under this license and clearly marked as such.
34
+ This may include source files, build scripts and documentation.
35
+
36
+ "Reserved Font Name" refers to any names specified as such after the
37
+ copyright statement(s).
38
+
39
+ "Original Version" refers to the collection of Font Software components
40
+ as distributed by the Copyright Holder(s).
41
+
42
+ "Modified Version" refers to any derivative made by adding to, deleting,
43
+ or substituting ? in part or in whole ?
44
+ any of the components of the Original Version, by changing formats or
45
+ by porting the Font Software to a new environment.
46
+
47
+ "Author" refers to any designer, engineer, programmer, technical writer
48
+ or other person who contributed to the Font Software.
49
+
50
+
51
+ PERMISSION & CONDITIONS
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining a
54
+ copy of the Font Software, to use, study, copy, merge, embed, modify,
55
+ redistribute, and sell modified and unmodified copies of the Font
56
+ Software, subject to the following conditions:
57
+
58
+ 1) Neither the Font Software nor any of its individual components,in
59
+ Original or Modified Versions, may be sold by itself.
60
+
61
+ 2) Original or Modified Versions of the Font Software may be bundled,
62
+ redistributed and/or sold with any software, provided that each copy
63
+ contains the above copyright notice and this license. These can be
64
+ included either as stand-alone text files, human-readable headers or
65
+ in the appropriate machine-readable metadata fields within text or
66
+ binary files as long as those fields can be easily viewed by the user.
67
+
68
+ 3) No Modified Version of the Font Software may use the Reserved Font
69
+ Name(s) unless explicit written permission is granted by the
70
+ corresponding Copyright Holder. This restriction only applies to the
71
+ primary font name as presented to the users.
72
+
73
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
74
+ Software shall not be used to promote, endorse or advertise any
75
+ Modified Version, except to acknowledge the contribution(s) of the
76
+ Copyright Holder(s) and the Author(s) or with their explicit written
77
+ permission.
78
+
79
+ 5) The Font Software, modified or unmodified, in part or in whole, must
80
+ be distributed entirely under this license, and must not be distributed
81
+ under any other license. The requirement for fonts to remain under
82
+ this license does not apply to any document created using the Font
83
+ Software.
84
+
85
+
86
+
87
+ TERMINATION
88
+ This license becomes null and void if any of the above conditions are not met.
89
+
90
+
91
+
92
+ DISCLAIMER
93
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
94
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
95
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
96
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
97
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
98
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
99
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
100
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
101
+ DEALINGS IN THE FONT SOFTWARE.
102
+