Have I mentioned that you should Wish List Death by Scrolling now, before you finish reading this?
Here is the code the runs TesterTron3000 in Death by Scrolling.
There is some code not listed that does set up, but the following runs the level.
It’s written in Dinky, a custom language I wrote for Delores based on what we used for Thimbleweeed Park and then used in Return to Monkey Island.
TesterTron3000 is as dumb as a box of rocks, but in some ways that’s what makes it fun to watch.
Before we get into code, here is another sample run.
It’s not the best code I’ve written but far from the worst and it gets the job done. TesterTron3000 has run for over 48 hours and not found a serious bug, so I’m happy.
Source code follows, you’ve been warned…
1function runLevel() {
2 local last_pos
3 local reset_time = gametime()+MINUTES(3) // Reset if level took longer than 3 minutes
4 local dest
5 do {
6 if (PLAYER?.dead) return
7 if (gametime() > reset_time) return
8 local pos
9 if (ROOT(in_camp)) {
10 pos = MAP?.start
11 dest = "start"
12 } else {
13 local targets
14 targets = null
15 if (PLAYER.max_health-PLAYER.health > 1) {
16 print("Looking for heart")
17 targets = entityFindSortedForwardEntities(PLAYER, point(0,1), 10, 360, TYPE_HEART)
18 dest = "heart"
19 }
20 if (!sizeof(targets)) {
21 print("Looking for powerup")
22 targets = entityFindSortedForwardEntities(PLAYER, point(0,1), 10, 360, TYPE_POWERUP)
23 dest = "powerup"
24 }
25 if (sizeof(targets)) {
26 local target = targets[0]
27 pos = entityPos(target)
28 local dist = pos?.y - cameraAt().y
29 if (dist < -6) { // Don't grab powerups near the bottom
30 pos = point(random(5,mapSize(MAP).x-5), entityPos(PLAYER).y+random(5,20))
31 dest = "pos"
32 }
33 } else {
34 pos = point(random(5,mapSize(MAP).x-5), entityPos(PLAYER).y+random(5,20))
35 dest = "pos"
36 }
37 }
38 printf("TesterTron3000 moving to %@ (%@)", pos, dest)
39 dest = pos
40 entityPathTo(PLAYER, dest, 9999)
41 breaktime(0.5) // Need time to finish pathing (happens on a real thread)
42 local bail_time = gametime()+SECONDS(8) // Seconds to path before changing location
43 while(isPathing(PLAYER)) {
44 if (gametime() > reset_time) return // Player probably stuck somewhere
45 if (gametime() > bail_time) break // Took too long on path
46 if (!entityPos(PLAYER)) return // Something is wrong
47 if (!MAP?.end) return // No end pos, something is wrong
48 if (PLAYER?.dead) return // We died
49 if (entityPos(PLAYER).y >= MAP.end) return // Got to end camp
50 local dist = dest.y - cameraAt().y
51 if (dist < -6) break // Too close to the bottom
52 ROOT(took_step)++ // So powerups will tick
53 PLAYER.last_move_vec <- dirToVec(entityFacing(PLAYER))
54 if (PLAYER?.range_image) { // Spin targeting range around
55 imageRotate(PLAYER?.range_image, angle(point(0,0), PLAYER?.last_move_vec))
56 }
57 if (entityPos(PLAYER) == last_pos) break // Didn't move, something is wrong
58 last_pos = entityPos(PLAYER)
59 breaktime(0.1)
60 }
61 // Choose new destination
62 }
63}