gosu 0.7.10.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }