ang 9.19.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 +7 -0
- data/CHANGELOG.txt +88 -0
- data/README.md +86 -0
- data/Rakefile.rb +228 -0
- data/Star.bmp +0 -0
- data/Star.png +0 -0
- data/Starfighter.bmp +0 -0
- data/Starfighter2.bmp +0 -0
- data/VERSION +1 -0
- data/ang.gemspec +48 -0
- data/bin/ang +5 -0
- data/bin/angm +5 -0
- data/bin/angm_net_test +3 -0
- data/commit.rb +11 -0
- data/main.rb +379 -0
- data/main_screencast.rb +324 -0
- data/net/Star.bmp +0 -0
- data/net/Starfighter.bmp +0 -0
- data/net/agent.rb +339 -0
- data/net/essai.rb +10 -0
- data/net/missile.rb +51 -0
- data/net/multicast_test.rb +74 -0
- data/net/net.rb +199 -0
- data/net/player.rb +166 -0
- data/net/star.rb +76 -0
- data/net/tools.rb +58 -0
- metadata +93 -0
data/bin/ang
ADDED
data/bin/angm
ADDED
data/bin/angm_net_test
ADDED
data/commit.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
comments=ARGV.join(" ")
|
2
|
+
exit! if comments.size==0
|
3
|
+
|
4
|
+
version=File.read("VERSION").strip.to_f
|
5
|
+
version= ((version*100)+1).round*0.01
|
6
|
+
open("VERSION","w") { |f| f.print(version.to_s) }
|
7
|
+
puts "New Version=#{version}"
|
8
|
+
|
9
|
+
system("git","commit","-a","-m","#{comments}")
|
10
|
+
system("git push") # put your password in .git/config : http://name:passwd@github...
|
11
|
+
puts "\n\nDone"
|
data/main.rb
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com># LGPL
|
2
|
+
###########################################################################
|
3
|
+
# ANG.RB : planetoide game
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
# install ruby
|
6
|
+
# install Gosu > gem install gosu
|
7
|
+
# download this > git http://github.com/rdaubarede/ang.git
|
8
|
+
# run > ruby main.rb
|
9
|
+
# do your own version :
|
10
|
+
# > loop { edit/main.rb ; ruby main.rb }
|
11
|
+
# make your disribution :
|
12
|
+
# > ocra main.rb
|
13
|
+
#
|
14
|
+
###########################################################################
|
15
|
+
if RUBY_VERSION >= "3.0.0"
|
16
|
+
puts "Sorry, not ready for ruiby 3..."
|
17
|
+
exit(0)
|
18
|
+
end
|
19
|
+
##################### Tuning ##########################
|
20
|
+
|
21
|
+
KK=0.5
|
22
|
+
SX=1280/KK # window size width
|
23
|
+
SY=900/KK # height
|
24
|
+
$INITIALE_SCORE=2000
|
25
|
+
$NB_STAR=35
|
26
|
+
$NB_PLANET=8
|
27
|
+
$RANGE_STAR_SIZE=(10..30) # more planet / bigger planets ==>> harder game!
|
28
|
+
$SCALE_PLAYER=0.8
|
29
|
+
$SIZE_TANK=5
|
30
|
+
$DD=false
|
31
|
+
#######################################################
|
32
|
+
|
33
|
+
require 'rubygems'
|
34
|
+
require 'gosu'
|
35
|
+
|
36
|
+
module ZOrder
|
37
|
+
Background, Stars, Player, UI = [0,1,2,3]
|
38
|
+
end
|
39
|
+
class Numeric
|
40
|
+
def minmax(min,max=nil)
|
41
|
+
(max=min;min=-min) if !max
|
42
|
+
return self if self>=min && self<=max
|
43
|
+
return(min) if self<min
|
44
|
+
return(max)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
class Range ; def rand() self.begin+Kernel.rand((self.end-self.begin).abs) end ; end
|
48
|
+
|
49
|
+
# pseudo newton
|
50
|
+
def newton_xy1(p1,p2,a,b)
|
51
|
+
k1=1.5/200
|
52
|
+
k2=1.0/30000
|
53
|
+
dx,dy=[a.x-b.x,a.y-b.y]
|
54
|
+
d=Math::sqrt(dx ** 2 + dy ** 2)
|
55
|
+
f1=(k1*p1*p2/(d*d)).minmax(100)
|
56
|
+
f2=(k2*p1*p2/(d)).minmax(100)
|
57
|
+
teta=Math.atan2(dy,dx)
|
58
|
+
f=(f2<f1) ? f1 : f2
|
59
|
+
$DD=true if (f2>f1)
|
60
|
+
r=[-f*Math.cos(teta),-f*Math.sin(teta)]
|
61
|
+
r
|
62
|
+
end
|
63
|
+
|
64
|
+
# newton, with 'amortissement'
|
65
|
+
def newton_xy(p1,p2,a,b,k=1.0/300,dmin=10,dmax=10000)
|
66
|
+
dx,dy=[a.x-b.x,a.y-b.y]
|
67
|
+
d=dmin+Math::sqrt(dx ** 2 + dy ** 2)
|
68
|
+
return [0,0] if d>dmax
|
69
|
+
#f=(k*p1*p2/(d*d)).minmax(100) : k/d**2 not good for gameplay
|
70
|
+
f=(k*p1*p2/(d*d-dmin*dmin)).minmax(10)
|
71
|
+
teta=Math.atan2(dy,dx)
|
72
|
+
[-f*Math.cos(teta),-f*Math.sin(teta)]
|
73
|
+
end
|
74
|
+
|
75
|
+
# apply gravity between obj and a list l of planet
|
76
|
+
# k=coef gr&avity, >0 attraction, <0 repulsion
|
77
|
+
# dmaw : distance max, no attaction if distance>dmax
|
78
|
+
def motion(l,obj,k,dmax)
|
79
|
+
dx,dy=0,0
|
80
|
+
l.each do |o|
|
81
|
+
next if o==obj
|
82
|
+
next if block_given? && ! yield(o)
|
83
|
+
dx1,dy1= newton_xy(obj.r,o.r,obj,o,k,0,dmax)
|
84
|
+
dx+=dx1
|
85
|
+
dy+=dy1
|
86
|
+
end
|
87
|
+
obj.x+=dx
|
88
|
+
obj.y+=dy
|
89
|
+
return Math.sqrt(dx*dx+dy*dy)
|
90
|
+
end
|
91
|
+
|
92
|
+
###########################################################################
|
93
|
+
# P l a y e r
|
94
|
+
###########################################################################
|
95
|
+
# move by arrow keyboard acceleration commande,
|
96
|
+
# eat star, move with current speed, and attractive planets
|
97
|
+
class Player
|
98
|
+
attr_accessor :x,:y,:r,:score
|
99
|
+
def initialize(window,animation)
|
100
|
+
@animation = animation
|
101
|
+
@app=window
|
102
|
+
clear()
|
103
|
+
@r=15
|
104
|
+
self.restart()
|
105
|
+
end
|
106
|
+
def clear
|
107
|
+
@pos=1
|
108
|
+
end
|
109
|
+
def restart
|
110
|
+
@vel_x = @vel_y = @angle = @vangle = 0.0
|
111
|
+
@x = SX/2
|
112
|
+
@y = SY/2
|
113
|
+
@score = $INITIALE_SCORE
|
114
|
+
@lxy=[]
|
115
|
+
end
|
116
|
+
def warp(x, y) @x, @y = x, y ; end
|
117
|
+
def turn_left() @pos=3 ; @vangle -= 0.3 ; end
|
118
|
+
def turn_right() @pos=4 ; @vangle += 0.3 ; end
|
119
|
+
def accelerate(s)
|
120
|
+
@pos= s ? 0 : 2
|
121
|
+
@score-=3
|
122
|
+
@vel_x += 0.7*Gosu::offset_x(@angle, s ? 0.1 : -0.1)
|
123
|
+
@vel_y += 0.7*Gosu::offset_y(@angle, s ? 0.1 : -0.1)
|
124
|
+
end
|
125
|
+
|
126
|
+
def move(stars)
|
127
|
+
a=false
|
128
|
+
(a=true;@vel_x *= -1) if @x >= SX || @x <= 0
|
129
|
+
(a=true;@vel_y *= -1) if @y >= SY || @y <= 0
|
130
|
+
@x -= 10 if @x >= SX && @vel_x == 0
|
131
|
+
@y -= 10 if @y >= SY && @vel_y == 0
|
132
|
+
@x += 10 if @x <= 0 && @vel_x == 0
|
133
|
+
@y += 10 if @y <= 0 && @vel_y == 0
|
134
|
+
|
135
|
+
@angle+=@vangle
|
136
|
+
@vangle=@vangle*95.0/100
|
137
|
+
@lxy << [@x,@y] if @vel_x!=0 && @vel_y!=0
|
138
|
+
vx,vy=newton(stars)
|
139
|
+
@vel_x+=vx
|
140
|
+
@vel_y+=vy
|
141
|
+
@vel_x.minmax(-50,+50)
|
142
|
+
@vel_y.minmax(-50,+50)
|
143
|
+
@x += @vel_x
|
144
|
+
@y += @vel_y
|
145
|
+
if a
|
146
|
+
@vel_x*=1
|
147
|
+
@vel_y*=1
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
def newton(stars)
|
152
|
+
vx = vy = 0.0
|
153
|
+
stars.each do |star|
|
154
|
+
next if star.type
|
155
|
+
d=Gosu::distance(@x,@y,star.x,star.y)-15-star.r
|
156
|
+
dx,dy=*newton_xy1(15*15,star.r*star.r,self,star)
|
157
|
+
vx+=dx
|
158
|
+
vy+=dy
|
159
|
+
end
|
160
|
+
[vx,vy]
|
161
|
+
end
|
162
|
+
|
163
|
+
def draw(app,stars)
|
164
|
+
img = @animation[@pos]
|
165
|
+
img.draw_rot(@x, @y, ZOrder::Player, @angle, 0.5, 0.5, $SCALE_PLAYER,$SCALE_PLAYER)
|
166
|
+
x,y=newton(stars) ; app.draw_line(@x,@y, 0xffffffff,@x+x*1000,@y+y*1000,0xffffffff) # debug: mark gravity force
|
167
|
+
if app.pending?
|
168
|
+
@lxy.each_cons(2) { |p0,p1| app.draw_line(p0[0],p0[1], 0xffffff00 ,p1[0],p1[1], 0xffffff00 ) if p1} rescue nil
|
169
|
+
elsif @lxy.size>100
|
170
|
+
@lxy[(-1*[800,@lxy.size].min)..-1].each_cons(2) { |p0,p1|
|
171
|
+
app.draw_line(p0[0],p0[1], 0x33ffff00 ,p1[0],p1[1], 0x33ffff00 ) if p1
|
172
|
+
} rescue nil
|
173
|
+
end
|
174
|
+
@lxy=@lxy[-5000..-1] if @lxy.size>10000
|
175
|
+
end
|
176
|
+
|
177
|
+
def collect_stars(stars)
|
178
|
+
stars.reject! do |star|
|
179
|
+
next if star.x-@x > 200
|
180
|
+
next if star.x-@x < -200
|
181
|
+
next if star.y-@y > 200
|
182
|
+
next if star.y-@y < -200
|
183
|
+
|
184
|
+
if Gosu::distance(@x, @y, star.x, star.y) < (15+star.r) then
|
185
|
+
if star.type
|
186
|
+
@score += 120
|
187
|
+
true
|
188
|
+
else
|
189
|
+
if @vel_x !=0 || @vel_y!=0
|
190
|
+
@score -= 10
|
191
|
+
@x -= 25*@vel_x
|
192
|
+
@y -= 25*@vel_y
|
193
|
+
@x -= -2*25*@vel_x if @x >= SX && @vel_x == 0
|
194
|
+
@y -= -2*25*@vel_y if @y >= SY && @vel_y == 0
|
195
|
+
else
|
196
|
+
@x += (-10..+10).rand
|
197
|
+
@y += (-10..+10).rand
|
198
|
+
end
|
199
|
+
@vel_x=0
|
200
|
+
@vel_y=0
|
201
|
+
false
|
202
|
+
end
|
203
|
+
else
|
204
|
+
false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
@app.looser if @score<=0
|
208
|
+
@app.winner(@score) if 0 == (stars.select { |s| s.type }.size )
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
###########################################################################
|
215
|
+
# S t a r
|
216
|
+
###########################################################################
|
217
|
+
class Star
|
218
|
+
attr_accessor :x, :y, :type,:r
|
219
|
+
|
220
|
+
def initialize(ls,type,animation)
|
221
|
+
@animation = animation
|
222
|
+
@ls=ls
|
223
|
+
@type=type
|
224
|
+
@r=@type ? $SIZE_TANK : $RANGE_STAR_SIZE.rand()
|
225
|
+
@no_img= type ? 1 : (rand()>0.5 && (@r>($RANGE_STAR_SIZE.max+$RANGE_STAR_SIZE.max)/2)) ? 0 : (rand(3)+2)
|
226
|
+
@rot=rand(180)
|
227
|
+
@color = Gosu::Color.new(0xff000000 )
|
228
|
+
@color.red = type ? 255 : 200
|
229
|
+
@color.green = type ? 0 : 200
|
230
|
+
@color.blue = type ? 0 : 200
|
231
|
+
@x = (SX/5..(SX-SX/5)).rand
|
232
|
+
@y = (SY/5..(SY-SY/5)).rand
|
233
|
+
end
|
234
|
+
def move(game,player,ls)
|
235
|
+
ox,oy=@x,@y
|
236
|
+
expand(game,player,ls)
|
237
|
+
@x=ox if @x>(SX-40) || @x<40
|
238
|
+
@y=oy if @y>(SY-40) || @y<40
|
239
|
+
|
240
|
+
end
|
241
|
+
def draw()
|
242
|
+
img = @animation[@no_img]
|
243
|
+
img.draw_rot(@x, @y, ZOrder::Stars, @rot, 0.5,0.5 ,@r/40.0, @r/40.0,@color)
|
244
|
+
end
|
245
|
+
def expand(game,player,ls)
|
246
|
+
return unless game.pending?(40)
|
247
|
+
motion(ls,self,-100.0,110) { |o| ! o.type} if type # Star <-> Planet
|
248
|
+
motion(ls,self,-10.0,50) { |o| o.type} # * <-> Star
|
249
|
+
motion(ls,self,-10.0,500) { |o| ! o.type } if !type # Planet <-> Planet
|
250
|
+
|
251
|
+
motion([player],self,-6,180) if type # Star <-> Player
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
###########################################################################
|
256
|
+
# W i n d o w
|
257
|
+
###########################################################################
|
258
|
+
|
259
|
+
class GameWindow < Gosu::Window
|
260
|
+
attr_reader :star,:ping
|
261
|
+
def initialize
|
262
|
+
super((SX*KK).to_i, (SY*KK).to_i, false)
|
263
|
+
self.caption = "Gosu Tutorial Game"
|
264
|
+
|
265
|
+
@lp=[]; 100.times { x=rand(SX) ; y=rand(SY); @lp<<x;@lp<<y }
|
266
|
+
|
267
|
+
@player_anim= Gosu::Image::load_tiles(self, "Starfighter.bmp", 50,50, false)
|
268
|
+
@player = Player.new(self,@player_anim)
|
269
|
+
@player.warp(320, 240)
|
270
|
+
@font = Gosu::Font.new(self, Gosu::default_font_name, (20/KK).round)
|
271
|
+
@font2 = Gosu::Font.new(self, Gosu::default_font_name, (40/KK).round)
|
272
|
+
|
273
|
+
@star_anim = Gosu::Image::load_tiles(self, "Star.bmp", 100,100, false)
|
274
|
+
@ping=0
|
275
|
+
@start=0
|
276
|
+
@mouse=nil
|
277
|
+
self.go("Start")
|
278
|
+
end
|
279
|
+
|
280
|
+
######################## Game global state
|
281
|
+
|
282
|
+
def looser() ego("Game Over !") end
|
283
|
+
def winner(n) ego("Wiiiinner #{n}") end
|
284
|
+
def ego(text)
|
285
|
+
return if @ping < @start
|
286
|
+
puts "ego #{text}"
|
287
|
+
@text=text
|
288
|
+
@start=@ping+200
|
289
|
+
Thread.new { sleep 2 ; self.go("Start") }
|
290
|
+
end
|
291
|
+
def go(text)
|
292
|
+
@start=@ping+200
|
293
|
+
@text=text
|
294
|
+
@stars = Array.new
|
295
|
+
$NB_PLANET.times { @stars.push( Star.new(@stars,false,@star_anim) ) }
|
296
|
+
$NB_STAR.times { @stars.push( Star.new(@stars,true,@star_anim) ) }
|
297
|
+
@player.restart
|
298
|
+
end
|
299
|
+
def pending?(d=0) (@start+d > @ping) end
|
300
|
+
|
301
|
+
######################## Global interactions : mouse/keyboard
|
302
|
+
|
303
|
+
def interactions()
|
304
|
+
@player.turn_left if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft
|
305
|
+
@player.turn_right if button_down? Gosu::KbRight or button_down? Gosu::GpRight
|
306
|
+
@player.accelerate(true) if button_down? Gosu::KbUp or button_down? Gosu::GpButton0
|
307
|
+
@player.accelerate(false) if button_down? Gosu::KbDown or button_down? Gosu::GpButton1
|
308
|
+
#mouse_control
|
309
|
+
end
|
310
|
+
def mouse_control
|
311
|
+
n=[mouse_x,mouse_y]
|
312
|
+
@mouse=n unless @mouse
|
313
|
+
dx=n[0]-@mouse[0]
|
314
|
+
dy=n[1]-@mouse[1]
|
315
|
+
if Math.hypot(dx,dy)>D
|
316
|
+
@player.turn_left if dx<-D
|
317
|
+
@player.turn_right if dx>D
|
318
|
+
@player.accelerate(true) if dy<-D
|
319
|
+
@player.accelerate(false) if dx>D
|
320
|
+
end
|
321
|
+
@mouse=n
|
322
|
+
end
|
323
|
+
def button_down(id)
|
324
|
+
if id == Gosu::KbEscape
|
325
|
+
if @player.score==0
|
326
|
+
close
|
327
|
+
else
|
328
|
+
@player.score=0
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
######################## Global draw : update()/draw() are invoked continuously by Gosu engine
|
334
|
+
|
335
|
+
def update
|
336
|
+
$DD=false
|
337
|
+
@ping+=1
|
338
|
+
@player.clear()
|
339
|
+
@stars.each { |star| star.move(self,@player,@stars) }
|
340
|
+
return if @ping<@start
|
341
|
+
interactions()
|
342
|
+
@player.move(@stars)
|
343
|
+
@player.collect_stars(@stars)
|
344
|
+
end
|
345
|
+
|
346
|
+
def draw
|
347
|
+
scale(KK,KK) {
|
348
|
+
draw_background
|
349
|
+
@player.draw(self,@stars)
|
350
|
+
@stars.each { |star| star.draw() }
|
351
|
+
draw_variable_background()
|
352
|
+
}
|
353
|
+
end
|
354
|
+
def draw_background
|
355
|
+
@lp.each_slice(2) do |x,y|
|
356
|
+
draw_triangle(
|
357
|
+
x, y, 0xAAFFFFFF,
|
358
|
+
x+(4..8).rand, y+(4..8).rand, 0xAAFFFFFF,
|
359
|
+
x+(4..8).rand, y, 0xAAFFFFFF)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
def draw_variable_background()
|
363
|
+
if @ping<@start
|
364
|
+
@font2.draw(@text+ " ! !", SX/2, SY/2, ZOrder::UI, 1.0, 1.0, 0xf0f0f000)
|
365
|
+
@font.draw("", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
|
366
|
+
else
|
367
|
+
#----------- barr graph energies reserve level
|
368
|
+
h=5+(@player.score/2000.0)*(SY-10)
|
369
|
+
draw_quad(5, 5, 0xBB55FF55, 20/KK, 5, 0xBB55FF55, 20/KK, h, 0xBBFFFF55, 5 , h , 0xBBFFFF55)
|
370
|
+
|
371
|
+
#------------ textual energie reserve level
|
372
|
+
@font.draw("Score: #{@player.score}", 25/KK, 10/KK, ZOrder::UI, 1.0, 1.0, 0xffffff00)
|
373
|
+
draw_quad(20, 20, 0xFFFF00FF, 40, 20, 0xFFFF00FF, 40, 40, 0xFFFF00FF, 20 , 40 , 0xFFFF00FF) if $DD
|
374
|
+
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
GameWindow.new.show
|
data/main_screencast.rb
ADDED
@@ -0,0 +1,324 @@
|
|
1
|
+
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com>
|
2
|
+
###########################################################################
|
3
|
+
# ANG.RB : planetoide game
|
4
|
+
#--------------------------------------------------------------------------
|
5
|
+
# install ruby
|
6
|
+
# install Gosu > gem install gosu
|
7
|
+
# download this > git http://github.com/rdaubarede/ang.git
|
8
|
+
# run > ruby main.rb
|
9
|
+
# do your own version :
|
10
|
+
# > loop { edit/main.rb ; ruby main.rb }
|
11
|
+
# make your disribution :
|
12
|
+
# > ocra main.rb
|
13
|
+
#
|
14
|
+
###########################################################################
|
15
|
+
require 'rubygems'
|
16
|
+
require 'gosu'
|
17
|
+
|
18
|
+
# KK=0.5
|
19
|
+
# SX=1280/KK
|
20
|
+
# SY=900/KK
|
21
|
+
KK=0.5
|
22
|
+
SX=640/KK
|
23
|
+
SY=480/KK
|
24
|
+
|
25
|
+
module ZOrder
|
26
|
+
Background, Stars, Player, UI = [0,1,2,3]
|
27
|
+
end
|
28
|
+
class Numeric
|
29
|
+
def minmax(min,max=nil)
|
30
|
+
(max=min;min=-min) if !max
|
31
|
+
return self if self>=min && self<=max
|
32
|
+
return(min) if self<min
|
33
|
+
return(max)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
class Range ; def rand() self.begin+Kernel.rand((self.end-self.begin).abs) end ; end
|
37
|
+
|
38
|
+
def e(a,b) (20.0/(a-b)).minmax(-10,+10) end
|
39
|
+
|
40
|
+
def newton_xy(p1,p2,a,b)
|
41
|
+
k=1.0/40000
|
42
|
+
dx,dy=[a.x-b.x,a.y-b.y]
|
43
|
+
d=Math::sqrt(dx ** 2 + dy ** 2)
|
44
|
+
#f=(k*p1*p2/(d*d)).minmax(100) : k/d**2 not good for gameplay
|
45
|
+
f=(k*p1*p2/(d)).minmax(10)
|
46
|
+
teta=Math.atan2(dy,dx)
|
47
|
+
[-f*Math.cos(teta),-f*Math.sin(teta)]
|
48
|
+
end
|
49
|
+
|
50
|
+
###########################################################################
|
51
|
+
# P l a y e r
|
52
|
+
###########################################################################
|
53
|
+
# move by arrow keyboard acceleration commande,
|
54
|
+
# eat star, move with current speed, and attractive planets
|
55
|
+
class Player
|
56
|
+
attr_reader :x,:y,:r
|
57
|
+
attr_accessor :score
|
58
|
+
def initialize(window,animation)
|
59
|
+
@animation = animation
|
60
|
+
@app=window
|
61
|
+
@r=15
|
62
|
+
@pos=1
|
63
|
+
self.restart()
|
64
|
+
end
|
65
|
+
def restart
|
66
|
+
@vel_x = @vel_y = @angle = @vangle = 0.0
|
67
|
+
@x = SX/2
|
68
|
+
@y = SY/2
|
69
|
+
@score = 2000
|
70
|
+
@lxy=[]
|
71
|
+
end
|
72
|
+
def clear
|
73
|
+
@pos=1
|
74
|
+
end
|
75
|
+
def warp(x, y) @x, @y = x, y ; end
|
76
|
+
def turn_left() @pos=3 ; @vangle -= 0.3 ; end
|
77
|
+
def turn_right() @pos=4 ; @vangle += 0.3 ; end
|
78
|
+
def accelerate(s)
|
79
|
+
@pos= s ? 0 : 2
|
80
|
+
@score-=3
|
81
|
+
@vel_x += Gosu::offset_x(@angle, s ? 0.1 : -0.1)
|
82
|
+
@vel_y += Gosu::offset_y(@angle, s ? 0.1 : -0.1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def move(stars)
|
86
|
+
@x += @vel_x
|
87
|
+
@y += @vel_y
|
88
|
+
@vel_x *= -1 if @x >= SX || @x <= 0
|
89
|
+
@vel_y *= -1 if @y >= SY || @y <= 0
|
90
|
+
@x -= 10 if @x >= SX && @vel_x == 0
|
91
|
+
@y -= 10 if @y >= SY && @vel_y == 0
|
92
|
+
@x += 10 if @x <= 0 && @vel_x == 0
|
93
|
+
@y += 10 if @y <= 0 && @vel_y == 0
|
94
|
+
|
95
|
+
#@vel_x *= 0.998
|
96
|
+
#@vel_y *= 0.998
|
97
|
+
@angle+=@vangle
|
98
|
+
@vangle=@vangle*95.0/100
|
99
|
+
@lxy << [@x,@y] if @vel_x!=0 && @vel_y!=0
|
100
|
+
vx,vy=newton(stars)
|
101
|
+
@vel_x+=vx
|
102
|
+
@vel_y+=vy
|
103
|
+
@vel_x.minmax(-50,+50)
|
104
|
+
@vel_y.minmax(-50,+50)
|
105
|
+
end
|
106
|
+
def newton(stars)
|
107
|
+
vx = vy = 0.0
|
108
|
+
stars.each do |star|
|
109
|
+
next if star.type
|
110
|
+
d=Gosu::distance(@x,@y,star.x,star.y)-15-star.r
|
111
|
+
dx,dy=*newton_xy(15*15,star.r*star.r,self,star)
|
112
|
+
vx+=dx
|
113
|
+
vy+=dy
|
114
|
+
end
|
115
|
+
[vx,vy]
|
116
|
+
end
|
117
|
+
|
118
|
+
def draw(app,stars)
|
119
|
+
img = @animation[@pos]
|
120
|
+
img.draw_rot(@x, @y, ZOrder::Player, @angle) # (@x, @y, ZOrder::Stars, 0, 0.5,0.5 ,@r/10, @r/10,@color)
|
121
|
+
#@image.draw_rot(@x, @y, ZOrder::Player, @angle)
|
122
|
+
x,y=newton(stars) ; app.draw_line(@x,@y, 0xffffffff,@x+x*1000,@y+y*1000,0xffffffff) # debug: mark gravity force
|
123
|
+
if app.is_pending
|
124
|
+
@lxy.each_cons(2) { |p0,p1| app.draw_line(p0[0],p0[1], 0xffffff00 ,p1[0],p1[1], 0xffffff00 ) if p1} rescue nil
|
125
|
+
elsif @lxy.size>100
|
126
|
+
@lxy[(-1*[300,@lxy.size].min)..-1].each_cons(2) { |p0,p1|
|
127
|
+
app.draw_line(p0[0],p0[1], 0x33ffff00 ,p1[0],p1[1], 0x33ffff00 ) if p1
|
128
|
+
} rescue nil
|
129
|
+
end
|
130
|
+
@lxy=@lxy[-5000..-1] if @lxy.size>10000
|
131
|
+
end
|
132
|
+
|
133
|
+
def collect_stars(stars)
|
134
|
+
stars.reject! do |star|
|
135
|
+
if Gosu::distance(@x, @y, star.x, star.y) < (15+star.r) then
|
136
|
+
if star.type
|
137
|
+
@score += 70
|
138
|
+
true
|
139
|
+
else
|
140
|
+
if @vel_x !=0 || @vel_y!=0
|
141
|
+
@score -= 10
|
142
|
+
@x -= 15*@vel_x
|
143
|
+
@y -= 15*@vel_y
|
144
|
+
else
|
145
|
+
@x += (-10..+10).rand
|
146
|
+
@y += (-10..+10).rand
|
147
|
+
end
|
148
|
+
@vel_x=0
|
149
|
+
@vel_y=0
|
150
|
+
false
|
151
|
+
end
|
152
|
+
else
|
153
|
+
false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@app.looser if @score<=0
|
157
|
+
@app.winner(@score) if 0 == (stars.select { |s| s.type }.size )
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
###########################################################################
|
164
|
+
# S t a r
|
165
|
+
###########################################################################
|
166
|
+
class Star
|
167
|
+
attr_reader :x, :y, :type,:r
|
168
|
+
|
169
|
+
def initialize(ls,type,animation)
|
170
|
+
@animation = animation
|
171
|
+
@ls=ls
|
172
|
+
@type=type
|
173
|
+
@r=@type ? 10 : (20..60).rand()
|
174
|
+
@color = Gosu::Color.new(0xff000000 )
|
175
|
+
@color.red = type ? 255 : 0
|
176
|
+
@color.green = type ? 0 : 200
|
177
|
+
@color.blue = type ? 0 : 200
|
178
|
+
@x = (SX/5..(SX-SX/5)).rand
|
179
|
+
@y = (SY/5..(SY-SY/5)).rand
|
180
|
+
end
|
181
|
+
def move(player)
|
182
|
+
expand(player)
|
183
|
+
end
|
184
|
+
def draw()
|
185
|
+
img = @animation[self.type ? 1 : 0 ]
|
186
|
+
img.draw_rot(@x, @y, ZOrder::Stars, 0, 0.5,0.5 ,@r/10, @r/10,@color)
|
187
|
+
end
|
188
|
+
def expand(p)
|
189
|
+
ox,oy=@x,@y
|
190
|
+
@ls.each do |s|
|
191
|
+
next if s==self
|
192
|
+
(@x+=e(@x,s.x) ; @y+=e(@y,s.y) ) if Gosu::distance(@x,@y,s.x,s.y)< ((s.type && self.type) ? 100 : 0 )
|
193
|
+
end
|
194
|
+
d=Gosu::distance(@x,@y,p.x,p.y)-@r-p.r
|
195
|
+
if @type
|
196
|
+
(@x+=-e(p.x,@x)/0.5 ; @y+=-e(p.y,@y)/0.5 ) if d> 20 && d < 180
|
197
|
+
else
|
198
|
+
(@x+=e(p.x,@x)/5.0 ; @y+=e(p.y,@y)/5.0 ) if d> 20 && d < 0
|
199
|
+
end
|
200
|
+
@x=ox if @x>600 || @x<40
|
201
|
+
@y=oy if @y>440 || @y<40
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
###########################################################################
|
206
|
+
# W i n d o w
|
207
|
+
###########################################################################
|
208
|
+
|
209
|
+
class GameWindow < Gosu::Window
|
210
|
+
attr_reader :star,:ping
|
211
|
+
def initialize
|
212
|
+
super((SX*KK).to_i, (SY*KK).to_i, false)
|
213
|
+
self.caption = "Gosu Tutorial Game"
|
214
|
+
|
215
|
+
@lp=[]; 100.times { x=rand(SX) ; y=rand(SY); @lp<<x;@lp<<y }
|
216
|
+
@player_anim= Gosu::Image::load_tiles(self, "Starfighter2.bmp", 50,50, false)
|
217
|
+
@player = Player.new(self,@player_anim)
|
218
|
+
@player.warp(320, 240)
|
219
|
+
@font = Gosu::Font.new(self, Gosu::default_font_name, (20/KK).round)
|
220
|
+
@font2 = Gosu::Font.new(self, Gosu::default_font_name, (40/KK).round)
|
221
|
+
|
222
|
+
@star_anim = Gosu::Image::load_tiles(self, "Star.png", 25,25, false)
|
223
|
+
@ping=0
|
224
|
+
@start=0
|
225
|
+
@mouse=nil
|
226
|
+
self.go("Start")
|
227
|
+
end
|
228
|
+
|
229
|
+
######################## Game global state
|
230
|
+
|
231
|
+
def looser() ego("Loose") end
|
232
|
+
def winner(n) ego("Winne #{n}") end
|
233
|
+
def ego(text)
|
234
|
+
puts "ego #{text}"
|
235
|
+
@text=text
|
236
|
+
@start=@ping+50
|
237
|
+
Thread.new { sleep 2 ; self.go("Start") }
|
238
|
+
end
|
239
|
+
def go(text)
|
240
|
+
@start=@ping+200
|
241
|
+
@text=text
|
242
|
+
@stars = Array.new
|
243
|
+
3.times { @stars.push( Star.new(@stars,false,@star_anim) ) }
|
244
|
+
10.times { @stars.push( Star.new(@stars,true,@star_anim) ) }
|
245
|
+
@player.restart
|
246
|
+
end
|
247
|
+
def is_pending() (@start > @ping) end
|
248
|
+
|
249
|
+
######################## Global interactions : mouse/keyboard
|
250
|
+
|
251
|
+
def interactions()
|
252
|
+
@player.turn_left if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft
|
253
|
+
@player.turn_right if button_down? Gosu::KbRight or button_down? Gosu::GpRight
|
254
|
+
@player.accelerate(true) if button_down? Gosu::KbUp or button_down? Gosu::GpButton0
|
255
|
+
@player.accelerate(false) if button_down? Gosu::KbDown or button_down? Gosu::GpButton1
|
256
|
+
#mouse_control
|
257
|
+
end
|
258
|
+
def mouse_control
|
259
|
+
n=[mouse_x,mouse_y]
|
260
|
+
@mouse=n unless @mouse
|
261
|
+
dx=n[0]-@mouse[0]
|
262
|
+
dy=n[1]-@mouse[1]
|
263
|
+
if Math.hypot(dx,dy)>D
|
264
|
+
@player.turn_left if dx<-D
|
265
|
+
@player.turn_right if dx>D
|
266
|
+
@player.accelerate(true) if dy<-D
|
267
|
+
@player.accelerate(false) if dx>D
|
268
|
+
end
|
269
|
+
@mouse=n
|
270
|
+
end
|
271
|
+
def button_down(id)
|
272
|
+
if id == Gosu::KbEscape
|
273
|
+
if @player.score==0
|
274
|
+
close
|
275
|
+
else
|
276
|
+
@player.score=0
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
######################## Global draw : update()/draw() are invoked continuously by Gosu engine
|
282
|
+
|
283
|
+
def update
|
284
|
+
@ping+=1
|
285
|
+
return if @ping<@start
|
286
|
+
@player.clear()
|
287
|
+
interactions()
|
288
|
+
@player.move(@stars)
|
289
|
+
@player.collect_stars(@stars)
|
290
|
+
@stars.each { |star| star.move(@player) }
|
291
|
+
end
|
292
|
+
|
293
|
+
def draw
|
294
|
+
scale(KK,KK) {
|
295
|
+
draw_background
|
296
|
+
@player.draw(self,@stars)
|
297
|
+
@stars.each { |star| star.draw() }
|
298
|
+
draw_variable_background()
|
299
|
+
}
|
300
|
+
end
|
301
|
+
def draw_background
|
302
|
+
@lp.each_slice(2) do |x,y|
|
303
|
+
draw_triangle(
|
304
|
+
x, y, 0xAAFFFFFF,
|
305
|
+
x+(3..5).rand, y+(3..5).rand, 0xAAFFFFFF,
|
306
|
+
x+(3..5).rand, y, 0xAAFFFFFF)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
def draw_variable_background()
|
310
|
+
if @ping<@start
|
311
|
+
@font2.draw(@text+ " ! !", SX/2, SY/2, ZOrder::UI, 1.0, 1.0, 0xf0f0f000)
|
312
|
+
@font.draw("", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
|
313
|
+
else
|
314
|
+
#----------- barr graph energies reserve level
|
315
|
+
h=5+(@player.score/2000.0)*(SY-10)
|
316
|
+
draw_quad(5, 5, 0xBB55FF55, 20/KK, 5, 0xBB55FF55, 20/KK, h, 0xBBFFFF55, 5 , h , 0xBBFFFF55)
|
317
|
+
|
318
|
+
#------------ textual energie reserve level
|
319
|
+
@font.draw("Score: #{@player.score}", 25/KK, 10/KK, ZOrder::UI, 1.0, 1.0, 0xffffff00)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
GameWindow.new.show
|