gosu 0.7.10.1

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 (249) hide show
  1. data/COPYING.txt +29 -0
  2. data/Gosu/Async.hpp +48 -0
  3. data/Gosu/Audio.hpp +145 -0
  4. data/Gosu/AutoLink.hpp +16 -0
  5. data/Gosu/Bitmap.hpp +85 -0
  6. data/Gosu/ButtonsMac.hpp +114 -0
  7. data/Gosu/ButtonsWin.hpp +111 -0
  8. data/Gosu/ButtonsX.hpp +115 -0
  9. data/Gosu/Color.hpp +172 -0
  10. data/Gosu/Directories.hpp +36 -0
  11. data/Gosu/Font.hpp +59 -0
  12. data/Gosu/Fwd.hpp +31 -0
  13. data/Gosu/Gosu.hpp +26 -0
  14. data/Gosu/Graphics.hpp +86 -0
  15. data/Gosu/GraphicsBase.hpp +45 -0
  16. data/Gosu/IO.hpp +255 -0
  17. data/Gosu/Image.hpp +148 -0
  18. data/Gosu/ImageData.hpp +45 -0
  19. data/Gosu/Input.hpp +116 -0
  20. data/Gosu/Math.hpp +95 -0
  21. data/Gosu/Platform.hpp +61 -0
  22. data/Gosu/RotFlip.hpp +116 -0
  23. data/Gosu/Sockets.hpp +129 -0
  24. data/Gosu/Text.hpp +47 -0
  25. data/Gosu/TextInput.hpp +57 -0
  26. data/Gosu/Timing.hpp +16 -0
  27. data/Gosu/Utility.hpp +24 -0
  28. data/Gosu/WinUtility.hpp +76 -0
  29. data/Gosu/Window.hpp +84 -0
  30. data/GosuImpl/Async.cpp +37 -0
  31. data/GosuImpl/AudioFmod.cpp +417 -0
  32. data/GosuImpl/AudioSDL.cpp +255 -0
  33. data/GosuImpl/DirectoriesMac.mm +38 -0
  34. data/GosuImpl/DirectoriesUnix.cpp +48 -0
  35. data/GosuImpl/DirectoriesWin.cpp +42 -0
  36. data/GosuImpl/FileUnix.cpp +100 -0
  37. data/GosuImpl/FileWin.cpp +83 -0
  38. data/GosuImpl/Graphics/Bitmap.cpp +116 -0
  39. data/GosuImpl/Graphics/BitmapBMP.cpp +232 -0
  40. data/GosuImpl/Graphics/BitmapColorKey.cpp +39 -0
  41. data/GosuImpl/Graphics/BitmapPNG.cpp +276 -0
  42. data/GosuImpl/Graphics/BitmapUtils.cpp +67 -0
  43. data/GosuImpl/Graphics/BlockAllocator.cpp +127 -0
  44. data/GosuImpl/Graphics/BlockAllocator.hpp +34 -0
  45. data/GosuImpl/Graphics/Color.cpp +126 -0
  46. data/GosuImpl/Graphics/Common.hpp +21 -0
  47. data/GosuImpl/Graphics/DrawOp.hpp +154 -0
  48. data/GosuImpl/Graphics/Font.cpp +110 -0
  49. data/GosuImpl/Graphics/Graphics.cpp +295 -0
  50. data/GosuImpl/Graphics/Image.cpp +159 -0
  51. data/GosuImpl/Graphics/LargeImageData.cpp +115 -0
  52. data/GosuImpl/Graphics/LargeImageData.hpp +37 -0
  53. data/GosuImpl/Graphics/RotFlip.cpp +184 -0
  54. data/GosuImpl/Graphics/TexChunk.cpp +77 -0
  55. data/GosuImpl/Graphics/TexChunk.hpp +40 -0
  56. data/GosuImpl/Graphics/Text.cpp +223 -0
  57. data/GosuImpl/Graphics/TextMac.cpp +242 -0
  58. data/GosuImpl/Graphics/TextPangoFT.cpp +186 -0
  59. data/GosuImpl/Graphics/TextWin.cpp +172 -0
  60. data/GosuImpl/Graphics/Texture.cpp +104 -0
  61. data/GosuImpl/Graphics/Texture.hpp +34 -0
  62. data/GosuImpl/IO.cpp +48 -0
  63. data/GosuImpl/InputMac.mm +677 -0
  64. data/GosuImpl/InputWin.cpp +444 -0
  65. data/GosuImpl/InputX.cpp +158 -0
  66. data/GosuImpl/MacUtility.hpp +48 -0
  67. data/GosuImpl/Math.cpp +49 -0
  68. data/GosuImpl/RubyGosu.swg +474 -0
  69. data/GosuImpl/RubyGosuStub.mm +17 -0
  70. data/GosuImpl/RubyGosu_DllMain.cxx +30 -0
  71. data/GosuImpl/RubyGosu_wrap.cxx +8521 -0
  72. data/GosuImpl/RubyGosu_wrap.h +31 -0
  73. data/GosuImpl/Sockets/CommSocket.cpp +304 -0
  74. data/GosuImpl/Sockets/ListenerSocket.cpp +60 -0
  75. data/GosuImpl/Sockets/MessageSocket.cpp +136 -0
  76. data/GosuImpl/Sockets/Socket.cpp +145 -0
  77. data/GosuImpl/Sockets/Sockets.hpp +66 -0
  78. data/GosuImpl/TextInputMac.mm +207 -0
  79. data/GosuImpl/TextInputWin.cpp +197 -0
  80. data/GosuImpl/TextInputX.cpp +201 -0
  81. data/GosuImpl/TextTTFWin.cpp +247 -0
  82. data/GosuImpl/TimingUnix.cpp +17 -0
  83. data/GosuImpl/TimingWin.cpp +28 -0
  84. data/GosuImpl/Utility.cpp +140 -0
  85. data/GosuImpl/WinMain.cpp +69 -0
  86. data/GosuImpl/WinUtility.cpp +137 -0
  87. data/GosuImpl/WindowMac.mm +466 -0
  88. data/GosuImpl/WindowWin.cpp +447 -0
  89. data/GosuImpl/WindowX.cpp +392 -0
  90. data/GosuImpl/X11vroot.h +118 -0
  91. data/README.txt +13 -0
  92. data/Rakefile +178 -0
  93. data/examples/ChipmunkIntegration.rb +275 -0
  94. data/examples/CptnRuby.rb +231 -0
  95. data/examples/MoreChipmunkAndRMagick.rb +155 -0
  96. data/examples/OpenGLIntegration.rb +232 -0
  97. data/examples/RMagickIntegration.rb +449 -0
  98. data/examples/TextInput.cpp +170 -0
  99. data/examples/TextInput.rb +139 -0
  100. data/examples/Tutorial.cpp +215 -0
  101. data/examples/Tutorial.rb +137 -0
  102. data/examples/media/Beep.wav +0 -0
  103. data/examples/media/CptnRuby Gem.png +0 -0
  104. data/examples/media/CptnRuby Map.txt +25 -0
  105. data/examples/media/CptnRuby Tileset.png +0 -0
  106. data/examples/media/CptnRuby.png +0 -0
  107. data/examples/media/Cursor.png +0 -0
  108. data/examples/media/Earth.png +0 -0
  109. data/examples/media/Explosion.wav +0 -0
  110. data/examples/media/LargeStar.png +0 -0
  111. data/examples/media/Sky.jpg +0 -0
  112. data/examples/media/Smoke.png +0 -0
  113. data/examples/media/Soldier.png +0 -0
  114. data/examples/media/Space.png +0 -0
  115. data/examples/media/Star.png +0 -0
  116. data/examples/media/Starfighter.bmp +0 -0
  117. data/linux/Makefile.in +98 -0
  118. data/linux/configure +5658 -0
  119. data/linux/configure.ac +126 -0
  120. data/linux/extconf.rb +11 -0
  121. data/mac/English.lproj/InfoPlist.strings +0 -0
  122. data/mac/Gosu-Info.plist +26 -0
  123. data/mac/Gosu.xcodeproj/project.pbxproj +1194 -0
  124. data/mac/RubyGosu Template-Info.plist +26 -0
  125. data/mac/libboost_thread_1_34_1_universal.a +0 -0
  126. data/mac/libboost_thread_d_1_34_1_universal.a +0 -0
  127. data/mac/libfmod_universal.a +0 -0
  128. data/mac/libpng_universal.a +0 -0
  129. data/mac/libz_universal.a +0 -0
  130. data/reference/Async_8hpp-source.html +70 -0
  131. data/reference/Audio_8hpp-source.html +114 -0
  132. data/reference/Audio_8hpp.html +50 -0
  133. data/reference/AutoLink_8hpp-source.html +38 -0
  134. data/reference/AutoLink_8hpp.html +34 -0
  135. data/reference/Bitmap_8hpp-source.html +85 -0
  136. data/reference/Bitmap_8hpp.html +58 -0
  137. data/reference/ButtonsMac_8hpp-source.html +133 -0
  138. data/reference/ButtonsWin_8hpp-source.html +133 -0
  139. data/reference/ButtonsX_8hpp-source.html +134 -0
  140. data/reference/Color_8hpp-source.html +169 -0
  141. data/reference/Color_8hpp.html +85 -0
  142. data/reference/Directories_8hpp-source.html +42 -0
  143. data/reference/Directories_8hpp.html +46 -0
  144. data/reference/Font_8hpp-source.html +65 -0
  145. data/reference/Font_8hpp.html +41 -0
  146. data/reference/Fwd_8hpp-source.html +52 -0
  147. data/reference/Fwd_8hpp.html +37 -0
  148. data/reference/Gosu_8hpp-source.html +48 -0
  149. data/reference/Gosu_8hpp.html +34 -0
  150. data/reference/GraphicsBase_8hpp-source.html +57 -0
  151. data/reference/GraphicsBase_8hpp.html +56 -0
  152. data/reference/Graphics_8hpp-source.html +96 -0
  153. data/reference/Graphics_8hpp.html +53 -0
  154. data/reference/IO_8hpp-source.html +255 -0
  155. data/reference/IO_8hpp.html +74 -0
  156. data/reference/ImageData_8hpp-source.html +62 -0
  157. data/reference/ImageData_8hpp.html +43 -0
  158. data/reference/Image_8hpp-source.html +126 -0
  159. data/reference/Image_8hpp.html +48 -0
  160. data/reference/Input_8hpp-source.html +118 -0
  161. data/reference/Input_8hpp.html +50 -0
  162. data/reference/Math_8hpp-source.html +92 -0
  163. data/reference/Math_8hpp.html +74 -0
  164. data/reference/Platform_8hpp-source.html +83 -0
  165. data/reference/Platform_8hpp.html +73 -0
  166. data/reference/RotFlip_8hpp-source.html +138 -0
  167. data/reference/RotFlip_8hpp.html +77 -0
  168. data/reference/Sockets_8hpp-source.html +130 -0
  169. data/reference/Sockets_8hpp.html +66 -0
  170. data/reference/TextInput_8hpp-source.html +64 -0
  171. data/reference/TextInput_8hpp.html +41 -0
  172. data/reference/Text_8hpp-source.html +51 -0
  173. data/reference/Text_8hpp.html +46 -0
  174. data/reference/Timing_8hpp-source.html +36 -0
  175. data/reference/Timing_8hpp.html +42 -0
  176. data/reference/Utility_8hpp-source.html +44 -0
  177. data/reference/Utility_8hpp.html +48 -0
  178. data/reference/WinUtility_8hpp-source.html +79 -0
  179. data/reference/WinUtility_8hpp.html +64 -0
  180. data/reference/Window_8hpp-source.html +91 -0
  181. data/reference/Window_8hpp.html +41 -0
  182. data/reference/annotated.html +51 -0
  183. data/reference/classGosu_1_1Audio-members.html +34 -0
  184. data/reference/classGosu_1_1Audio.html +46 -0
  185. data/reference/classGosu_1_1Bitmap-members.html +44 -0
  186. data/reference/classGosu_1_1Bitmap.html +263 -0
  187. data/reference/classGosu_1_1Buffer-members.html +44 -0
  188. data/reference/classGosu_1_1Buffer.html +78 -0
  189. data/reference/classGosu_1_1Buffer.png +0 -0
  190. data/reference/classGosu_1_1Button-members.html +36 -0
  191. data/reference/classGosu_1_1Button.html +143 -0
  192. data/reference/classGosu_1_1Color-members.html +56 -0
  193. data/reference/classGosu_1_1Color.html +387 -0
  194. data/reference/classGosu_1_1File-members.html +41 -0
  195. data/reference/classGosu_1_1File.html +69 -0
  196. data/reference/classGosu_1_1File.png +0 -0
  197. data/reference/classGosu_1_1Font-members.html +39 -0
  198. data/reference/classGosu_1_1Font.html +309 -0
  199. data/reference/classGosu_1_1Graphics-members.html +50 -0
  200. data/reference/classGosu_1_1Graphics.html +234 -0
  201. data/reference/classGosu_1_1Image-members.html +45 -0
  202. data/reference/classGosu_1_1Image.html +518 -0
  203. data/reference/classGosu_1_1ImageData-members.html +37 -0
  204. data/reference/classGosu_1_1ImageData.html +60 -0
  205. data/reference/classGosu_1_1Input-members.html +44 -0
  206. data/reference/classGosu_1_1Input.html +223 -0
  207. data/reference/classGosu_1_1MessageSocket-members.html +40 -0
  208. data/reference/classGosu_1_1MessageSocket.html +233 -0
  209. data/reference/classGosu_1_1Resource-members.html +39 -0
  210. data/reference/classGosu_1_1Resource.html +116 -0
  211. data/reference/classGosu_1_1Resource.png +0 -0
  212. data/reference/classGosu_1_1Sample-members.html +37 -0
  213. data/reference/classGosu_1_1Sample.html +200 -0
  214. data/reference/classGosu_1_1SampleInstance-members.html +38 -0
  215. data/reference/classGosu_1_1SampleInstance.html +169 -0
  216. data/reference/classGosu_1_1Song-members.html +43 -0
  217. data/reference/classGosu_1_1Song.html +260 -0
  218. data/reference/classGosu_1_1TextInput-members.html +38 -0
  219. data/reference/classGosu_1_1TextInput.html +121 -0
  220. data/reference/classGosu_1_1Window-members.html +50 -0
  221. data/reference/classGosu_1_1Window.html +271 -0
  222. data/reference/doxyfile +233 -0
  223. data/reference/doxygen.css +433 -0
  224. data/reference/doxygen.png +0 -0
  225. data/reference/files.html +54 -0
  226. data/reference/functions.html +236 -0
  227. data/reference/functions_enum.html +45 -0
  228. data/reference/functions_func.html +227 -0
  229. data/reference/functions_vars.html +47 -0
  230. data/reference/hierarchy.html +53 -0
  231. data/reference/index.html +26 -0
  232. data/reference/namespaceGosu.html +2890 -0
  233. data/reference/namespaceGosu_1_1Colors.html +70 -0
  234. data/reference/namespaceGosu_1_1Win.html +275 -0
  235. data/reference/namespacemembers.html +216 -0
  236. data/reference/namespacemembers_enum.html +52 -0
  237. data/reference/namespacemembers_eval.html +54 -0
  238. data/reference/namespacemembers_func.html +185 -0
  239. data/reference/namespacemembers_type.html +46 -0
  240. data/reference/namespacemembers_vars.html +46 -0
  241. data/reference/namespaces.html +35 -0
  242. data/reference/tab_b.gif +0 -0
  243. data/reference/tab_l.gif +0 -0
  244. data/reference/tab_r.gif +0 -0
  245. data/reference/tabs.css +102 -0
  246. data/windows/Gosu.sln +29 -0
  247. data/windows/Gosu.vcproj +553 -0
  248. data/windows/RubyGosu.vcproj +138 -0
  249. metadata +305 -0
