rgame 0.1.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +228 -0
- data/README.md +142 -243
- data/docs/api/README.md +141 -90
- data/docs/api/app.md +125 -68
- data/docs/api/assets.md +203 -166
- data/docs/api/audio.md +130 -89
- data/docs/api/cli.md +259 -0
- data/docs/api/components.md +1045 -254
- data/docs/api/drawing.md +187 -119
- data/docs/api/examples.md +263 -0
- data/docs/api/game.md +165 -45
- data/docs/api/images.md +39 -39
- data/docs/api/input.md +344 -85
- data/docs/api/internals.md +241 -62
- data/docs/api/localization.md +285 -0
- data/docs/api/scene_graph.md +503 -123
- data/docs/api/signals.md +81 -76
- data/docs/api/systems.md +197 -64
- data/docs/api/text.md +58 -41
- data/docs/api/tile_maps.md +237 -0
- data/docs/api/toolbox.md +400 -147
- data/docs/api/ui.md +927 -0
- data/docs/api/values.md +234 -19
- data/examples/assets/README.md +322 -0
- data/examples/assets/blip.ogg +0 -0
- data/examples/assets/glyphs.json +5 -0
- data/examples/assets/glyphs.png +0 -0
- data/examples/assets/hero.json +12 -0
- data/examples/assets/hero.png +0 -0
- data/examples/assets/icons.json +13 -0
- data/examples/assets/icons.png +0 -0
- data/examples/assets/music.ogg +0 -0
- data/examples/assets/skills.json +10 -0
- data/examples/assets/skills.png +0 -0
- data/examples/assets/tileset.png +0 -0
- data/examples/assets/tileset.tsx +65 -0
- data/examples/assets/town.tmx +26 -0
- data/examples/assets/ui.json +11 -0
- data/examples/assets/ui.png +0 -0
- data/examples/collision/locales/en.yml +8 -0
- data/examples/collision/main.rb +316 -0
- data/examples/collision_tiles/locales/en.yml +9 -0
- data/examples/collision_tiles/main.rb +274 -0
- data/examples/fullscreen/locales/en.yml +10 -0
- data/examples/fullscreen/main.rb +216 -0
- data/examples/game_menu/locales/en.yml +8 -0
- data/examples/game_menu/main.rb +170 -0
- data/examples/input_glyphs/locales/en.yml +14 -0
- data/examples/input_glyphs/main.rb +213 -0
- data/examples/jump_topdown/locales/en.yml +9 -0
- data/examples/jump_topdown/main.rb +178 -0
- data/examples/localization/locales/de.yml +12 -0
- data/examples/localization/locales/en.yml +13 -0
- data/examples/localization/main.rb +158 -0
- data/examples/menu_navigation/locales/en.yml +23 -0
- data/examples/menu_navigation/main.rb +365 -0
- data/examples/music/locales/en.yml +7 -0
- data/examples/music/main.rb +134 -0
- data/examples/pathfinding/locales/en.yml +17 -0
- data/examples/pathfinding/main.rb +298 -0
- data/examples/pooling/locales/en.yml +7 -0
- data/examples/pooling/main.rb +259 -0
- data/examples/quick_wheel/locales/en.yml +16 -0
- data/examples/quick_wheel/main.rb +184 -0
- data/examples/radial_menu/locales/en.yml +16 -0
- data/examples/radial_menu/main.rb +184 -0
- data/examples/save_load/locales/en.yml +11 -0
- data/examples/save_load/main.rb +207 -0
- data/examples/save_load_ids/locales/en.yml +11 -0
- data/examples/save_load_ids/main.rb +322 -0
- data/examples/scroll_map/locales/en.yml +4 -0
- data/examples/scroll_map/main.rb +140 -0
- data/examples/signals/locales/en.yml +6 -0
- data/examples/signals/main.rb +278 -0
- data/examples/skill_bar/locales/en.yml +14 -0
- data/examples/skill_bar/main.rb +159 -0
- data/examples/sound/locales/en.yml +6 -0
- data/examples/sound/main.rb +122 -0
- data/examples/split_screen/locales/en.yml +9 -0
- data/examples/split_screen/main.rb +304 -0
- data/examples/sprite/locales/en.yml +8 -0
- data/examples/sprite/main.rb +180 -0
- data/examples/timer/locales/en.yml +12 -0
- data/examples/timer/main.rb +273 -0
- data/examples/velocity/locales/en.yml +6 -0
- data/examples/velocity/main.rb +196 -0
- data/examples/walk/locales/en.yml +4 -0
- data/examples/walk/main.rb +99 -0
- data/exe/rgame +9 -0
- data/ext/README.md +6 -5
- data/ext/rgame_core/app/app.c +215 -11
- data/ext/rgame_core/app/locale.c +67 -0
- data/ext/rgame_core/app/locale.h +28 -0
- data/ext/rgame_core/audio/audio.c +113 -2
- data/ext/rgame_core/example.rb +18 -56
- data/ext/rgame_core/extconf.rb +16 -113
- data/ext/rgame_core/graphics/canvas.c +45 -4
- data/ext/rgame_core/graphics/canvas.h +65 -10
- data/ext/rgame_core/graphics/clip.c +22 -13
- data/ext/rgame_core/include/rgame/core.h +151 -5
- data/ext/rgame_core/input/gamepad.c +57 -3
- data/ext/rgame_core/ruby/audio_ext.c +10 -5
- data/ext/rgame_core/ruby/core_ext.c +46 -7
- data/ext/rgame_core/ruby/core_ext.h +3 -0
- data/ext/rgame_core/ruby/locale_ext.c +44 -0
- data/ext/rgame_core/ruby/recording_ext.c +1 -1
- data/ext/rgame_core/ruby/renderer_ext.c +42 -19
- data/ext/rgame_util/color_ext.c +12 -3
- data/ext/rgame_util/extconf.rb +2 -20
- data/ext/rgame_util/route_search.c +305 -0
- data/ext/rgame_util/route_search.h +86 -0
- data/ext/rgame_util/route_search_ext.c +150 -0
- data/ext/rgame_util/solid_grid.c +58 -0
- data/ext/rgame_util/solid_grid.h +49 -0
- data/ext/rgame_util/solid_grid_ext.c +161 -0
- data/ext/rgame_util/tile_sweep.c +164 -0
- data/ext/rgame_util/tile_sweep.h +62 -0
- data/ext/rgame_util/tile_sweep_ext.c +155 -0
- data/ext/rgame_util/util_ext.c +3 -0
- data/ext/rgame_util/util_ext.h +15 -0
- data/lib/rgame/boot.rb +0 -10
- data/lib/rgame/cli/new_project.rb +139 -0
- data/lib/rgame/cli/templates/Gemfile.tt +23 -0
- data/lib/rgame/cli/templates/README.md.tt +93 -0
- data/lib/rgame/cli/templates/Rakefile.tt +9 -0
- data/lib/rgame/cli/templates/assets/locales/en.yml.tt +10 -0
- data/lib/rgame/cli/templates/game.rb.tt +23 -0
- data/lib/rgame/cli/templates/gitignore.tt +12 -0
- data/lib/rgame/cli/templates/main.rb.tt +11 -0
- data/lib/rgame/cli/templates/nodes/root.rb.tt +24 -0
- data/lib/rgame/cli/templates/rspec.tt +2 -0
- data/lib/rgame/cli/templates/rubocop.yml.tt +75 -0
- data/lib/rgame/cli/templates/ruby-version.tt +1 -0
- data/lib/rgame/cli/templates/spec/locales_spec.rb.tt +18 -0
- data/lib/rgame/cli/templates/spec/nodes/root_spec.rb.tt +18 -0
- data/lib/rgame/cli/templates/spec/spec_helper.rb.tt +37 -0
- data/lib/rgame/cli.rb +66 -0
- data/lib/rgame/core/app.rb +6 -42
- data/lib/rgame/core/asset_manager.rb +13 -31
- data/lib/rgame/core/audio.rb +37 -16
- data/lib/rgame/core/font.rb +0 -3
- data/lib/rgame/core/input.rb +35 -41
- data/lib/rgame/core/locale.rb +22 -0
- data/lib/rgame/core/nine_slice.rb +0 -21
- data/lib/rgame/core/recording.rb +3 -1
- data/lib/rgame/core/renderer.rb +75 -84
- data/lib/rgame/core/sprite_sheet.rb +0 -3
- data/lib/rgame/core/tile_map_renderer.rb +77 -65
- data/lib/rgame/core/ui_atlas.rb +28 -13
- data/lib/rgame/core.rb +1 -8
- data/lib/rgame/engine/actor_blockers.rb +131 -0
- data/lib/rgame/engine/animation_set.rb +1 -0
- data/lib/rgame/engine/audio_director.rb +36 -6
- data/lib/rgame/engine/bounds_blockers.rb +74 -0
- data/lib/rgame/engine/camera.rb +55 -10
- data/lib/rgame/engine/circle_collider.rb +4 -2
- data/lib/rgame/engine/collision_box.rb +26 -1
- data/lib/rgame/engine/collision_system.rb +110 -22
- data/lib/rgame/engine/component.rb +35 -1
- data/lib/rgame/engine/components/action_trigger.rb +0 -1
- data/lib/rgame/engine/components/animated_sprite.rb +31 -23
- data/lib/rgame/engine/components/box_collider.rb +99 -0
- data/lib/rgame/engine/components/camera_follow.rb +45 -0
- data/lib/rgame/engine/components/character_body.rb +21 -41
- data/lib/rgame/engine/components/circle_collider.rb +47 -11
- data/lib/rgame/engine/components/collision_world.rb +159 -31
- data/lib/rgame/engine/components/despawn_offscreen.rb +24 -8
- data/lib/rgame/engine/components/feet_collider.rb +61 -0
- data/lib/rgame/engine/components/hop.rb +76 -0
- data/lib/rgame/engine/components/identity.rb +73 -0
- data/lib/rgame/engine/components/mover.rb +285 -0
- data/lib/rgame/engine/components/navigator.rb +145 -0
- data/lib/rgame/engine/components/path_follow.rb +123 -31
- data/lib/rgame/engine/components/player_controller.rb +5 -2
- data/lib/rgame/engine/components/pool.rb +1 -1
- data/lib/rgame/engine/components/screen_wrap.rb +33 -11
- data/lib/rgame/engine/components/sprite.rb +22 -6
- data/lib/rgame/engine/components/targeting.rb +9 -11
- data/lib/rgame/engine/components/thrust_controller.rb +1 -1
- data/lib/rgame/engine/components/tile_world.rb +70 -29
- data/lib/rgame/engine/components/timer.rb +1 -1
- data/lib/rgame/engine/components/velocity.rb +23 -7
- data/lib/rgame/engine/components/wander_controller.rb +6 -2
- data/lib/rgame/engine/components/world.rb +133 -0
- data/lib/rgame/engine/contact_set.rb +74 -0
- data/lib/rgame/engine/culling.rb +45 -0
- data/lib/rgame/engine/debug_overlay.rb +23 -18
- data/lib/rgame/engine/i18n/plural.rb +45 -0
- data/lib/rgame/engine/i18n/plural_rules.rb +82 -0
- data/lib/rgame/engine/i18n/template.rb +59 -0
- data/lib/rgame/engine/i18n.rb +276 -51
- data/lib/rgame/engine/input/action_mapper.rb +76 -22
- data/lib/rgame/engine/input/actions.rb +63 -12
- data/lib/rgame/engine/input/input_map.rb +196 -0
- data/lib/rgame/engine/layout.rb +82 -0
- data/lib/rgame/engine/nav_grid.rb +87 -0
- data/lib/rgame/engine/node2d.rb +364 -80
- data/lib/rgame/engine/path.rb +4 -6
- data/lib/rgame/engine/player.rb +69 -0
- data/lib/rgame/engine/player_layer.rb +70 -0
- data/lib/rgame/engine/players.rb +205 -0
- data/lib/rgame/engine/presentation.rb +171 -0
- data/lib/rgame/engine/scene/scene_stack.rb +29 -7
- data/lib/rgame/engine/sealed_privates.rb +54 -0
- data/lib/rgame/engine/spatial_hash.rb +53 -8
- data/lib/rgame/engine/text.rb +194 -0
- data/lib/rgame/engine/tile_blockers.rb +63 -0
- data/lib/rgame/engine/tile_map.rb +2 -3
- data/lib/rgame/engine/tile_map_layer.rb +82 -0
- data/lib/rgame/engine/tileset.rb +2 -4
- data/lib/rgame/engine/timer.rb +2 -2
- data/lib/rgame/engine/ui/button.rb +248 -0
- data/lib/rgame/engine/ui/column.rb +20 -0
- data/lib/rgame/engine/ui/icon_button.rb +93 -0
- data/lib/rgame/engine/ui/menu.rb +290 -0
- data/lib/rgame/engine/ui/navigation.rb +57 -0
- data/lib/rgame/engine/ui/nine_slice_style.rb +50 -0
- data/lib/rgame/engine/ui/option_button.rb +163 -0
- data/lib/rgame/engine/ui/panel_button.rb +32 -0
- data/lib/rgame/engine/ui/panel_menu.rb +36 -0
- data/lib/rgame/engine/ui/pointing.rb +146 -0
- data/lib/rgame/engine/ui/radial_menu.rb +85 -0
- data/lib/rgame/engine/ui/ring.rb +55 -0
- data/lib/rgame/engine/ui/row.rb +21 -0
- data/lib/rgame/engine/ui/shape_style.rb +102 -0
- data/lib/rgame/engine/ui/stack.rb +58 -0
- data/lib/rgame/engine/ui/stepping.rb +93 -0
- data/lib/rgame/engine/ui/text_button.rb +59 -0
- data/lib/rgame/engine/view.rb +76 -0
- data/lib/rgame/engine/viewports.rb +171 -0
- data/lib/rgame/engine/world_view.rb +71 -0
- data/lib/rgame/engine.rb +43 -25
- data/lib/rgame/game.rb +164 -22
- data/lib/rgame/rubocop/cop/game/draw_in_local_space.rb +103 -0
- data/lib/rgame/rubocop/cop/game/hot_path.rb +36 -0
- data/lib/rgame/rubocop/cop/game/layer_boundary.rb +43 -0
- data/lib/rgame/rubocop/cop/game/no_core_in_engine_layer.rb +100 -0
- data/lib/rgame/rubocop/cop/game/no_engine_in_core_layer.rb +84 -0
- data/lib/rgame/rubocop/cop/game/no_interpolation_in_hot_path.rb +50 -0
- data/lib/rgame/rubocop/cop/game/no_literal_text.rb +41 -0
- data/lib/rgame/rubocop/cop/game/no_needless_allocation.rb +112 -0
- data/lib/rgame/rubocop/default.yml +39 -0
- data/lib/rgame/rubocop/plugin.rb +45 -0
- data/lib/rgame/rubocop.rb +11 -0
- data/lib/rgame/util/color.rb +20 -24
- data/lib/rgame/util/controls.rb +106 -44
- data/lib/rgame/util/route_search.rb +27 -0
- data/lib/rgame/util/save_file.rb +107 -0
- data/lib/rgame/util/solid_grid.rb +37 -0
- data/lib/rgame/util/tensor.rb +0 -9
- data/lib/rgame/util/tile_sweep.rb +36 -0
- data/lib/rgame/util/z.rb +123 -0
- data/lib/rgame/util.rb +5 -3
- data/lib/rgame/version.rb +1 -1
- data/lib/rgame.rb +0 -15
- metadata +176 -20
- data/lib/rgame/engine/actor.rb +0 -53
- data/lib/rgame/engine/body.rb +0 -49
- data/lib/rgame/engine/cached_label.rb +0 -33
- data/lib/rgame/engine/camera_view.rb +0 -28
- data/lib/rgame/engine/input/player_controller.rb +0 -14
- data/lib/rgame/engine/matrix.rb +0 -32
- data/lib/rgame/engine/resettable.rb +0 -67
- data/lib/rgame/engine/tile_collision.rb +0 -78
|
@@ -19,8 +19,19 @@ extern "C" {
|
|
|
19
19
|
|
|
20
20
|
typedef struct rgame_app rgame_app;
|
|
21
21
|
|
|
22
|
-
/*
|
|
23
|
-
|
|
22
|
+
/*
|
|
23
|
+
* Creates the window, GL context and internal state. Returns NULL on failure.
|
|
24
|
+
*
|
|
25
|
+
* `fullscreen` non-zero opens the window fullscreen straight away, rather than
|
|
26
|
+
* opening it windowed and switching after. The difference is visible: a switch
|
|
27
|
+
* made after creation shows one windowed frame first, which is the flash a
|
|
28
|
+
* player sees when a game starts up wrong.
|
|
29
|
+
*
|
|
30
|
+
* `width` and `height` still matter when starting fullscreen — they are the
|
|
31
|
+
* size the window returns to when it leaves fullscreen, and the size SDL keeps
|
|
32
|
+
* for it in the meantime.
|
|
33
|
+
*/
|
|
34
|
+
rgame_app *rgame_app_create(int width, int height, const char *title, int fullscreen);
|
|
24
35
|
|
|
25
36
|
/* Destroys the GL context/window and frees the app. Safe to call with NULL. */
|
|
26
37
|
void rgame_app_destroy(rgame_app *app);
|
|
@@ -52,16 +63,93 @@ void rgame_app_destroy(rgame_app *app);
|
|
|
52
63
|
#define RGAME_BUTTON_GAMEPAD_FIRST 0x1000
|
|
53
64
|
#define RGAME_BUTTON_GAMEPAD_LAST 0x10FF
|
|
54
65
|
|
|
55
|
-
/* Keyboard ids == SDL scancodes
|
|
56
|
-
*
|
|
66
|
+
/* Keyboard ids == SDL scancodes, which are physical *positions* rather than
|
|
67
|
+
* letters: RGAME_KEY_A is the key marked A on a QWERTY board and Q on AZERTY.
|
|
68
|
+
* The set is what a Western keyboard can be relied on to have. No numpad (most
|
|
69
|
+
* laptops have none), no GUI/Windows/Command key, no print-screen cluster, and
|
|
70
|
+
* nothing whose position depends on the layout. Those can be added when
|
|
71
|
+
* something needs them; each is a #define plus a _Static_assert in app.c. */
|
|
72
|
+
#define RGAME_KEY_A 4
|
|
73
|
+
#define RGAME_KEY_B 5
|
|
74
|
+
#define RGAME_KEY_C 6
|
|
75
|
+
#define RGAME_KEY_D 7
|
|
76
|
+
#define RGAME_KEY_E 8
|
|
77
|
+
#define RGAME_KEY_F 9
|
|
78
|
+
#define RGAME_KEY_G 10
|
|
79
|
+
#define RGAME_KEY_H 11
|
|
80
|
+
#define RGAME_KEY_I 12
|
|
81
|
+
#define RGAME_KEY_J 13
|
|
82
|
+
#define RGAME_KEY_K 14
|
|
83
|
+
#define RGAME_KEY_L 15
|
|
84
|
+
#define RGAME_KEY_M 16
|
|
85
|
+
#define RGAME_KEY_N 17
|
|
86
|
+
#define RGAME_KEY_O 18
|
|
87
|
+
#define RGAME_KEY_P 19
|
|
88
|
+
#define RGAME_KEY_Q 20
|
|
89
|
+
#define RGAME_KEY_R 21
|
|
90
|
+
#define RGAME_KEY_S 22
|
|
91
|
+
#define RGAME_KEY_T 23
|
|
92
|
+
#define RGAME_KEY_U 24
|
|
93
|
+
#define RGAME_KEY_V 25
|
|
94
|
+
#define RGAME_KEY_W 26
|
|
95
|
+
#define RGAME_KEY_X 27
|
|
96
|
+
#define RGAME_KEY_Y 28
|
|
97
|
+
#define RGAME_KEY_Z 29
|
|
98
|
+
#define RGAME_KEY_1 30
|
|
99
|
+
#define RGAME_KEY_2 31
|
|
100
|
+
#define RGAME_KEY_3 32
|
|
101
|
+
#define RGAME_KEY_4 33
|
|
102
|
+
#define RGAME_KEY_5 34
|
|
103
|
+
#define RGAME_KEY_6 35
|
|
104
|
+
#define RGAME_KEY_7 36
|
|
105
|
+
#define RGAME_KEY_8 37
|
|
106
|
+
#define RGAME_KEY_9 38
|
|
107
|
+
#define RGAME_KEY_0 39
|
|
57
108
|
#define RGAME_KEY_RETURN 40
|
|
58
109
|
#define RGAME_KEY_ESCAPE 41
|
|
110
|
+
#define RGAME_KEY_BACKSPACE 42
|
|
111
|
+
#define RGAME_KEY_TAB 43
|
|
59
112
|
#define RGAME_KEY_SPACE 44
|
|
113
|
+
#define RGAME_KEY_MINUS 45
|
|
114
|
+
#define RGAME_KEY_EQUALS 46
|
|
115
|
+
#define RGAME_KEY_LEFTBRACKET 47
|
|
116
|
+
#define RGAME_KEY_RIGHTBRACKET 48
|
|
117
|
+
#define RGAME_KEY_BACKSLASH 49
|
|
118
|
+
#define RGAME_KEY_SEMICOLON 51
|
|
119
|
+
#define RGAME_KEY_APOSTROPHE 52
|
|
120
|
+
#define RGAME_KEY_GRAVE 53
|
|
121
|
+
#define RGAME_KEY_COMMA 54
|
|
122
|
+
#define RGAME_KEY_PERIOD 55
|
|
123
|
+
#define RGAME_KEY_SLASH 56
|
|
124
|
+
#define RGAME_KEY_CAPSLOCK 57
|
|
60
125
|
#define RGAME_KEY_F1 58
|
|
126
|
+
#define RGAME_KEY_F2 59
|
|
127
|
+
#define RGAME_KEY_F3 60
|
|
128
|
+
#define RGAME_KEY_F4 61
|
|
129
|
+
#define RGAME_KEY_F5 62
|
|
130
|
+
#define RGAME_KEY_F6 63
|
|
131
|
+
#define RGAME_KEY_F7 64
|
|
132
|
+
#define RGAME_KEY_F8 65
|
|
133
|
+
#define RGAME_KEY_F9 66
|
|
134
|
+
#define RGAME_KEY_F10 67
|
|
135
|
+
#define RGAME_KEY_F11 68
|
|
136
|
+
#define RGAME_KEY_F12 69
|
|
137
|
+
#define RGAME_KEY_INSERT 73
|
|
138
|
+
#define RGAME_KEY_HOME 74
|
|
139
|
+
#define RGAME_KEY_PAGEUP 75
|
|
140
|
+
#define RGAME_KEY_DELETE 76
|
|
141
|
+
#define RGAME_KEY_END 77
|
|
142
|
+
#define RGAME_KEY_PAGEDOWN 78
|
|
61
143
|
#define RGAME_KEY_RIGHT 79
|
|
62
144
|
#define RGAME_KEY_LEFT 80
|
|
63
145
|
#define RGAME_KEY_DOWN 81
|
|
64
146
|
#define RGAME_KEY_UP 82
|
|
147
|
+
#define RGAME_KEY_LCTRL 224
|
|
148
|
+
#define RGAME_KEY_LSHIFT 225
|
|
149
|
+
#define RGAME_KEY_LALT 226
|
|
150
|
+
#define RGAME_KEY_RCTRL 228
|
|
151
|
+
#define RGAME_KEY_RSHIFT 229
|
|
152
|
+
#define RGAME_KEY_RALT 230
|
|
65
153
|
|
|
66
154
|
/* Gamepad button ids: the gamepad range plus SDL's controller button number.
|
|
67
155
|
* app.c asserts each against the SDL constant it mirrors, as with the keys. */
|
|
@@ -80,6 +168,12 @@ void rgame_app_destroy(rgame_app *app);
|
|
|
80
168
|
#define RGAME_PAD_DPAD_DOWN (RGAME_BUTTON_GAMEPAD_FIRST + 12)
|
|
81
169
|
#define RGAME_PAD_DPAD_LEFT (RGAME_BUTTON_GAMEPAD_FIRST + 13)
|
|
82
170
|
#define RGAME_PAD_DPAD_RIGHT (RGAME_BUTTON_GAMEPAD_FIRST + 14)
|
|
171
|
+
#define RGAME_PAD_MISC1 (RGAME_BUTTON_GAMEPAD_FIRST + 15)
|
|
172
|
+
#define RGAME_PAD_PADDLE1 (RGAME_BUTTON_GAMEPAD_FIRST + 16)
|
|
173
|
+
#define RGAME_PAD_PADDLE2 (RGAME_BUTTON_GAMEPAD_FIRST + 17)
|
|
174
|
+
#define RGAME_PAD_PADDLE3 (RGAME_BUTTON_GAMEPAD_FIRST + 18)
|
|
175
|
+
#define RGAME_PAD_PADDLE4 (RGAME_BUTTON_GAMEPAD_FIRST + 19)
|
|
176
|
+
#define RGAME_PAD_TOUCHPAD (RGAME_BUTTON_GAMEPAD_FIRST + 20)
|
|
83
177
|
|
|
84
178
|
/*
|
|
85
179
|
* Analog axes are their own small id space rather than part of the button
|
|
@@ -121,6 +215,7 @@ void rgame_app_destroy(rgame_app *app);
|
|
|
121
215
|
typedef void (*rgame_frame_begin_fn)(void *userdata);
|
|
122
216
|
typedef void (*rgame_update_fn)(void *userdata, double dt_seconds);
|
|
123
217
|
typedef void (*rgame_draw_fn)(void *userdata);
|
|
218
|
+
typedef void (*rgame_frame_end_fn)(void *userdata);
|
|
124
219
|
typedef int (*rgame_needs_redraw_fn)(void *userdata);
|
|
125
220
|
typedef void (*rgame_button_fn)(void *userdata, int button_id);
|
|
126
221
|
typedef void (*rgame_resize_fn)(void *userdata, int width, int height);
|
|
@@ -141,6 +236,12 @@ typedef void (*rgame_gamepad_fn)(void *userdata, int slot);
|
|
|
141
236
|
* - `needs_redraw` is polled before drawing; returning 0 skips the draw for
|
|
142
237
|
* that frame (simulation still advances). NULL means always redraw.
|
|
143
238
|
* - `draw` renders one frame.
|
|
239
|
+
* - `frame_end` runs once per rendered frame, after that frame's drawing has
|
|
240
|
+
* been submitted to the GPU but *before* the buffer swap — the only point
|
|
241
|
+
* at which the frame just drawn can be read back deterministically. Never
|
|
242
|
+
* called when `needs_redraw` skipped the draw. Rarely needed: it exists for
|
|
243
|
+
* reading pixels back for a test, not for game code, since a real driver is
|
|
244
|
+
* free to swap however it likes once `draw` returns.
|
|
144
245
|
* - `button_down`/`button_up` report discrete key presses and releases. Key
|
|
145
246
|
* repeats are filtered out, so holding a key reports exactly one press.
|
|
146
247
|
* - `resize` reports a new window size; the GL viewport is already updated.
|
|
@@ -154,6 +255,7 @@ typedef struct {
|
|
|
154
255
|
rgame_update_fn update;
|
|
155
256
|
rgame_needs_redraw_fn needs_redraw;
|
|
156
257
|
rgame_draw_fn draw;
|
|
258
|
+
rgame_frame_end_fn frame_end;
|
|
157
259
|
rgame_button_fn button_down;
|
|
158
260
|
rgame_button_fn button_up;
|
|
159
261
|
rgame_resize_fn resize;
|
|
@@ -181,6 +283,19 @@ void rgame_app_close(rgame_app *app);
|
|
|
181
283
|
int rgame_app_width(const rgame_app *app);
|
|
182
284
|
int rgame_app_height(const rgame_app *app);
|
|
183
285
|
|
|
286
|
+
/*
|
|
287
|
+
* Fullscreen, toggled at any time. This is *desktop* fullscreen: the window
|
|
288
|
+
* takes the whole screen at the screen's own resolution, rather than asking the
|
|
289
|
+
* display to change mode. That makes the switch instant and reversible, costs
|
|
290
|
+
* no mode list to choose from, and leaves other windows where they were.
|
|
291
|
+
*
|
|
292
|
+
* Switching resizes the window, so the `resize` callback fires with the new
|
|
293
|
+
* size — the same path a user dragging a window edge takes. Nothing else needs
|
|
294
|
+
* telling.
|
|
295
|
+
*/
|
|
296
|
+
void rgame_app_set_fullscreen(rgame_app *app, int fullscreen);
|
|
297
|
+
int rgame_app_fullscreen(const rgame_app *app);
|
|
298
|
+
|
|
184
299
|
/* Window title. The returned string is owned by the window, not the caller. */
|
|
185
300
|
const char *rgame_app_title(const rgame_app *app);
|
|
186
301
|
void rgame_app_set_title(rgame_app *app, const char *title);
|
|
@@ -226,6 +341,18 @@ unsigned int rgame_app_ticks_ms(const rgame_app *app);
|
|
|
226
341
|
/* Most recent frames-per-second reading (updated ~once per second). */
|
|
227
342
|
double rgame_app_fps(const rgame_app *app);
|
|
228
343
|
|
|
344
|
+
/*
|
|
345
|
+
* The user's preferred locales, most preferred first, written into `out` as
|
|
346
|
+
* "de-AT,en": a language, a hyphen and a country when the OS names one, and
|
|
347
|
+
* no trailing comma. Needs no app.
|
|
348
|
+
*
|
|
349
|
+
* Returns the length of the whole list, not counting the NUL, like snprintf:
|
|
350
|
+
* a result >= `capacity` means `out` holds a truncated prefix, and a buffer of
|
|
351
|
+
* result + 1 bytes fits it. `out` is always NUL-terminated when `capacity` is
|
|
352
|
+
* non-zero. Returns 0, with `out` empty, when the OS reports none.
|
|
353
|
+
*/
|
|
354
|
+
size_t rgame_preferred_locales(char *out, size_t capacity);
|
|
355
|
+
|
|
229
356
|
/*
|
|
230
357
|
* ---------------------------------------------------------------------------
|
|
231
358
|
* Images
|
|
@@ -360,7 +487,7 @@ int rgame_app_draw_image_rot(rgame_app *app, const rgame_image *image, float cx,
|
|
|
360
487
|
float angle_degrees, float scale, unsigned int color, double z);
|
|
361
488
|
|
|
362
489
|
/*
|
|
363
|
-
* The transform and
|
|
490
|
+
* The transform, clip and layer stacks. Every push is undone by the same
|
|
364
491
|
* `rgame_app_pop`, so a caller can never pop the wrong one; a push that cannot
|
|
365
492
|
* be honoured (a full stack) is still counted, so pops stay balanced and the
|
|
366
493
|
* drawing comes out untransformed rather than desynchronised.
|
|
@@ -378,6 +505,25 @@ void rgame_app_push_scale(rgame_app *app, float sx, float sy);
|
|
|
378
505
|
* always succeed as far as the caller is concerned.
|
|
379
506
|
*/
|
|
380
507
|
int rgame_app_push_clip(rgame_app *app, int x, int y, int width, int height);
|
|
508
|
+
|
|
509
|
+
/*
|
|
510
|
+
* The layer stack: what every subsequent `z` is measured from, until the
|
|
511
|
+
* matching pop. It *replaces* rather than accumulates, and starts at 0, so a
|
|
512
|
+
* caller that never pushes one gets exactly the z it passes.
|
|
513
|
+
*
|
|
514
|
+
* `rgame_app_next_layer_slot` is the other half: one counter per band, reset at
|
|
515
|
+
* the start of each frame, handing out increasing indices. It counts and
|
|
516
|
+
* nothing more — what a band means, and how a slot index becomes a base, is
|
|
517
|
+
* decided a layer up (RGame::Util::Z). Bands outside 0..7 answer 0.
|
|
518
|
+
*
|
|
519
|
+
* Together they are how a scene graph gives each node its own narrow window of
|
|
520
|
+
* z values: the traversal takes the next slot as it reaches a node, so draw
|
|
521
|
+
* order is tree order, and a node's `z:` can only reorder its own drawing.
|
|
522
|
+
*/
|
|
523
|
+
void rgame_app_push_layer(rgame_app *app, double base);
|
|
524
|
+
double rgame_app_layer(rgame_app *app);
|
|
525
|
+
unsigned int rgame_app_next_layer_slot(rgame_app *app, int band);
|
|
526
|
+
|
|
381
527
|
void rgame_app_pop(rgame_app *app);
|
|
382
528
|
|
|
383
529
|
/*
|
|
@@ -11,15 +11,69 @@ _Static_assert(RGAME_GAMEPAD_AXIS_COUNT == SDL_CONTROLLER_AXIS_MAX,
|
|
|
11
11
|
|
|
12
12
|
/* Gamepad button ids are the gamepad range plus SDL's own button number, so
|
|
13
13
|
* each named id must line up with the SDL constant it mirrors. */
|
|
14
|
-
_Static_assert(RGAME_PAD_A ==
|
|
14
|
+
_Static_assert(RGAME_PAD_A ==
|
|
15
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_A,
|
|
15
16
|
"pad id must match SDL controller button");
|
|
16
|
-
_Static_assert(
|
|
17
|
+
_Static_assert(RGAME_PAD_B ==
|
|
18
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_B,
|
|
17
19
|
"pad id must match SDL controller button");
|
|
18
|
-
_Static_assert(
|
|
20
|
+
_Static_assert(RGAME_PAD_X ==
|
|
21
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_X,
|
|
22
|
+
"pad id must match SDL controller button");
|
|
23
|
+
_Static_assert(RGAME_PAD_Y ==
|
|
24
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_Y,
|
|
25
|
+
"pad id must match SDL controller button");
|
|
26
|
+
_Static_assert(RGAME_PAD_BACK ==
|
|
27
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_BACK,
|
|
28
|
+
"pad id must match SDL controller button");
|
|
29
|
+
_Static_assert(RGAME_PAD_GUIDE ==
|
|
30
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_GUIDE,
|
|
31
|
+
"pad id must match SDL controller button");
|
|
32
|
+
_Static_assert(RGAME_PAD_START ==
|
|
33
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_START,
|
|
34
|
+
"pad id must match SDL controller button");
|
|
35
|
+
_Static_assert(RGAME_PAD_LEFT_STICK ==
|
|
36
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
|
37
|
+
"pad id must match SDL controller button");
|
|
38
|
+
_Static_assert(RGAME_PAD_RIGHT_STICK ==
|
|
39
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
|
40
|
+
"pad id must match SDL controller button");
|
|
41
|
+
_Static_assert(RGAME_PAD_LEFT_SHOULDER ==
|
|
42
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
|
43
|
+
"pad id must match SDL controller button");
|
|
44
|
+
_Static_assert(RGAME_PAD_RIGHT_SHOULDER ==
|
|
45
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
|
46
|
+
"pad id must match SDL controller button");
|
|
47
|
+
_Static_assert(RGAME_PAD_DPAD_UP ==
|
|
48
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_DPAD_UP,
|
|
49
|
+
"pad id must match SDL controller button");
|
|
50
|
+
_Static_assert(RGAME_PAD_DPAD_DOWN ==
|
|
51
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
|
52
|
+
"pad id must match SDL controller button");
|
|
53
|
+
_Static_assert(RGAME_PAD_DPAD_LEFT ==
|
|
54
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
|
19
55
|
"pad id must match SDL controller button");
|
|
20
56
|
_Static_assert(RGAME_PAD_DPAD_RIGHT ==
|
|
21
57
|
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
|
|
22
58
|
"pad id must match SDL controller button");
|
|
59
|
+
_Static_assert(RGAME_PAD_MISC1 ==
|
|
60
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_MISC1,
|
|
61
|
+
"pad id must match SDL controller button");
|
|
62
|
+
_Static_assert(RGAME_PAD_PADDLE1 ==
|
|
63
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_PADDLE1,
|
|
64
|
+
"pad id must match SDL controller button");
|
|
65
|
+
_Static_assert(RGAME_PAD_PADDLE2 ==
|
|
66
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_PADDLE2,
|
|
67
|
+
"pad id must match SDL controller button");
|
|
68
|
+
_Static_assert(RGAME_PAD_PADDLE3 ==
|
|
69
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_PADDLE3,
|
|
70
|
+
"pad id must match SDL controller button");
|
|
71
|
+
_Static_assert(RGAME_PAD_PADDLE4 ==
|
|
72
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_PADDLE4,
|
|
73
|
+
"pad id must match SDL controller button");
|
|
74
|
+
_Static_assert(RGAME_PAD_TOUCHPAD ==
|
|
75
|
+
RGAME_BUTTON_GAMEPAD_FIRST + SDL_CONTROLLER_BUTTON_TOUCHPAD,
|
|
76
|
+
"pad id must match SDL controller button");
|
|
23
77
|
_Static_assert(RGAME_AXIS_LEFT_X == SDL_CONTROLLER_AXIS_LEFTX, "axis id must match SDL axis");
|
|
24
78
|
_Static_assert(RGAME_AXIS_TRIGGER_RIGHT == SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
|
25
79
|
"axis id must match SDL axis");
|
|
@@ -114,10 +114,15 @@ static VALUE audio_s_debug_live_sounds(VALUE klass) {
|
|
|
114
114
|
* ------------------------------------------------------------------------- */
|
|
115
115
|
|
|
116
116
|
/*
|
|
117
|
-
* Both wrap a C handle plus the Ruby Audio it came from. Marking the device
|
|
118
|
-
*
|
|
119
|
-
* device's mixer, and freeing the mixer out from
|
|
120
|
-
* an unpredictable moment.
|
|
117
|
+
* Both wrap a C handle plus the Ruby Audio it came from. Marking the device
|
|
118
|
+
* keeps it alive for as long as any sound made from it is *reachable*: the
|
|
119
|
+
* sound is a voice inside that device's mixer, and freeing the mixer out from
|
|
120
|
+
* under a live sound would be a crash at an unpredictable moment.
|
|
121
|
+
*
|
|
122
|
+
* It is only half the guarantee, and the half it leaves out is worth knowing.
|
|
123
|
+
* When a sound and its device become garbage in the same collection, marking
|
|
124
|
+
* has nothing left to say and the sweep frees them in whatever order it reaches
|
|
125
|
+
* them. audio.c refcounts the device for that case; see audio_release there.
|
|
121
126
|
*/
|
|
122
127
|
typedef struct {
|
|
123
128
|
void *handle; /* rgame_sample * or rgame_song * */
|
|
@@ -312,7 +317,7 @@ void rgame_init_audio(VALUE mCore) {
|
|
|
312
317
|
rb_define_class_under(cSong, "LoadError", rb_eStandardError);
|
|
313
318
|
rb_define_alloc_func(cSong, song_alloc);
|
|
314
319
|
rb_define_method(cSong, "initialize", song_initialize, 2);
|
|
315
|
-
|
|
320
|
+
rb_define_private_method(cSong, "play_looping", song_play, 1);
|
|
316
321
|
rb_define_method(cSong, "stop", song_stop, 0);
|
|
317
322
|
rb_define_method(cSong, "playing?", song_playing_p, 0);
|
|
318
323
|
rb_define_method(cSong, "looping?", song_looping_p, 0);
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
* def button_down(id); end # discrete key press
|
|
24
24
|
* def button_up(id); end
|
|
25
25
|
* def frame_begin; end # once per frame, before the tick batch
|
|
26
|
+
* def frame_end; end # after draw is submitted, before the swap —
|
|
27
|
+
* # rarely needed; it exists for reading pixels
|
|
28
|
+
* # back in a test, not for game code
|
|
26
29
|
* def resize(w, h); end
|
|
27
30
|
* def gamepad_connected(slot); end
|
|
28
31
|
* def gamepad_disconnected(slot); end
|
|
@@ -47,6 +50,7 @@
|
|
|
47
50
|
static ID id_frame_begin;
|
|
48
51
|
static ID id_update;
|
|
49
52
|
static ID id_draw;
|
|
53
|
+
static ID id_frame_end;
|
|
50
54
|
static ID id_needs_redraw;
|
|
51
55
|
static ID id_button_down;
|
|
52
56
|
static ID id_button_up;
|
|
@@ -57,6 +61,7 @@ static ID id_width;
|
|
|
57
61
|
static ID id_height;
|
|
58
62
|
static ID id_caption;
|
|
59
63
|
static ID id_media_root;
|
|
64
|
+
static ID id_fullscreen;
|
|
60
65
|
|
|
61
66
|
/* ------------------------------------------------------------------------- *
|
|
62
67
|
* rgame_app lifetime, wrapped as a Ruby object
|
|
@@ -106,8 +111,9 @@ static VALUE app_alloc(VALUE klass) {
|
|
|
106
111
|
return TypedData_Wrap_Struct(klass, &app_data_type, NULL);
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
/* App.new(width:, height:, caption:) — keyword
|
|
110
|
-
* subclass's own initialize will forward to via
|
|
114
|
+
/* App.new(width:, height:, caption:, media_root:, fullscreen:) — keyword
|
|
115
|
+
* arguments, matching the shape a subclass's own initialize will forward to via
|
|
116
|
+
* super. The first three are required; the last two have defaults. */
|
|
111
117
|
static VALUE app_initialize(int argc, VALUE *argv, VALUE self) {
|
|
112
118
|
VALUE opts = Qnil;
|
|
113
119
|
rb_scan_args(argc, argv, "0:", &opts); /* no positional args, keywords only */
|
|
@@ -115,14 +121,19 @@ static VALUE app_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
115
121
|
rb_raise(rb_eArgError, "missing keywords: :width, :height, :caption");
|
|
116
122
|
}
|
|
117
123
|
|
|
118
|
-
const ID keys[
|
|
119
|
-
VALUE values[
|
|
120
|
-
/* 3 required,
|
|
124
|
+
const ID keys[5] = { id_width, id_height, id_caption, id_media_root, id_fullscreen };
|
|
125
|
+
VALUE values[5];
|
|
126
|
+
/* 3 required, 2 optional: raises on a missing or unknown keyword. An absent
|
|
121
127
|
* optional comes back as Qundef. */
|
|
122
|
-
rb_get_kwargs(opts, keys, 3,
|
|
128
|
+
rb_get_kwargs(opts, keys, 3, 2, values);
|
|
123
129
|
|
|
130
|
+
/* Passed to create rather than set afterwards, so a game that starts
|
|
131
|
+
* fullscreen never shows a windowed frame first. RTEST treats both nil and
|
|
132
|
+
* false as off, so `fullscreen: settings[:fullscreen]` works with a key that
|
|
133
|
+
* is missing. */
|
|
124
134
|
rgame_app *app = rgame_app_create(NUM2INT(values[0]), NUM2INT(values[1]),
|
|
125
|
-
StringValueCStr(values[2])
|
|
135
|
+
StringValueCStr(values[2]),
|
|
136
|
+
values[4] != Qundef && RTEST(values[4]));
|
|
126
137
|
if (!app) {
|
|
127
138
|
rb_raise(rb_eRuntimeError, "failed to create rgame app");
|
|
128
139
|
}
|
|
@@ -265,6 +276,10 @@ static void tramp_draw(void *userdata) {
|
|
|
265
276
|
protected_call((run_state *)userdata, id_draw, 0, NULL);
|
|
266
277
|
}
|
|
267
278
|
|
|
279
|
+
static void tramp_frame_end(void *userdata) {
|
|
280
|
+
protected_call((run_state *)userdata, id_frame_end, 0, NULL);
|
|
281
|
+
}
|
|
282
|
+
|
|
268
283
|
static int tramp_needs_redraw(void *userdata) {
|
|
269
284
|
VALUE result = protected_call((run_state *)userdata, id_needs_redraw, 0, NULL);
|
|
270
285
|
return RTEST(result) ? 1 : 0;
|
|
@@ -308,6 +323,7 @@ static VALUE app_run(VALUE self) {
|
|
|
308
323
|
.update = tramp_update,
|
|
309
324
|
.needs_redraw = tramp_needs_redraw,
|
|
310
325
|
.draw = tramp_draw,
|
|
326
|
+
.frame_end = tramp_frame_end,
|
|
311
327
|
.button_down = tramp_button_down,
|
|
312
328
|
.button_up = tramp_button_up,
|
|
313
329
|
.resize = tramp_resize,
|
|
@@ -354,6 +370,18 @@ static VALUE app_set_caption(VALUE self, VALUE title) {
|
|
|
354
370
|
return title;
|
|
355
371
|
}
|
|
356
372
|
|
|
373
|
+
static VALUE app_fullscreen_p(VALUE self) {
|
|
374
|
+
return rgame_app_fullscreen(rgame_app_unwrap(self)) ? Qtrue : Qfalse;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* Returns what it was given, as an assignment must in Ruby — so
|
|
378
|
+
* `app.fullscreen = settings.fullscreen?` reads back the value that was set,
|
|
379
|
+
* not the window's state. Read #fullscreen? for that. */
|
|
380
|
+
static VALUE app_set_fullscreen(VALUE self, VALUE on) {
|
|
381
|
+
rgame_app_set_fullscreen(rgame_app_unwrap(self), RTEST(on));
|
|
382
|
+
return on;
|
|
383
|
+
}
|
|
384
|
+
|
|
357
385
|
/*
|
|
358
386
|
* Raw input queries. These take the numeric device and button/axis ids from
|
|
359
387
|
* RGame::Core::Input rather than symbolic action names — turning `:fire` into
|
|
@@ -421,6 +449,11 @@ static VALUE app_default_draw(VALUE self) {
|
|
|
421
449
|
return Qnil;
|
|
422
450
|
}
|
|
423
451
|
|
|
452
|
+
static VALUE app_default_frame_end(VALUE self) {
|
|
453
|
+
(void)self;
|
|
454
|
+
return Qnil;
|
|
455
|
+
}
|
|
456
|
+
|
|
424
457
|
/* Default is "always redraw", matching a NULL needs_redraw in the C API. */
|
|
425
458
|
static VALUE app_default_needs_redraw(VALUE self) {
|
|
426
459
|
(void)self;
|
|
@@ -457,6 +490,7 @@ void Init_core_ext(void) {
|
|
|
457
490
|
id_frame_begin = rb_intern("frame_begin");
|
|
458
491
|
id_update = rb_intern("update");
|
|
459
492
|
id_draw = rb_intern("draw");
|
|
493
|
+
id_frame_end = rb_intern("frame_end");
|
|
460
494
|
id_needs_redraw = rb_intern("needs_redraw?");
|
|
461
495
|
id_button_down = rb_intern("button_down");
|
|
462
496
|
id_button_up = rb_intern("button_up");
|
|
@@ -467,6 +501,7 @@ void Init_core_ext(void) {
|
|
|
467
501
|
id_height = rb_intern("height");
|
|
468
502
|
id_caption = rb_intern("caption");
|
|
469
503
|
id_media_root = rb_intern("media_root");
|
|
504
|
+
id_fullscreen = rb_intern("fullscreen");
|
|
470
505
|
|
|
471
506
|
/*
|
|
472
507
|
* rb_define_module is idempotent — it returns the existing RGame if some
|
|
@@ -486,6 +521,8 @@ void Init_core_ext(void) {
|
|
|
486
521
|
rb_define_method(cApp, "height", app_height, 0);
|
|
487
522
|
rb_define_method(cApp, "caption", app_caption, 0);
|
|
488
523
|
rb_define_method(cApp, "caption=", app_set_caption, 1);
|
|
524
|
+
rb_define_method(cApp, "fullscreen?", app_fullscreen_p, 0);
|
|
525
|
+
rb_define_method(cApp, "fullscreen=", app_set_fullscreen, 1);
|
|
489
526
|
rb_define_method(cApp, "ticks_ms", app_ticks_ms, 0);
|
|
490
527
|
rb_define_method(cApp, "fps", app_fps, 0);
|
|
491
528
|
rb_define_method(cApp, "input_down?", app_input_down_p, 2);
|
|
@@ -498,6 +535,7 @@ void Init_core_ext(void) {
|
|
|
498
535
|
rb_define_method(cApp, "frame_begin", app_default_frame_begin, 0);
|
|
499
536
|
rb_define_method(cApp, "update", app_default_update, 1);
|
|
500
537
|
rb_define_method(cApp, "draw", app_default_draw, 0);
|
|
538
|
+
rb_define_method(cApp, "frame_end", app_default_frame_end, 0);
|
|
501
539
|
rb_define_method(cApp, "needs_redraw?", app_default_needs_redraw, 0);
|
|
502
540
|
rb_define_method(cApp, "button_down", app_default_button, 1);
|
|
503
541
|
rb_define_method(cApp, "button_up", app_default_button, 1);
|
|
@@ -510,4 +548,5 @@ void Init_core_ext(void) {
|
|
|
510
548
|
rgame_init_recording(mCore);
|
|
511
549
|
rgame_init_font(mCore);
|
|
512
550
|
rgame_init_audio(mCore);
|
|
551
|
+
rgame_init_locale(mCore);
|
|
513
552
|
}
|
|
@@ -21,6 +21,9 @@ void rgame_init_renderer(VALUE mCore);
|
|
|
21
21
|
void rgame_init_recording(VALUE mCore);
|
|
22
22
|
void rgame_init_font(VALUE mCore);
|
|
23
23
|
|
|
24
|
+
/* Not a class: RGame::Core.preferred_locales, a module function. */
|
|
25
|
+
void rgame_init_locale(VALUE mCore);
|
|
26
|
+
|
|
24
27
|
/* Defines Audio, Sample and Song together — see audio_ext.c for why those three
|
|
25
28
|
* share a file when everything else here does not. */
|
|
26
29
|
void rgame_init_audio(VALUE mCore);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* locale_ext.c — the Ruby binding for RGame::Core.preferred_locales.
|
|
3
|
+
*
|
|
4
|
+
* RGame::Core.preferred_locales # => ["de-AT", "en"]
|
|
5
|
+
*
|
|
6
|
+
* A module function rather than an App method because SDL answers without an
|
|
7
|
+
* app, or even SDL_Init. The reading is locale.c; this file only sizes a buffer
|
|
8
|
+
* and splits the list. Choosing a language from the answer is
|
|
9
|
+
* RGame::Engine::I18n.choose, which the glue calls.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "ruby/core_ext.h"
|
|
13
|
+
|
|
14
|
+
#include "rgame/core.h"
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* The user's preferred locales as Strings, most preferred first, or [] when the
|
|
18
|
+
* OS names none. Never contains nil or an empty String.
|
|
19
|
+
*/
|
|
20
|
+
static VALUE core_preferred_locales(VALUE self) {
|
|
21
|
+
(void)self;
|
|
22
|
+
|
|
23
|
+
char stack[256];
|
|
24
|
+
size_t length = rgame_preferred_locales(stack, sizeof stack);
|
|
25
|
+
if (length < sizeof stack) {
|
|
26
|
+
return rb_str_split(rb_utf8_str_new(stack, (long)length), ",");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Too long for the stack. The environment is read on every call, so the
|
|
30
|
+
* list can grow between two of them: size again until what was written fits. */
|
|
31
|
+
for (;;) {
|
|
32
|
+
VALUE list = rb_utf8_str_new(NULL, (long)length);
|
|
33
|
+
size_t written = rgame_preferred_locales(RSTRING_PTR(list), length + 1);
|
|
34
|
+
if (written <= length) {
|
|
35
|
+
rb_str_set_len(list, (long)written);
|
|
36
|
+
return rb_str_split(list, ",");
|
|
37
|
+
}
|
|
38
|
+
length = written;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void rgame_init_locale(VALUE mCore) {
|
|
43
|
+
rb_define_module_function(mCore, "preferred_locales", core_preferred_locales, 0);
|
|
44
|
+
}
|
|
@@ -176,7 +176,7 @@ void rgame_init_recording(VALUE mCore) {
|
|
|
176
176
|
|
|
177
177
|
rb_define_alloc_func(cRecording, recording_alloc);
|
|
178
178
|
rb_define_singleton_method(cRecording, "new", recording_s_new, -1);
|
|
179
|
-
|
|
179
|
+
rb_define_private_method(cRecording, "draw_at", recording_draw_at, 4);
|
|
180
180
|
rb_define_method(cRecording, "batch_count", recording_batch_count, 0);
|
|
181
181
|
rb_define_method(cRecording, "vertex_count", recording_vertex_count, 0);
|
|
182
182
|
rb_define_method(cRecording, "empty?", recording_empty_p, 0);
|
|
@@ -297,6 +297,26 @@ static VALUE renderer_push_clip(VALUE self, VALUE x, VALUE y, VALUE width, VALUE
|
|
|
297
297
|
return self;
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/*
|
|
301
|
+
* #push_layer(base) / #layer — the base every subsequent z is measured from.
|
|
302
|
+
*
|
|
303
|
+
* `base` is a whole number well inside a double's exact range (see
|
|
304
|
+
* RGame::Util::Z), so it crosses as a double and comes back as a Float.
|
|
305
|
+
*/
|
|
306
|
+
static VALUE renderer_push_layer(VALUE self, VALUE base) {
|
|
307
|
+
rgame_app_push_layer(drawing_app(self), NUM2DBL(base));
|
|
308
|
+
return self;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static VALUE renderer_layer(VALUE self) {
|
|
312
|
+
return DBL2NUM(rgame_app_layer(drawing_app(self)));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* #next_layer_slot(band) -> the next slot index in that band this frame. */
|
|
316
|
+
static VALUE renderer_next_layer_slot(VALUE self, VALUE band) {
|
|
317
|
+
return UINT2NUM(rgame_app_next_layer_slot(drawing_app(self), NUM2INT(band)));
|
|
318
|
+
}
|
|
319
|
+
|
|
300
320
|
/* ------------------------------------------------------------------------- *
|
|
301
321
|
* Recording
|
|
302
322
|
*
|
|
@@ -354,23 +374,26 @@ void rgame_init_renderer(VALUE mCore) {
|
|
|
354
374
|
rb_define_method(cRenderer, "drawing?", renderer_drawing_p, 0);
|
|
355
375
|
rb_define_method(cRenderer, "app", renderer_app, 0);
|
|
356
376
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
rb_define_private_method(cRenderer, "draw_rect", renderer_draw_rect, 6);
|
|
378
|
+
rb_define_private_method(cRenderer, "draw_quad", renderer_draw_quad, -1);
|
|
379
|
+
rb_define_private_method(cRenderer, "draw_triangle", renderer_draw_triangle, -1);
|
|
380
|
+
rb_define_private_method(cRenderer, "draw_line", renderer_draw_line, -1);
|
|
381
|
+
rb_define_private_method(cRenderer, "draw_circle", renderer_draw_circle, -1);
|
|
382
|
+
rb_define_private_method(cRenderer, "draw_image", renderer_draw_image, 5);
|
|
383
|
+
rb_define_private_method(cRenderer, "draw_image_scaled", renderer_draw_image_scaled, -1);
|
|
384
|
+
rb_define_private_method(cRenderer, "draw_image_rot", renderer_draw_image_rot, -1);
|
|
385
|
+
rb_define_private_method(cRenderer, "draw_text", renderer_draw_text, -1);
|
|
386
|
+
|
|
387
|
+
rb_define_private_method(cRenderer, "push_translate", renderer_push_translate, 2);
|
|
388
|
+
rb_define_private_method(cRenderer, "push_rotate", renderer_push_rotate, 3);
|
|
389
|
+
rb_define_private_method(cRenderer, "push_scale", renderer_push_scale, 2);
|
|
390
|
+
rb_define_private_method(cRenderer, "push_clip", renderer_push_clip, 4);
|
|
391
|
+
rb_define_private_method(cRenderer, "push_layer", renderer_push_layer, 1);
|
|
392
|
+
rb_define_method(cRenderer, "layer", renderer_layer, 0);
|
|
393
|
+
rb_define_private_method(cRenderer, "next_layer_slot", renderer_next_layer_slot, 1);
|
|
394
|
+
rb_define_private_method(cRenderer, "pop", renderer_pop, 0);
|
|
395
|
+
|
|
396
|
+
rb_define_private_method(cRenderer, "begin_record", renderer_begin_record, 0);
|
|
397
|
+
rb_define_private_method(cRenderer, "end_record", renderer_end_record, 0);
|
|
398
|
+
rb_define_private_method(cRenderer, "cancel_record", renderer_cancel_record, 0);
|
|
376
399
|
}
|
data/ext/rgame_util/color_ext.c
CHANGED
|
@@ -63,10 +63,19 @@ static VALUE color_s_new(int argc, VALUE *argv, VALUE klass) {
|
|
|
63
63
|
NIL_P(a) ? 255 : component(a, "alpha")));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/* Color.from_packed(0xRRGGBBAA)
|
|
66
|
+
/* Color.from_packed(0xRRGGBBAA)
|
|
67
|
+
*
|
|
68
|
+
* The bounds check below needs a C type wider than 32 bits so a
|
|
69
|
+
* one-bit-too-wide value can be caught rather than silently truncated.
|
|
70
|
+
* `unsigned long` is that on Linux/macOS (LP64: 64 bits) but not on Windows
|
|
71
|
+
* (LLP64: `long` stays 32 bits even in a 64-bit build), where NUM2ULONG would
|
|
72
|
+
* itself raise RangeError on the very value this function means to turn into
|
|
73
|
+
* a clean ArgumentError. NUM2ULL is unsigned long long, 64 bits on every
|
|
74
|
+
* platform this project builds for, so the check below runs before either
|
|
75
|
+
* platform's integer width can get in the way. */
|
|
67
76
|
static VALUE color_s_from_packed(VALUE klass, VALUE packed) {
|
|
68
|
-
unsigned long value =
|
|
69
|
-
if (value >
|
|
77
|
+
unsigned long long value = NUM2ULL(packed);
|
|
78
|
+
if (value > 0xFFFFFFFFull) {
|
|
70
79
|
rb_raise(rb_eArgError, "packed colour must fit in 32 bits");
|
|
71
80
|
}
|
|
72
81
|
return color_wrap(klass, (rgame_color)value);
|