Grumpy Gamer

Ye Olde Grumpy Gamer Blog. Est. 2004

Sep 3, 2025

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}