@@ -0,0 +1,449 @@
1
+ # A (too) simple Gorilla-style shooter for two players.
2
+ # Shows how Gosu and RMagick can be used together to generate a map, implement
3
+ # a dynamic landscape and generally look great.
4
+ # Also shows a very minimal, yet effective way of designing a game's object system.
5
+
6
+ # Doesn't make use of Gosu's Z-ordering. Not many different things to draw, it's
7
+ # easy to get the order right without it.
8
+
9
+ # Known issues:
10
+ # * Collision detection of the missiles is lazy, allows shooting through thin walls.
11
+ # * The look of dead soldiers is, err, by accident. Soldier.png needs to be
12
+ # designed in a less obfuscated way :)
13
+
14
+ begin
15
+ # In case you use Gosu via RubyGems.
16
+ require 'rubygems'
17
+ rescue LoadError
18
+ # In case you don't.
19
+ end
20
+
21
+ require 'gosu'
22
+ require 'RMagick'
23
+
24
+ NULL_PIXEL = Magick::Pixel.from_color('none')
25
+
26
+ # The class for this game's map.
27
+ # Design:
28
+ # * Dynamic map creation at startup, holding it as RMagick Image in @image
29
+ # * Testing for solidity by testing @image's pixel values
30
+ # * Drawing by (re)creating an array of Gosu::Image instances, each representing
31
+ # a part of the large @image
32
+ # * Blasting holes into the map is implemented by drawing and erasing portions
33
+ # of @image, then setting the corresponding Gosu::Image instances to nil, so
34
+ # they will be recreated in Map#draw
35
+ # Note: The splitting is done because recreating such a large Gosu::Image for
36
+ # every map change would be a very noticeable delay!
37
+
38
+ class Map
39
+ WIDTH, HEIGHT = 800, 600
40
+ TILE_SIZE = 100
41
+ TILES_X = WIDTH / TILE_SIZE
42
+ TILES_Y = HEIGHT / TILE_SIZE
43
+
44
+ def initialize(window)
45
+ # We'll need the window later for re-creating Gosu images.
46
+ @window = window
47
+
48
+ # Let's start with something simple and load the sky via RMagick.
49
+ # Loading JPEG files isn't possible with Gosu, so say wow!
50
+ sky = Magick::Image.read("media/Sky.jpg").first
51
+ @sky = Gosu::Image.new(window, sky, true)
52
+
53
+ # This is the one large RMagick image that represents the map.
54
+ @image = Magick::Image.new(WIDTH, HEIGHT) { self.background_color = 'none' }
55
+
56
+ # Set up a Draw object that fills with an earth texture.
57
+ earth = Magick::Image.read('media/Earth.png').first.resize(1.5)
58
+ gc = Magick::Draw.new
59
+ gc.pattern('earth', 0, 0, earth.columns, earth.rows) { gc.composite(0, 0, 0, 0, earth) }
60
+ gc.fill('earth')
61
+ gc.stroke('#603000').stroke_width(1.5)
62
+ # Draw a smooth bezier island onto the map!
63
+ polypoints = [0, HEIGHT]
64
+ 0.upto(TILES_X) do |x|
65
+ polypoints += [x * TILE_SIZE, HEIGHT * 0.2 + rand(HEIGHT * 0.8)]
66
+ end
67
+ polypoints += [WIDTH, HEIGHT]
68
+ gc.bezier(*polypoints)
69
+ gc.draw(@image)
70
+
71
+ # Create a bright-dark gradient fill, an image from it and change the map's
72
+ # brightness with it.
73
+ fill = Magick::GradientFill.new(0, HEIGHT * 0.4, WIDTH, HEIGHT * 0.4, '#fff', '#666')
74
+ gradient = Magick::Image.new(WIDTH, HEIGHT, fill)
75
+ gradient = @image.composite(gradient, 0, 0, Magick::InCompositeOp)
76
+ @image.composite!(gradient, 0, 0, Magick::MultiplyCompositeOp)
77
+
78
+ # Finally, place the star in the middle of the map, just onto the ground.
79
+ star = Magick::Image.read('media/LargeStar.png').first
80
+ star_y = 0
81
+ star_y += 20 until solid?(WIDTH / 2, star_y)
82
+ @image.composite!(star, (WIDTH - star.columns) / 2, star_y - star.rows * 0.85,
83
+ Magick::DstOverCompositeOp)
84
+
85
+ # Creates an X*Y array for the Gosu images.
86
+ # (Initialized to nil automatically).
87
+ @gosu_images = Array.new(TILES_X) { Array.new(TILES_Y) }
88
+ end
89
+
90
+ def solid?(x, y)
91
+ # Map is open at the top.
92
+ return false if y < 0
93
+ # Map is closed on all other sides.
94
+ return true if x < 0 or x >= 800 or y >= 600
95
+ # Inside of the map, determine solidity from the map image.
96
+ @image.pixel_color(x, y) != NULL_PIXEL
97
+ end
98
+
99
+ def draw
100
+ # Sky background.
101
+ @sky.draw(0, 0, 0)
102
+ # All the tiles.
103
+ TILES_Y.times do |y|
104
+ TILES_X.times do |x|
105
+ # Recreate images that haven't been created yet, or need to be recreated
106
+ # due to map changes.
107
+ if @gosu_images[x][y].nil? then
108
+ part = @image.crop(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
109
+ @gosu_images[x][y] = Gosu::Image.new(@window, part, true)
110
+ end
111
+ # At last - draw it!
112
+ @gosu_images[x][y].draw(x * TILE_SIZE, y * TILE_SIZE, 0)
113
+ end
114
+ end
115
+ end
116
+
117
+ # Radius of a crater.
118
+ RADIUS = 25
119
+ # Radius of a crater, SHadow included.
120
+ SH_RADIUS = 45
121
+
122
+ def blast(x, y)
123
+ # This code assumes at most 2x2 tiles are affected by a blast, so
124
+ # don't change the RADIUS to 200 or something ;)
125
+ # Calculate the x/y indices of the two to four affected tiles.
126
+ # (left/right and top/bottom might be the same).
127
+
128
+ left = (x - SH_RADIUS) / TILE_SIZE
129
+ right = (x + SH_RADIUS) / TILE_SIZE
130
+ top = (y - SH_RADIUS) / TILE_SIZE
131
+ bottom = (y + SH_RADIUS) / TILE_SIZE
132
+
133
+ # Set affected images to nil.
134
+ # A 'double-free' doesn't hurt if e.g. left == right! However, we have to watch out
135
+ # for out-of-bounds errors.
136
+
137
+ @gosu_images[left][top] = nil unless left < 0 or top < 0
138
+ @gosu_images[right][top] = nil unless right >= TILES_X or top < 0
139
+ @gosu_images[left][bottom] = nil unless left < 0 or bottom >= TILES_Y
140
+ @gosu_images[right][bottom] = nil unless right >= TILES_X or bottom >= TILES_Y
141
+
142
+ # Create the crater image (basically a circle shape that is used to erase
143
+ # parts of the map) and the crater shadow image, if they don't exist
144
+ # already.
145
+
146
+ if @crater_image.nil? then
147
+ @crater_image = Magick::Image.new(2 * RADIUS, 2 * RADIUS) { self.background_color = 'none' }
148
+ gc = Magick::Draw.new
149
+ gc.fill('black').circle(RADIUS, RADIUS, RADIUS, 0)
150
+ gc.draw(@crater_image)
151
+ @crater_shadow = @crater_image.shadow(0, 0, (SH_RADIUS - RADIUS) / 2, 1)
152
+ end
153
+
154
+ # Draw the shadow (twice for more intensity), then erase a circle from the map.
155
+ @image.composite!(@crater_shadow, x - SH_RADIUS, y - SH_RADIUS, Magick::AtopCompositeOp)
156
+ @image.composite!(@crater_shadow, x - SH_RADIUS, y - SH_RADIUS, Magick::AtopCompositeOp)
157
+ @image.composite!(@crater_image, x - RADIUS, y - RADIUS, Magick::DstOutCompositeOp)
158
+ end
159
+ end
160
+
161
+ # Player class.
162
+ # Note that applies to the whole game:
163
+ # All objects implement an informal interface.
164
+ # draw: Draws the object (obviously)
165
+ # update: Moves the object etc., returns false if the object is to be deleted
166
+ # hit_by?(missile): Returns true if an object is hit by the missile, causing
167
+ # it to explode on this object.
168
+
169
+ class Player
170
+ # Magic numbers considered harmful! This is the height of the
171
+ # player as used for collision detection.
172
+ HEIGHT = 14
173
+
174
+ attr_reader :x, :y, :dead
175
+
176
+ def initialize(window, x, y, color)
177
+ # Only load the images once for all instances of this class.
178
+ @@images ||= Gosu::Image.load_tiles(window, "media/Soldier.png", 40, 50, false)
179
+
180
+ @window, @x, @y, @color = window, x, y, color
181
+ @vy = 0
182
+
183
+ # -1: left, +1: right
184
+ @dir = -1
185
+
186
+ # Aiming angle.
187
+ @angle = 90
188
+ end
189
+
190
+ def draw
191
+ if dead then
192
+ # Poor, broken soldier.
193
+ @@images[0].draw_rot(x, y, 0, 290 * @dir, 0.5, 0.65, @dir * 0.5, 0.5, @color)
194
+ @@images[2].draw_rot(x, y, 0, 160 * @dir, 0.95, 0.5, 0.5, @dir * 0.5, @color)
195
+ else
196
+ # Was moved last frame?
197
+ if @show_walk_anim
198
+ # Yes: Display walking animation.
199
+ frame = Gosu::milliseconds / 200 % 2
200
+ else
201
+ # No: Stand around (boring).
202
+ frame = 0
203
+ end
204
+
205
+ # Draw feet, then chest.
206
+ @@images[frame].draw(x - 10 * @dir, y - 20, 0, @dir * 0.5, 0.5, @color)
207
+ angle = @angle
208
+ angle = 180 - angle if @dir == -1
209
+ @@images[2].draw_rot(x, y - 5, 0, angle, 1, 0.5, 0.5, @dir * 0.5, @color)
210
+ end
211
+ end
212
+
213
+ def update
214
+ # First, assume that no walking happened this frame.
215
+ @show_walk_anim = false
216
+
217
+ # Gravity.
218
+ @vy += 1
219
+
220
+ if @vy > 1 then
221
+ # Move upwards until hitting something.
222
+ @vy.times do
223
+ if @window.map.solid?(x, y + 1)
224
+ @vy = 0
225
+ break
226
+ else
227
+ @y += 1
228
+ end
229
+ end
230
+ else
231
+ # Move downwards until hitting something.
232
+ (-@vy).times do
233
+ if @window.map.solid?(x, y - HEIGHT - 1)
234
+ @vy = 0
235
+ break
236
+ else
237
+ @y -= 1
238
+ end
239
+ end
240
+ end
241
+
242
+ # Soldiers are never deleted (they may die, though, but that is a different
243
+ # concept).
244
+ true
245
+ end
246
+
247
+ def aim_up
248
+ @angle -= 2 unless @angle < 10
249
+ end
250
+
251
+ def aim_down
252
+ @angle += 2 unless @angle > 170
253
+ end
254
+
255
+ def try_walk(dir)
256
+ @show_walk_anim = true
257
+ @dir = dir
258
+ # First, magically move up (so soldiers can run up hills)
259
+ 2.times { @y -= 1 unless @window.map.solid?(x, y - HEIGHT - 1) }
260
+ # Now move into the desired direction.
261
+ @x += dir unless @window.map.solid?(x + dir, y) or
262
+ @window.map.solid?(x + dir, y - HEIGHT)
263
+ # To make up for unnecessary movement upwards, sink downward again.
264
+ 2.times { @y += 1 unless @window.map.solid?(x, y + 1) }
265
+ end
266
+
267
+ def try_jump
268
+ @vy = -12 if @window.map.solid?(x, y + 1)
269
+ end
270
+
271
+ def shoot
272
+ @window.objects << Missile.new(@window, x + 10 * @dir, y - 10, @angle * @dir)
273
+ end
274
+
275
+ def hit_by?(missile)
276
+ if Gosu::distance(missile.x, missile.y, x, y) < 30 then
277
+ # Was hit :(
278
+ @dead = true
279
+ return true
280
+ else
281
+ return false
282
+ end
283
+ end
284
+ end
285
+
286
+ # Implements the same interface as Player, except it'S a missile!
287
+
288
+ class Missile
289
+ attr_reader :x, :y, :vx, :vy
290
+
291
+ def initialize(window, x, y, angle)
292
+ # All missile instances use the same sound.
293
+ @@explosion_sound ||= Gosu::Sample.new(window, "media/Explosion.wav")
294
+
295
+ # Horizontal/vertical velocity.
296
+ @vx, @vy = Gosu::offset_x(angle, 20).to_i, Gosu::offset_y(angle, 20).to_i
297
+
298
+ @window, @x, @y = window, x + @vx, y + @vy
299
+ end
300
+
301
+ def update
302
+ # Movement, gravity
303
+ @x += @vx
304
+ @y += @vy
305
+ @vy += 1
306
+ # Hit anything?
307
+ if @window.map.solid?(x, y) or @window.objects.any? { |o| o.hit_by?(self) } then
308
+ # Create great particles.
309
+ 5.times { @window.objects << Particle.new(@window, x - 25 + rand(51), y - 25 + rand(51)) }
310
+ @window.map.blast(x, y)
311
+ # Weeee, stereo sound!
312
+ @@explosion_sound.play_pan((2 * @x - Map::WIDTH) / Map::WIDTH)
313
+ return false
314
+ else
315
+ return true
316
+ end
317
+ end
318
+
319
+ def draw
320
+ # Just draw a small quad.
321
+ @window.draw_quad(x-2, y-2, 0xff800000, x+2, y-2, 0xff800000,
322
+ x-2, y+2, 0xff800000, x+2, y+2, 0xff800000, 0)
323
+ end
324
+
325
+ def hit_by?(missile)
326
+ # Missiles can't be hit by other missiles!
327
+ false
328
+ end
329
+ end
330
+
331
+ # Very minimal object that just draws a fading particle.
332
+
333
+ class Particle
334
+ def initialize(window, x, y)
335
+ # All Particle instances use the same image
336
+ @@image ||= Gosu::Image.new(window, 'media/Smoke.png', false)
337
+
338
+ @x, @y = x, y
339
+ @color = Gosu::Color.new(255, 255, 255, 255)
340
+ end
341
+
342
+ def update
343
+ @y -= 5
344
+ @x = @x - 1 + rand(3)
345
+ @color.alpha -= 5
346
+
347
+ # Remove if faded completely.
348
+ @color.alpha > 0
349
+ end
350
+
351
+ def draw
352
+ @@image.draw(@x - 25, @y - 25, 0, 1, 1, @color)
353
+ end
354
+
355
+ def hit_by?(missile)
356
+ # Smoke can't be hit!
357
+ false
358
+ end
359
+ end
360
+
361
+ # Finally, the class that ties it all together.
362
+ # Very straightforward implementation.
363
+
364
+ class GameWindow < Gosu::Window
365
+ attr_reader :map, :objects
366
+
367
+ def initialize()
368
+ super(800, 600, false)
369
+ self.caption = "Medal of Anna - Gosu & RMagick Integration Demo"
370
+
371
+ # Texts to display in the appropriate situations.
372
+ @player_instructions = []
373
+ @player_won_messages = []
374
+ 2.times do |plr|
375
+ @player_instructions << Gosu::Image.from_text(self,
376
+ "It is the #{ plr == 0 ? 'green' : 'red' } toy soldier's turn.\n" +
377
+ "(Arrow keys to walk and aim, Return to jump, Space to shoot)",
378
+ Gosu::default_font_name, 25, 0, width, :center)
379
+ @player_won_messages << Gosu::Image.from_text(self,
380
+ "The #{ plr == 0 ? 'green' : 'red' } toy soldier has won!",
381
+ Gosu::default_font_name, 25, 5, width, :center)
382
+ end
383
+
384
+ # Create everything!
385
+ @map = Map.new(self)
386
+ @players = [Player.new(self, 200, 40, 0xff308000), Player.new(self, 600, 40, 0xff803000)]
387
+ @objects = @players.dup
388
+
389
+ # Let any player start.
390
+ @current_player = rand(2)
391
+ # Currently not waiting for a missile to hit something.
392
+ @waiting = false
393
+ end
394
+
395
+ def draw
396
+ # Draw the main game.
397
+ @map.draw
398
+ @objects.each { |o| o.draw }
399
+
400
+ # If any text should be displayed, draw it - and add a nice black border around it
401
+ # by drawing it four times, with a little offset in each direction.
402
+
403
+ cur_text = @player_instructions[@current_player] if not @waiting
404
+ cur_text = @player_won_messages[1 - @current_player] if @players[@current_player].dead
405
+
406
+ if cur_text then
407
+ x, y = 0, 30
408
+ cur_text.draw(x - 1, y, 0, 1, 1, 0xff000000)
409
+ cur_text.draw(x + 1, y, 0, 1, 1, 0xff000000)
410
+ cur_text.draw(x, y - 1, 0, 1, 1, 0xff000000)
411
+ cur_text.draw(x, y + 1, 0, 1, 1, 0xff000000)
412
+ cur_text.draw(x, y, 0, 1, 1, 0xffffffff)
413
+ end
414
+ end
415
+
416
+ def update
417
+ # if waiting for the next player's turn, continue to do so until the missile has
418
+ # hit something.
419
+ @waiting &&= !@objects.grep(Missile).empty?
420
+
421
+ # Remove all objects whose update method returns false.
422
+ @objects.reject! { |o| o.update == false }
423
+
424
+ # If it's a player's turn, forward controls.
425
+ if not @waiting and not @players[@current_player].dead then
426
+ player = @players[@current_player]
427
+ player.aim_up if button_down? Gosu::KbUp
428
+ player.aim_down if button_down? Gosu::KbDown
429
+ player.try_walk(-1) if button_down? Gosu::KbLeft
430
+ player.try_walk(1) if button_down? Gosu::KbRight
431
+ player.try_jump if button_down? Gosu::KbReturn
432
+ end
433
+ end
434
+
435
+ def button_down(id)
436
+ if id == Gosu::KbSpace and not @waiting and not @players[@current_player].dead then
437
+ # Shoot! This is handled in button_down because holding space shouldn't
438
+ # auto-fire - the shots would come from different players.
439
+ @players[@current_player].shoot
440
+ @current_player = 1 - @current_player
441
+ @waiting = true
442
+ end
443
+ # Very important feature! ;)
444
+ close if id == Gosu::KbEscape
445
+ end
446
+ end
447
+
448
+ # So far we have only defined how everything *should* work - now set it up and run it!
449
+ GameWindow.new.show
@@ -0,0 +1,170 @@
1
+ // This example demonstrates the use of the TextInput functionality.
2
+ // One can tab through, or click into the text fields and change it's contents.
3
+
4
+ // At its most basic form, you only need to create a new TextInput instance and
5
+ // pass it to your window via setTextInput. Until you call this function again,
6
+ // passing 0, the TextInput object will build a text that can be accessed via
7
+ // TextInput::text().
8
+
9
+ // The TextInput object also maintains the position of the caret as the index
10
+ // of the character that it's left to via the caretPos() member function.
11
+ // Furthermore, if there is a selection, the selectionStart() member yields its
12
+ // beginning, using the same indexing scheme. If there is no selection,
13
+ // selectionStart() is equal to caretPos().
14
+
15
+ // A TextInput object is purely abstract, though; drawing the input field is left
16
+ // to the user. In this case, we are subclassing TextInput to add this code.
17
+ // As with most of Gosu, how this is handled is completely left open; the scheme
18
+ // presented here is not mandatory! Gosu only aims to provide enough code for
19
+ // games (or intermediate UI toolkits) to be built upon it.
20
+
21
+ #include <Gosu/Gosu.hpp>
22
+ #include <boost/scoped_ptr.hpp>
23
+
24
+ class TextField : public Gosu::TextInput
25
+ {
26
+ Gosu::Window& window;
27
+ Gosu::Font& font;
28
+ double x, y;
29
+
30
+ public:
31
+ // Some constants that define our appearance.
32
+ // (Can't use Gosu::Color that easily as a class constant, thanks to C++.)
33
+ static const unsigned long INACTIVE_COLOR = 0xcc666666;
34
+ static const unsigned long ACTIVE_COLOR = 0xccff6666;
35
+ static const unsigned long SELECTION_COLOR = 0xcc0000ff;
36
+ static const unsigned long CARET_COLOR = 0xffffffff;
37
+ static const int PADDING = 5;
38
+
39
+ TextField(Gosu::Window& window, Gosu::Font& font, double x, double y)
40
+ : window(window), font(font), x(x), y(y)
41
+ {
42
+ // Start with a self-explanatory text in each field.
43
+ setText(L"Click to change text");
44
+ }
45
+
46
+ void draw() const
47
+ {
48
+ // Depending on whether this is the currently selected input or not, change the
49
+ // background's color.
50
+ Gosu::Color backgroundColor;
51
+ if (window.input().textInput() == this)
52
+ backgroundColor = ACTIVE_COLOR;
53
+ else
54
+ backgroundColor = INACTIVE_COLOR;
55
+ window.graphics().drawQuad(x - PADDING, y - PADDING, backgroundColor,
56
+ x + width() + PADDING, y - PADDING, backgroundColor,
57
+ x - PADDING, y + height() + PADDING, backgroundColor,
58
+ x + width() + PADDING, y + height() + PADDING, backgroundColor, 0);
59
+
60
+ // Calculate the position of the caret and the selection start.
61
+ double posX = x + font.textWidth(text().substr(0, caretPos()));
62
+ double selX = x + font.textWidth(text().substr(0, selectionStart()));
63
+
64
+ // Draw the selection background, if any; if not, sel_x and pos_x will be
65
+ // the same value, making this quad empty.
66
+ window.graphics().drawQuad(selX, y, SELECTION_COLOR,
67
+ posX, y, SELECTION_COLOR,
68
+ selX, y + height(), SELECTION_COLOR,
69
+ posX, y + height(), SELECTION_COLOR, 0);
70
+
71
+ // Draw the caret; again, only if this is the currently selected field.
72
+ if (window.input().textInput() == this)
73
+ window.graphics().drawLine(posX, y, CARET_COLOR,
74
+ posX, y + height(), CARET_COLOR, 0);
75
+
76
+ // Finally, draw the text itself!
77
+ font.draw(text(), x, y, 0);
78
+ }
79
+
80
+ // This text field grows with the text that's being entered.
81
+ // (Without clipping, one has to be a bit creative about this ;) )
82
+ double width() const
83
+ {
84
+ return font.textWidth(text());
85
+ }
86
+
87
+ double height() const
88
+ {
89
+ return font.height();
90
+ }
91
+
92
+ bool isUnderPoint(double mouseX, double mouseY)
93
+ {
94
+ return mouseX > x - PADDING && mouseX < x + width() + PADDING &&
95
+ mouseY > y - PADDING and mouseY < y + height() + PADDING;
96
+ }
97
+ };
98
+
99
+ // Helper magic to get size of static array, MUCH safer than sizeof hackery.
100
+ template<typename T, std::size_t Len>
101
+ std::size_t lengthof(const T(&) [Len])
102
+ {
103
+ return Len;
104
+ }
105
+
106
+ class TextInputWindow : public Gosu::Window
107
+ {
108
+ boost::scoped_ptr<Gosu::Font> font;
109
+ boost::scoped_ptr<TextField> textFields[3];
110
+ boost::scoped_ptr<Gosu::Image> cursor;
111
+
112
+ public:
113
+ TextInputWindow()
114
+ : Gosu::Window(300, 200, false)
115
+ {
116
+ setCaption(L"Text Input Example");
117
+
118
+ font.reset(new Gosu::Font(graphics(), Gosu::defaultFontName(), 20));
119
+
120
+ for (int index = 0; index < lengthof(textFields); ++index)
121
+ textFields[index].reset(new TextField(*this, *font, 50, 30 + index * 50));
122
+
123
+ cursor.reset(new Gosu::Image(graphics(), L"media/Cursor.png", false));
124
+ }
125
+
126
+ void draw()
127
+ {
128
+ for (int i = 0; i < lengthof(textFields); ++i)
129
+ textFields[i]->draw();
130
+
131
+ cursor->draw(input().mouseX(), input().mouseY(), 0);
132
+ }
133
+
134
+ void buttonDown(Gosu::Button btn)
135
+ {
136
+ if (btn == Gosu::kbTab)
137
+ {
138
+ // Tab key will not be 'eaten' by text fields; use for switching through
139
+ // text fields.
140
+ int index = -1;
141
+ for (int i = 0; i < lengthof(textFields); ++i)
142
+ if (input().textInput() == textFields[i].get())
143
+ index = i;
144
+ input().setTextInput(textFields[(index + 1) % lengthof(textFields)].get());
145
+ }
146
+ else if (btn == Gosu::kbEscape)
147
+ {
148
+ // Escape key will not be 'eaten' by text fields; use for deselecting.
149
+ if (input().textInput())
150
+ input().setTextInput(0);
151
+ else
152
+ close();
153
+ }
154
+ else if (btn == Gosu::msLeft)
155
+ {
156
+ // Mouse click: Select text field based on mouse position.
157
+ input().setTextInput(0);
158
+ for (int i = 0; i < lengthof(textFields); ++i)
159
+ if (textFields[i]->isUnderPoint(input().mouseX(), input().mouseY()))
160
+ input().setTextInput(textFields[i].get());
161
+ }
162
+ }
163
+ };
164
+
165
+ int main(int argc, char* argv[])
166
+ {
167
+ TextInputWindow win;
168
+ win.show();
169
+ return 0;
170
+ }