-
Notifications
You must be signed in to change notification settings - Fork 8
/
Example - Turrets zombies Random maps.monkey
697 lines (664 loc) · 20.4 KB
/
Example - Turrets zombies Random maps.monkey
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
Import mojo
Global screenwidth:Int,screenheight:Int
' This is a class that holds x and y variables.
Class pathnode
Field x:Int,y:Int
Method New(x:Int,y:Int)
Self.x = x
Self.y = y
End Method
End Class
Class bullet
' bullet x and y and radius
Field bx:Float,by:Float,br:Int=4
Field angle:Int
Field bulletspeed:Float=3
Field bulletmaxdist:Int
Field bullettraveled:Int
Field deleteme:Bool=False
' start location and target location for getting angle
Method New(x1:Int,y1:Int,x2:Int,y2:Int)
bx = x1
by = y1
'set the bullet radius
br = mymap.tw/4
' set the max bullet distance
bulletmaxdist = mymap.tw*3
angle = getangle(x1,y1,x2,y2)
End Method
Method update()
' update the bullet position
bx += Cos(angle)*bulletspeed
by += Sin(angle)*bulletspeed
' If the bullet hits a wall then delete the bullet
If mymap.map[bx/mymap.tw][by/mymap.th] = 0 Then deleteme = True
If mymap.map[bx/mymap.tw][by/mymap.th] = 2 Then deleteme = True
' if distance to long then flag bullet for removal
bullettraveled+=1
If bullettraveled > bulletmaxdist Then deleteme = True
' check collision with zombies
For Local i:=Eachin myzombie
If circleoverlap(bx,by,br,i.zx,i.zy,i.zr)
deleteme = True
i.hitpoints-=1
' Bounce them a bit back
'i.zx += Cos(angle+Rnd(-20,20))*Rnd(5,13)
'i.zy += Sin(angle+Rnd(-20,20))*Rnd(5,13)
i.flash = True
' If they are dead then flag them
If i.hitpoints<=0 Then
i.deleteme = True
' increase our counter
zombieskilled+=1
End If
End If
Next
End Method
Method draw()
SetColor 255,255,0
DrawCircle bx,by,br
End Method
End Class
Class turret
Field id:Int
' turret x and y and radius
Field tx:Int,ty:Int,tr:Int=16
'our target x and y
Field targetx:Int,targety:Int
' if to be deleted on the next run
Field deleteme:Bool=False
' delay counter between shots
Field shootdelay:Int
' maximum shoot delay
Field maxshootdelay:Int
' Our current angle and the angle where we want to shoot
Field currentangle:Int,shootangle:Int
' if we have no target
Field notarget:Bool=True
' how fast can we turn
Field turnspeed:Int
' Shaking variables
Field shakex:Int,shakey:Int
Field shaketime:Int
' The path towards the turret
Field pathmap:Int[][]
Field maxrange:Int=10 'turrent tile range
Method New(x:Int,y:Int)
'give the turret a unique id number
Repeat
Local nid:Int=Rnd(18972313)
Local change:Bool=True
For Local i:=Eachin myzombie
If nid = id Then change=False
Next
If change = True
Self.id = nid
Exit
End If
Forever
tx = x*mymap.tw
ty = y*mymap.th
' set the turret radius
tr = mymap.tw/2
' a random turnspeed
turnspeed = Rnd(1,10)
' How fast can we shoot
maxshootdelay = Rnd(3,15)
pathmap = New Int[mymap.mw][]
For Local i:=0 Until mymap.mw
pathmap[i] = New Int[mymap.mh]
Next
' create the path towards the turret
createpathmap()
End Method
Method update()
shootdelay += 1
' shooting here
' check if there are zombies on the map
If Not myzombie.IsEmpty
Local ntx:Int=-1,nty:Int=-1 ' New target x And y
Local sdist:Int=1000 'shortest distance
' find zombie closest to turret
For Local i:=Eachin myzombie
Local d:Int=distance(tx,ty,i.zx,i.zy)
' if within the turret range
If d<mymap.tw*maxrange
If d<sdist And clearshot(i.zx,i.zy) Then
sdist = d
ntx = i.zx
nty = i.zy
End If
End If
Next
If ntx<>-1
shootangle = getangle(tx,ty,ntx,nty)+Rnd(-5,5)
targetx = ntx
targety = nty
notarget = False
Else
notarget = True
End If
Else
notarget = True
End If
'if we have a target
If notarget = False Then
'turn the turrent to target
For Local i:=0 Until turnspeed
If currentangle <> shootangle
currentangle += turndirection(shootangle)
If currentangle>180 Then currentangle = -180
If currentangle<-180 Then currentangle = 180
End If
Next
' if we are aimed and reloaded then shoot
If shootdelay > maxshootdelay
shootdelay = 0
If currentangle = shootangle
' add a shake in the opposite direction
' of the barrel.
shakex = -Cos(currentangle)*2
shakey = -Sin(currentangle)*2
shaketime = 4
mybullet.AddLast(New bullet(tx+Cos(shootangle)*tr,ty+Sin(shootangle)*tr,targetx,targety))
End If
End If
End If
End Method
' Do we have a clear shot at the zombie
' no walls in the way...
Method clearshot:Bool(x2:Int,y2:Int)
Local x1:Float=tx
Local y1:Float=ty
Local angle:Int = getangle(x1,y1,x2,y2)
For Local i:=0 Until 320
x1+=Cos(angle)*1
y1+=Sin(angle)*1
Local x3:Int=x1/mymap.tw
Local y3:Int=y1/mymap.th
' if outside map then skip
If x3<0 Or y3<0 Or x3>=mymap.mw Or y3>=mymap.mh Then Continue
' if we touch a wall then return false
If mymap.map[x3][y3] = 0 Or mymap.map[x3][y3] = 2 Then Return False
' if we touch the zombie then return true
If circleoverlap(x1,y1,4,x2,y2,4) Then Return True
'??
If i>200 Then Return True
Next
Return False
End Method
' returns -1(left) or 1 right to turn towards the closest turn
Method turndirection(destangle:Int)
If destangle<-180 Then destangle=-180
If destangle>180 Then destangle=180
Local myangle:Int=currentangle
Local d1:Int
'turn left
While myangle <> destangle
myangle-=1
If myangle<-180 Then myangle = 180
d1+=1
Wend
myangle = currentangle
Local d2:Int
While myangle <> destangle
myangle+=1
If myangle>180 Then myangle = -180
d2+=1
Wend
If d2>d1 Then Return -1 Else Return 1
End Method
' Here we create a floodfill map for pathfinding
Method createpathmap()
Local sx:Int=tx/mymap.tw
Local sy:Int=ty/mymap.th
'flood the map with distance from
'sx and sy
' Create a list with a class inside it (the class has
' the x and y variables)
Local ol:List<pathnode> = New List<pathnode>
' Add the start position on the list
ol.AddLast(New pathnode(sx,sy))
' set the cloes map at the start position to distance 1
pathmap[sx][sy] = 1
' some helper arrays. We can determine the top,right,and bottom
' and left position cells with these numbers.
Local dx:Int[] = [0,1,0,-1]
Local dy:Int[] = [-1,0,1,0]
' While there is contents in the list
While ol.Count <> 0
' Get the current location
Local x1:Int=ol.First.x
Local y1:Int=ol.First.y
' Remove the current location from the list
ol.RemoveFirst
' Get 4 new positions around the current positions
For Local i:=0 Until 4
' Set new x and y
Local nx:Int=x1+dx[i]
Local ny:Int=y1+dy[i]
' If the coordinates are inside the map
If nx>=0 And ny>=0 And nx<mymap.mw And ny<mymap.mh
' If the closedmap is not written to yet
If pathmap[nx][ny] = 0 And mymap.map[nx][ny] = 1
' Set the new distance based on the current distance
pathmap[nx][ny] = pathmap[x1][y1] + 1
' Add new position to the list
ol.AddLast(New pathnode(nx,ny))
End If
End If
Next
Wend
End Method
Method draw()
Local tx:Int = tx+shakex
Local ty:Int = ty+shakey
shaketime-=1
If shaketime < 0 Then shakex = 0 ; shakey = 0
'draw the turret
SetColor 0,255,255
DrawCircle tx,ty,tr
'draw the barrel
Local x2:Int,y2:Int
x2 = Cos(currentangle)*tr
y2 = Sin(currentangle)*tr
DrawCircle tx+x2,ty+y2,tr/2
End Method
End Class
Class zombie
Field id:Int
Field hastarget:Bool=False 'is he heading towards a target
' zombie x and y and radius
Field zx:Float,zy:Float,zr:Int=16
Field tx:Int,ty:Int
Field angle:Int
Field movementspeed:Float=.5
Field deleteme:Bool=False
Field hitpoints:Int
Field flash:Bool=False
Field flashtime:Int
Field pathmap:Int[][]
Method New(x:Int,y:Int)
zx = x*mymap.tw
zy = y*mymap.th
'set the zombie radius
zr = mymap.tw/2
hitpoints = Rnd(1,4)
If Rnd(100)<2 Then hitpoints*=5
movementspeed = Rnd(0.15,0.35)
' This holds the path for the zombie (he
' moves towards the smaller number
pathmap = New Int[mymap.mw][]
For Local i:=0 Until mymap.mw
pathmap[i] = New Int[mymap.mh]
Next
End Method
Method update()
Local done:Bool=False
' if there is a turret on the map or more
If Not myturret.IsEmpty And hastarget=False
Local targetid:Int=-1
Local ntx:Int,nty:Int
Local cdist:Int=10000
' find the closest
For Local i:=Eachin myturret
Local d:Int=distance(zx,zy,i.tx,i.ty)
If d<cdist
cdist = d
targetid = i.id
hastarget = True
ntx = i.tx
nty = i.ty
End If
Next
' copy the pathmap from this turrent to
' the pathmap of the zombie
For Local i:=Eachin myturret
If targetid = i.id
For Local y:=0 Until mymap.mh
For Local x:=0 Until mymap.mw
pathmap[x][y] = i.pathmap[x][y]
Next
Next
End If
Next
tx = zx
ty = zy
' angle = getangle(zx,zy,ntx,nty)
End If
'move the zombie to new grid if he arived
'at the current grid
If circleoverlap(zx,zy,2,tx,ty,2)
Local cn:Int=pathmap[zx/mymap.tw][zy/mymap.th]
' above/right/bottom/left (4 directions)
Local mx:Int[] = [0,1,0,-1]
Local my:Int[] = [-1,0,1,0]
' store the floodvalues around the zombie
Local myx:Stack<Int> = New Stack<Int>
Local myy:Stack<Int> = New Stack<Int>
Local myv:Stack<Int> = New Stack<Int>
' read around the zombie for direction (pathmapflood)
For Local i:=0 Until 4
Local x3:Int=(zx/mymap.tw)+mx[i]
Local y3:Int=(zy/mymap.th)+my[i]
If x3<0 Or y3<0 Or x3>=mymap.mw Or y3>=mymap.mh Then Continue
Local d:Int=pathmap[x3][y3]
If d>0 Then
myx.Push(zx+(mx[i]*mymap.tw))
myy.Push(zy+(my[i]*mymap.th))
myv.Push(d)
End If
Next
'choose lowest new direction
Local lowest:Int=1414
For Local i:=0 Until myx.Length()
If myv.Get(i)<lowest Then
lowest = myv.Get(i)
tx = myx.Get(i)
ty = myy.Get(i)
End If
Next
' set the angle we are going to head in
angle = getangle(zx,zy,tx,ty)
End If
zx += Cos(angle) * movementspeed
zy += Sin(angle) * movementspeed
'if the zombie collides with a turrent then
' flag turret for deletion
For Local i:=Eachin myturret
If circleoverlap(zx,zy,zr,i.tx,i.ty,i.tr)
i.deleteme = True
' move the turret far away so the zombies can pick
' the next nearest target
i.tx = -1000
i.ty = -1000
For Local ii:=Eachin myzombie
ii.hastarget = False
Next
End If
Next
End Method
Method draw()
If flash = False
SetColor 255,0,0
flashtime = 4
Else
SetColor 255,255,255
flashtime -= 1
If flashtime < 0 Then flash = False
End If
DrawOval zx,zy,mymap.tw,mymap.th
Return
SetColor 255,255,255
For Local y1:=-2 To 2
For Local x1:=-2 To 2
Local x2:Int=zx/mymap.tw+x1
Local y2:Int=zy/mymap.th+y1
DrawText pathmap[x2][y2],zx+(x1*mymap.tw),zy+(y1*mymap.th)
Next
Next
End Method
End Class
Class map
Field map:Int[][]
Field mw:Int,mh:Int,tw:Float,th:Float
Method New(mw:Int,mh:Int)
Self.mw = mw
Self.mh = mh
tw = Float(screenwidth) / Float(mw)
th = Float(screenheight) / Float(mh)
map = New Int[mw][]
For Local i:=0 Until mw
map[i] = New Int[mh]
Next
newmap()
End Method
Method newmap()
For Local y=0 Until mh
For Local x=0 Until mw
map[x][y] = 0
Next
Next
drawrectinmap(Rnd(10,mw-10),Rnd(10,mh-10),5,3)
For Local i=0 Until 50
makeroom()
Next
makewalls()
End Method
Method makewalls()
' put walls on the map
For Local y=1 Until mh-1
For Local x=1 Until mw-1
If map[x][y] = 0
If map[x+1][y] = 1
map[x+1][y] = 2
End If
End If
If map[x][y] = 1
If map[x+1][y] = 0
map[x][y] = 2
End If
End If
If map[x][y] = 1
If map[x][y+1] = 0
map[x][y] = 2
End If
End If
If map[x][y] = 0
If map[x][y+1] = 1
map[x][y+1] = 2
End If
End If
Next
Next
End Method
Method makeroom:Bool()
'find suitable place to make room
Local exitloop:Bool=False
Local cnt:Int=0
While exitloop = False
cnt+=1
If cnt>8000 Then exitloop=True
Local x:Int=Rnd(5,mw-8)
Local y:Int=Rnd(5,mh-8)
Local roomw:Int=Rnd(4,8)
Local roomh:Int=Rnd(4,8)
Local pass1:Bool=True
For Local y1=0 Until roomh
For Local x1=0 Until roomh
If map[x1+x][y1+y] = 1 Then pass1=False
Next
Next
Local pass2:Bool=False
If pass1=True Then
For Local y1=3 To roomh-3
If map[x-1][y+y1] = 1 Then pass2=True
If map[x+roomw][y+y1] = 1 Then pass2=True
Next
For Local x1=3 To roomw-3
If map[x+x1][y-1] = 1 Then pass2 = True
If map[x+x1][y+roomh] = 1 Then pass2 = True
Next
End If
If pass2 = True Then
drawrectinmap(x,y,roomw,roomh)
Return
End If
Wend
End Method
Method issuitable:Bool(x:Int,y:Int,w:Int,h:Int)
For Local y1=0 Until h
For Local x1=0 Until w
If map[x+x1][y+y1] = 1 Then Return False
Next
Next
Return True
End Method
Method drawrectinmap(x:Int,y:Int,w:Int,h:Int)
For Local y1=0 Until h
For Local x1=0 Until w
map[x+x1][y+y1] = 1
Next
Next
End Method
Method draw()
For Local y:=0 Until mh
For Local x:=0 Until mw
If map[x][y] = 0 Then
SetColor 100,100,100
DrawRect x*tw,y*th,tw+1,th+1
End If
If map[x][y] = 1 Then
SetColor 0,0,0
DrawRect x*tw,y*th,tw+1,th+1
End If
If map[x][y] = 2 Then
SetColor 200,100,100
DrawRect x*tw,y*th,tw+1,th+1
End If
Next
Next
End Method
End Class
Global zombieskilled:Int=0
Global mymap:map
Global myturret:List<turret>
Global myzombie:List<zombie>
Global mybullet:List<bullet>
Class MyGame Extends App
Field difficulty:Int=2
Method OnCreate()
SetUpdateRate(60) 'speed of the refresh
Seed = GetDate[4]+GetDate[5] ' random values based on the date
screenwidth = DeviceWidth
screenheight = DeviceHeight
newmap() 'create a new map
End Method
Method OnUpdate()
' update the zombie and bullet and turrets
For Local i:=Eachin myzombie
i.update()
Next
For Local i:=Eachin mybullet
i.update()
Next
For Local i:=Eachin myturret
i.update()
Next
' delete from the lists those that were destroyed
For Local i:=Eachin myzombie
If i.deleteme = True Then myzombie.Remove(i)
Next
For Local i:=Eachin mybullet
If i.deleteme = True Then mybullet.Remove(i)
Next
For Local i:=Eachin myturret
If i.deleteme = True Then myturret.Remove(i)
Next
' if pressed mouse or space then reset difficulty
' and create new map
If MouseHit(MOUSE_LEFT) Or KeyHit(KEY_SPACE) Then
difficulty = 2
newmap
End If
' spawn new zombie
If Rnd(200)<difficulty Then placezombie()
' Increase difficulty
If Rnd(500)<2 Then difficulty+=1
' if there are no more turrets then start new map
If myturret.IsEmpty Then difficulty = 2 ; newmap()
End Method
Method OnRender()
Cls 0,0,0
mymap.draw()
For Local i:=Eachin myturret
i.draw()
Next
For Local i:=Eachin myzombie
i.draw()
Next
For Local i:=Eachin mybullet
i.draw()
Next
SetColor 255,255,255
DrawText "Zombies and Turrets on random maps - Press Space or Mouse to create new map",0,0
DrawText "Zombies Killed : "+zombieskilled,0,20
End Method
End Class
Function newmap()
' reset our variable that holds track
' of the zombies killed
zombieskilled = 0
' create a variable with a value to create map with
Local s:Int=Rnd(30,60)
mymap = New map(s,s)
' (re)create the turret zombie and bullet classes
myturret = New List<turret>
myzombie = New List<zombie>
mybullet = New List<bullet>
' add a number of turrets
Local numturrets:Int=Rnd(3,s/5)
For Local i:=0 Until numturrets
placeturret()
Next
End Function
' Here we place a new zombie on the map
Function placezombie()
Repeat
' create a x and y value
Local x:Int=Rnd(mymap.mw)
Local y:Int=Rnd(mymap.mh)
' if on the map under these new
' coordinates there is no wall
If mymap.map[x][y] = 1
' be sure it is not placed to near
' a turret.
Local notnearturret:Bool=True
For Local i:=Eachin myturret
If distance(i.tx,i.ty,x*mymap.tw,y*mymap.th) < mymap.tw*6 Then notnearturret=False
Next
If notnearturret = True
myzombie.AddLast(New zombie(x,y))
Exit
End If
End If
Forever
End Function
''
' Place a turret on the map
Function placeturret()
Repeat
Local x:Int=Rnd(1,mymap.mw-1)
Local y:Int=Rnd(1,mymap.mh-1)
' if not on a wall then make him
If mymap.map[x][y] = 1
Local a:Int=mymap.map[x-1][y]
Local b:Int=mymap.map[x+1][y]
Local c:Int=mymap.map[x][y-1]
Local d:Int=mymap.map[x][y+1]
'spawn not near/in edge
If a<>1 Or b<>1 Or c<>1 Or d<>1
Else
myturret.AddLast(New turret(x,y))
Exit
Endif
End If
Forever
End Function
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1)+Abs(y2-y1)
End Function
Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Local dx = x2 - x1
Local dy = y2 - y1
Return ATan2(dy,dx)+360 Mod 360
End Function
Function circleoverlap:Bool(x1:Int,y1:Int,r1:Int,x2:Int,y2:Int,r2:Int)
Local dx:Int = x1-x2
Local dy:Int = y1-y2
Local r:Int = r1+r2
If dx*dx+dy*dy <= r*r Then Return True Else Return False
End
Function Main()
New MyGame()
End Function