Code archives/Algorithms/Pac Man

This code has been declared by its author to be Public Domain code.

Download source code

Pac Man by Vic 3 Babes
(Posted 20 years ago)
Based on an old Phillips Console version that I used to love, and which was later updated on the Amiga as the excellent Deluxe Pac Man.

This is just the basic game engine. There are only 11 levels, but I have also provided a level editor which saves the levels as data statements that you can paste - see Pac Man Editor in the Code Archives. It takes - ooh - about a minute to design a level :)

I wrote this for a tutorial - but when I have time, I may actually turn it into a proper game - with the bonus items that appear in the middle like

E - X - T - R - A (collect 'em all for extra life (Mr Do?))
Slow Down
Speed Up
Glue (Freezes ghosts)
Gun (Rack-up big scores)
etc.

---

Each array element has its bits set as follows:

bit 0 - Wall North
bit 1 - Wall East
bit 2 - Wall South
bit 3 - Wall West
------------------------------------------
bit 4 - This array element is Ghost-pen if set
bit 5 - Pill in this square
bit 6 - Power-pill in this square
bit 7 - Chomped ghosts move down here to enter pen at funny x-co-ordinate
------------------------------------------
bit 8 - Pen is North from here
bit 9 - Pen is East from here
bit 10- Pen is South from here
bit 11- Pen is West from here
------------------------------------------
bit 12- Pac Man is North from here
bit 13- Pac Man is East from here
bit 14- Pac Man is South from here
bit 15- Pac Man is West from here
------------------------------------------
bit 16- Warp Square - array elements off sides of screen
bit 17- Illegal - array element is outside play area

---

The AI with regard to the path-finding feels about right - as far as the feel of the older games go - but at one point, instead of compass direction to Pac Man, I had bit 18 onwards holding a value that showed how many squares away Pac Man was. I then got the ghosts to alter the values in the path as they went along - causing ghosts behind them to split-up in a different direction - made the game a lot harder, and made the ghosts look like they knew what they were doing - however, altering the path as they went along also caused it to affect ghosts coming from other directions as well - which wasn't good. For this reason, I took it out - and tried an alternative.

I tried to get the ghosts to set bits 18-21 to indicate that they had entered this square from a certain direction, so that ghosts thinking of entering the square from the same direction might think twice about it, causing them to split-up - whereas ghosts entering from a different direction would be unaffected. Results are encouraging, but it needs more work, because you have to determine how long it should be before those bits are cleared.

---

It was taking 3 milliseconds (2 without debugging) to draw the level, including the walls and pills - so I decided to go oldskool. Since the walls never need redrawing, and only the pill that Pac Man eats needs removing, the code grabs the background before drawing the characters, and then puts it back before moving them. Now takes 0 milliseconds per frame - even with debugging on.

---

You can view path data at any time by pressing the appropriate function key - F9 shows the pre-calculated path back to the Ghost Pen for when a ghost has been chomped (done by the Editor). F10 shows the current path to Pac Man. The code then waits for a keypress - so if you press the same function key again, you can advance the path information. The path to Pac Man is updated every 16 pixels, so that the square that Pac Man is moving into gets set as the new start position for the path. The path isn't updated if Pac Man doesn't move.

You can also turn collision-detection off with F5.

---

Could have used a viewport - but too much mucking about when drawing score and stuff - so black rectangles drawn either side of level to cover characters moving off and on.

---

The final version should use banks for the levels - that way you can warp to certain levels, without having loads of Restore commands in a Select Map Number thingy. Will also use a bank for the path-finding (instead of types).

---

Can't supply the GFX, so had to create them, and take out references to animation frame numbers - oh - and it's better with sound, natch! If you want to add and play sfx, and have suitable wavs lying around, they should be played in the following functions

Dead() - you know the sound
Move_PacMan() - eat pill, and eat powerpill
Check_Collided() - eat ghost noise

Plus any others you might want to add.

The max-multiplier bonus starts at 1000. Each time you complete a level with maximum points, without dying, the max-multiplier is doubled - 2000, 4000, 8000 etc. Die, or fail to get max points, and the max-multiplier goes back to 1000. Maximum points is:

(numpills * 10) + (numpowers * 10) + ((3 ghosts) * numpowers)

Game is probably too fast if your monitor refreshes at more than 60hz - so you'll need to change it to do a flip false on a timer - but looks horribly jittery - I tried it.

I know that one of the ghosts should have rgb values of 255,0,255, but I prefer purple.

And it's good fun to play - but needs joypad support adding.
;Harlequin Software 2007

;#######################################
;REMEBER TO KEEP NUM_MAPS IN SYNC WITH ANY YOU ADD REMOVE!!
;The game loops back to level 1 when you get to the end - could write some code
;to reduce the amount of time that gosts are scared SCARED_TIME every time the levels loop

;CONSTANTS YOU CAN EASILY CHANGE
Const SCARED_TIME=350	;number of frames ghosts should be scared after Pac eats powerpill
Const NUM_LIVES=3		;drawing more than 3 will overwrite help-text on screen
Const NUM_MAPS=11		;if you also have the level editor
Const TIMEWARP=16		;number of frames ghosts spend in warp tunnel - 16 allows pac man to get an extra square
						;away from them - but if you start to put it too high, then pathfinding will be affected
						;because would need to take into account time taken to warp - YOU'LL HAVE TO ADJUST THE
						;RELEVANT CODE IN MOVE_GHOSTS() IF YOU WANT A TIMEWARP OF ZERO.  Oh, Rocky!
Const VWAIT_DELAY=10	;amount to delay before asking if vertical blank - don't put it higher
;than the time between monitor refreshes i.e.
;At 60hz refresh rate - monitor refreshes every 1000/60 = 16.666 millisecs - so 14 is probably safe - but no higher
;it helps to keep CPU usage down - Ctrl-Alt-Del - Performance Tab
;You could take the Vwait out and do a flip false on a timer if you prefer - game is probably too fast at more than 60hz

;Don't change any other constants unless modifying the code
Const DIRECTIONS$="NE S   W      P"
Const NORTH=1, EAST=2, SOUTH=4, WEST=8
Const GHOSTPEN=$10, PILL=$20, POWERPILL=$40, ABOVEPEN=$80
Const ANY_PILLS=$60

Const NOT_NORTH=$E, NOT_EAST=$D, NOT_SOUTH=$B, NOT_WEST=$7

Const NORTHEAST=$3, SOUTHEAST=$6, SOUTHWEST=$C, NORTHWEST=$9
;used for ghost pen pathfinding
Const PENSHIFT=8
Const PEN_NORTH = NORTH Shl PENSHIFT
Const PEN_EAST  = EAST  Shl PENSHIFT
Const PEN_SOUTH = SOUTH Shl PENSHIFT
Const PEN_WEST  = WEST  Shl PENSHIFT
Const PAVED=$F00, NOT_PAVED=$FF0FF

Const HORIZONTAL=EAST Or WEST, VERTICAL = NORTH Or SOUTH

Const NO_PILLS=$FFF9F
;used for Pac Man pathfinding
Const PACSHIFT=12
Const PAC_NORTH = NORTH Shl PACSHIFT
Const PAC_EAST  = EAST  Shl PACSHIFT
Const PAC_SOUTH = SOUTH Shl PACSHIFT
Const PAC_WEST  = WEST  Shl PACSHIFT
Const PACPAVED=$F000, NOT_PACPAVED=$3F0FFF

Const WARP=$10000
Const ILLEGAL=$20000
;used in do_paths()
Const NORTHorPACPAVEDorILLEGAL = NORTH Or PACPAVED Or ILLEGAL
Const EASTorPACPAVEDorILLEGAL  = EAST Or PACPAVED Or ILLEGAL
Const SOUTHorPACPAVEDorILLEGAL = SOUTH Or PACPAVED Or ILLEGAL
Const WESTorPACPAVEDorILLEGAL  = WEST Or PACPAVED Or ILLEGAL
;bits for splitting up
Const SPLITSHIFT=18
Const SPLIT_NORTH = NORTH Shl SPLITSHIFT
Const SPLIT_EAST  = EAST  Shl SPLITSHIFT
Const SPLIT_SOUTH = SOUTH Shl SPLITSHIFT
Const SPLIT_WEST  = WEST  Shl SPLITSHIFT
Const SPLIT_PAVED = $3C0000, NOT_PACSPLITPAVED=$30FFF

Const RIGHTARROW=205, LEFTARROW=203, UPARROW=200, DOWNARROW=208
Const F5=63, F9=67, F10=68, ENTER=28

Const PACSTARTX=176, PACSTARTY=160
Const STATIC=0

;ghost states
Const SCARED=1, NORMAL=2, PENNED=4, CHOMPED=$10

Const QUITTED=1, DIED=2, COMPLETED=4, ESCAPED=8, EXITMAINLOOP=$F, ESCAPEHIT=9

Dim level(11,9)
;pacx and pacy are screen co-ordinates - px and py are array co-ordinates that get calculated
;as soon as pacx or pacy change becasue they are used in so many places - better than using px
;every time we need the array position - which is quite a lot
Global pacx, pacy, px, py, pacdir, pacframe, mapnum
Global lives, score, oldscore, ghostschomped, bonus
Global wallcolour, framecount, action
Global numpills, numpowers, leveltotal, levelmax, maxmultiplier
Global staticcount
Global show_paths, collisionoff
Global titletext$, titles

AppTitle "Pac Man"
Graphics 384,320

Global pacman=CreateImage(22,22)
Global spooks=CreateImage(22,22,4)
Global Hwalls=CreateImage(26,4,6)
Global Vwalls=CreateImage(4,26,6)
Global eyes=CreateImage(6,6)
Global pillpics=CreateImage(8,8,8)
Global bg=CreateImage(22,22,3)
Global pacbg=CreateImage(22,22)
Global cover=CreateImage(32,23)
Global scoreboard=CreateImage(150,28)

HandleImage pacman,-5,-5
HandleImage pacbg,-5,-5
HandleImage spooks,-5,-5
HandleImage bg,-5,-5

Type square	;used for flood-fill pathfinding
	Field x, y
End Type

Type ghost
	Field x, y, direction, state, frame, num, xadd, yadd
	Field safe, chompcount, warpcount, scarecount, pencount
	Field warping
End Type

setup_ghosts()
create_tiles()
SetBuffer BackBuffer()
wallcolour=-1: mapnum=1
Repeat
	Cls
	action=False
	Restore titlescreen
	For titles=0 To 17
		If titles=7 Then Color 255,255,0
		Read titletext$
		Text 192*(titles<9),16+(titles Shl 4),titletext$,titles<9,False
	Next
	DrawBlock scoreboard,0,0
	Flip
	Color 255,255,255
	FlushKeys()
	Repeat
		Delay 1
		If KeyHit(QUITTED) Then action=QUITTED
	Until KeyHit(ENTER) Or action
	If action<>QUITTED Then new_game()
	While ((action And ESCAPEHIT)=False) And (lives>0)
		new_level()
		Delay 1000
		Repeat
			If framecount<15 Then framecount=framecount+1 Else framecount=0
			If KeyHit(F10) Then show_paths=1
			If KeyHit(F9) Then show_paths=2
			move_pacman()
			move_ghosts()
			If Not(collisionoff) Then check_collided()
			draw_game()
			If KeyHit(QUITTED) Then action=ESCAPED
			If KeyHit(F5) Then collisionoff=Not(collisionoff)
		Until action And EXITMAINLOOP
		If action=DIED
			dead()
		Else
			If action=COMPLETED
				score=score+leveltotal
				score=score+(500 * lives)
				If leveltotal=levelmax
					score=score+(1000 Shl maxmultiplier)
					maxmultiplier=maxmultiplier+1
				Else
					maxmultiplier=0
				EndIf
			EndIf
		EndIf
	Wend
Until action=QUITTED
End
;####################################
Function check_collided()
If action=COMPLETED Then Return
	For ghosts.ghost=Each ghost
		If RectsOverlap(pacx+9,pacy+9,14,14,ghosts\x+9,ghosts\y+9,14,14)
			Select ghosts\state
				Case SCARED
					ghostschomped=ghostschomped+1
					leveltotal=leveltotal+(100 Shl ghostschomped)
					If ghostschomped=3 Then ghostschomped=0
					ghosts\state=CHOMPED
					ghosts\x=ghosts\x And $FFC	;align to 4 pixels for new movement rate
					ghosts\y=ghosts\y And $FFC
					ghosts\scarecount=0
					ghosts\xadd=4
					ghosts\yadd=4
					ghosts\safe=True
					If ghosts\warping
						ghosts\warping=False
						ghosts\warpcount=0
					EndIf
				Case NORMAL
					If ghosts\warping=False Then action=DIED
			End Select
		EndIf
	Next
Return
End Function
;####################################
Function create_tiles()
Local r, g, b, index
SetBuffer ImageBuffer(pacman)
	Color 255,255,0
	Oval 0,0,22,22
Restore colours
For index=0 To 3
	Read r, g, b
	SetBuffer ImageBuffer(spooks,index)
	Color r, g, b
	Oval 0,0,22,22
	Rect 0,11,22,11
Next
	Color 0,8,0
	Oval 2,2,18,18
	Rect 2,11,18,9
For index=0 To 5
	Read r,g,b
	SetBuffer ImageBuffer(Hwalls,index)
	ClsColor r,g,b
	Cls
	Color r*1.5,g*1.5,b*1.5
	Line 0,2,25,2
	Color r*2,g*2,b*2
	Line 0,1,25,1
	SetBuffer ImageBuffer(Vwalls,index)
	ClsColor r,g,b
	Cls
	Color r*1.5,g*1.5,b*1.5
	Line 1,0,1,25
	Color r*2,g*2,b*2
	Line 2,0,2,25
	SetBuffer ImageBuffer(pillpics,index)
	Color r,g,b
	Oval 0,0,8,8
	Color r*1.5,g*1.5,b*1.5
	Oval 1,1,6,6
	Color r*2,g*2,b*2
	Rect 4,2,2,2
	WritePixel 5,2,$FFFFFF
Next
SetBuffer ImageBuffer(pillpics,6)
	Color 255,255,255
	Oval 2,2,4,4
SetBuffer ImageBuffer(pillpics,7)
	Color 255,255,0
	Oval 0,0,8,8
SetBuffer ImageBuffer(eyes)
	Color 255,255,255
	Oval 0,0,6,6
	Color 0,8,0
	Rect 2,2,2,2
SetBuffer ImageBuffer(scoreboard)
	Color 0,255,0
Return
End Function
;####################################
Function dead()
	Delay 500
	;PlaySound(diedsnd)
	draw_walls()
	Flip False
	DrawImage pacman,pacx,pacy
	For framecount=1 To 11
		DrawBlockRect pacbg,pacx,pacy,0,0,22,framecount Shl 1
		Flip False
		Delay 100
	Next
	score=score+leveltotal
	lives=lives-1: leveltotal=0: maxmultiplier=0
Return
End Function
;####################################
Function do_paths()
Local x, y, numsquares, remove
	;remove=NOT_PACSPLITPAVED Else remove=NOT_PACPAVED
	For y=1 To 8
		For x=0 To 11
			level(x,y)=level(x,y) And NOT_PACPAVED	;lose old path info
		Next
	Next
	;When pacman is half-in one square and half in another - change the start position
	;of the path to the tile he is moving into - this function called every 16 pixels moved
	Select pacdir
		Case NORTH	ty=(pacy+(pacy And $10)) Shr 5: tx=pacx Shr 5
		Case EAST	tx=(pacx+(pacx And $10)) Shr 5: ty=pacy Shr 5
		Case SOUTH	ty=(pacy-(pacy And $10)) Shr 5: tx=pacx Shr 5
		Case WEST	tx=(pacx-(pacx And $10)) Shr 5: ty=pacy Shr 5
		Default
			tx=pacx Shr 5: ty=pacy Shr 5
	End Select
	level(tx,ty)=level(tx,ty) Or PACPAVED	;temporarily set these bits in pac's square so not affected below
	squares.square=New square
	squares\x=tx
	squares\y=ty
	numsquares=1
	Repeat
		squares.square=First square
		x=squares\x: y=squares\y
		Delete squares
		numsquares=numsquares-1
		;test north
		If (level(x,y-1) And SOUTHorPACPAVEDorILLEGAL)=False
		;looks really long but is really just If (level(x,y) and $60004)=False
		;which means wall south, already paved or outside play area
			level(x,y-1)=level(x,y-1) Or PAC_SOUTH
			squares.square=New square
			squares\x=x
			squares\y=y-1
			numsquares=numsquares+1
		EndIf
		;test west
		If x>0
			If (level(x-1,y) And EASTorPACPAVEDorILLEGAL)=False
				level(x-1,y)=level(x-1,y) Or PAC_EAST
				squares.square=New square
				squares\x=x-1
				squares\y=y
				numsquares=numsquares+1
			EndIf
		Else	;warp exit
			If level(x,y) And WARP
				If (level(11,4) And PACPAVED)=False
					level(11,4)=level(11,4) Or PAC_EAST
					squares.square=New square
					squares\x=11
					squares\y=4
					numsquares=numsquares+1
				EndIf
			EndIf
		EndIf
		;test south
		If (level(x,y+1) And NORTHorPACPAVEDorILLEGAL)=False
			level(x,y+1)=level(x,y+1) Or PAC_NORTH
			squares.square=New square
			squares\x=x
			squares\y=y+1
			numsquares=numsquares+1
		EndIf
		;test east
		If x<11
			If (level(x+1,y) And WESTorPACPAVEDorILLEGAL)=False
				level(x+1,y)=level(x+1,y) Or PAC_WEST
				squares.square=New square
				squares\x=x+1
				squares\y=y
				numsquares=numsquares+1
			EndIf
		Else	;warp exit
			If level(x,y) And WARP
				If (level(0,4) And PACPAVED)=False
					level(0,4)=level(0,4) Or PAC_WEST
					squares.square=New square
					squares\x=0
					squares\y=4
					numsquares=numsquares+1
				EndIf
			EndIf
		EndIf
	Until numsquares=0
Return
End Function
;####################################
Function draw_game()
Local x, y, tile, xpos, ypos, eyenum, pathdir, eyex, eyey
	DrawImage pacman,pacx,pacy
	Select pacdir
		Case NORTH	DrawImage eyes,pacx+9,pacy+9
		Case EAST	DrawImage eyes,pacx+17,pacy+9
		Case SOUTH	DrawImage eyes,pacx+9,pacy+17
		Case WEST	DrawImage eyes,pacx+9,pacy+9
		Case STATIC DrawImage eyes,pacx+9,pacy+9
					DrawImage eyes,pacx+17,pacy+9
	End Select
	For ghosts.ghost=Each ghost
		If Not(ghosts\warping)
			If ghosts\state < CHOMPED Then DrawImage spooks,ghosts\x,ghosts\y,ghosts\frame
			DrawImage eyes,ghosts\x+9,ghosts\y+10
			DrawImage eyes,ghosts\x+17,ghosts\y+10
		EndIf
	Next
	If Not(action=died)	;cover side exits with a black tile
		DrawBlock cover,0,132
		DrawBlock cover,350,132
	EndIf
	If (score+leveltotal)>oldscore Then draw_score()
	If show_paths
		For y=1 To 9
			For x=0 To 11
				If Not(level(x,y) And ILLEGAL)
					If show_paths=1
						pathdir=(level(x,y) And PACPAVED) Shr PACSHIFT
					Else
						pathdir=(level(x,y) And PAVED) Shr PENSHIFT
					EndIf
					Text x Shl 5,y Shl 5,Mid$(DIRECTIONS$,pathdir,1)
				EndIf
			Next
		Next
	EndIf
	Delay VWAIT_DELAY
	Flip
	If show_paths
		FlushKeys()
		WaitKey()
		show_paths=False
		draw_walls()
	EndIf
Return
End Function
;####################################
Function draw_score()
	oldscore=(score+leveltotal)
	SetBuffer ImageBuffer(scoreboard)
	Cls
	Text 0,0,"Score: "+oldscore
	Text 0,14,"Level: "+(mapnum-1)+"  MMx"+maxmultiplier
	SetBuffer BackBuffer()
	DrawBlock scoreboard,0,0
Return
End Function
;####################################
Function draw_walls()
Local x, y
Cls
For y=1 To 9
	ypos=y Shl 5
	For x=1 To 11
		DrawBlock pillpics,(x Shl 5)-4,ypos-4,wallcolour
	Next
Next
For y=1 To 8
	ypos=y Shl 5
	For x=1 To 10
		tile=level(x,y)
		xpos=x Shl 5
		If tile And NORTH Then DrawBlock Hwalls,xpos+3,ypos-2,wallcolour
		If tile And WEST Then DrawBlock Vwalls,xpos-2,ypos+3,wallcolour
		If tile And ANY_PILLS Then DrawBlock pillpics,xpos+12,ypos+12,((tile Shr 6) And 1)+6
	Next
Next
For x=1 To 10
	DrawBlock Hwalls,(x Shl 5)+3,286,wallcolour
Next
For y=1 To 8
	If tile And EAST Then DrawBlock Vwalls,350,(y Shl 5)+3,wallcolour
Next
For x=1 To lives
	DrawBlock pacman,2+((x-1) * 25),290
	DrawImage eyes,19+((x-1) * 25),299
Next
DrawBlock cover,176,120
DrawBlock cover,350,133
draw_score()
Text 200,0,"F5  - collision on/off"
Text 90,290,"F9  - Show directions to ghost-pen"
Text 90,304,"F10 - Show directions to Pac Man"
Return
End Function
;####################################
Function get_new_ghost_dir(sign, x, y, direction)
Local n, e, s, w, z, tile, pacdirection
	tile=level(x,y)
	pacdirection=(tile And PACPAVED) Shr PACSHIFT
	; Is there a wall in this dir?     Can't reverse direction unless no alternative
	n=(((tile And NORTH)>0) * 1000) + ((direction=SOUTH) * 500)
	      ;+1/-1 not scared/scared			   moving in opposite direction - can't reverse unless no choice
	n=n+ ( sign * (((tile And PAC_NORTH)=0) + ( (direction Or pacdirection)=VERTICAL  )) )

	e=(((tile And EAST )>0) * 1000) + ((direction=WEST ) * 500)
	e=e+ ( sign * (((tile And PAC_EAST )=0) + ( (direction Or pacdirection)=HORIZONTAL)) )

	s=(((tile And SOUTH)>0) * 1000) + ((direction=NORTH) * 500)
	s=s+ ( sign * (((tile And PAC_SOUTH)=0) + ( (direction Or pacdirection)=VERTICAL  )) )
	
	w=(((tile And WEST )>0) * 1000) + ((direction=EAST ) * 500)
	w=w+ ( sign * (((tile And PAC_WEST )=0) + ( (direction Or pacdirection)=HORIZONTAL)) )
	z=n: direction=NORTH
	If e<z Then z=e: direction=EAST
	If s<z Then z=s: direction=SOUTH
	If w<z Then z=w: direction=WEST
Return direction
End Function
;####################################
Function get_next_map()
Local x, y
	numpills=0: numpowers=0
	For y=0 To 9
		For x=0 To 11
			Read level(x,y)
			If (level(x,y) And PILL) Then numpills=numpills+1 ElseIf (level(x,y) And POWERPILL) Then numpowers=numpowers+1
		Next
	Next
	levelmax=(numpills * 10) + (numpowers * 50) + (1400 * numpowers)
Return
End Function
;####################################
Function move_ghosts()
Local newdir, walls, x, y, canchangedir
For ghosts.ghost=Each ghost
	x=ghosts\x Shr 5: y=ghosts\y Shr 5	;makes lines shorter, and speeds up variable accessing
	newdir=ghosts\direction
	canchangedir=False

	If (Not((ghosts\x And $1F) Or (ghosts\y And $1F)))	;aligned to centre of a tile?
		If level(x,y) And WARP
			If Not(ghosts\warping)
				ghosts\warping=True
				ghosts\warpcount=TIMEWARP
				newdir=STATIC
			Else
				ghosts\warpcount=ghosts\warpcount-1
				If ghosts\warpcount=0
					ghosts\warping=False
					If x = 0
						newdir=WEST: ghosts\x=352: x=11
					Else
						newdir=EAST: ghosts\x=0: x=0
					EndIf
				EndIf
			EndIf
		Else
			canchangedir=True
		EndIf
	Else
		If (ghosts\direction=NORTH) And (ghosts\x=176)	;exiting pen
			If ghosts\y=96
				newdir=(level(5,3) And PACPAVED) Shr PACSHIFT
			EndIf
		EndIf
	EndIf

	Select ghosts\state
		Case NORMAL
			If canchangedir Then newdir=get_new_ghost_dir(1,x,y,newdir)
		Case SCARED
			ghosts\scarecount=ghosts\scarecount-1
			If (ghosts\scarecount=0)
				ghosts\frame=ghosts\num-1
				ghosts\state=NORMAL
				ghosts\x=ghosts\x And $3FE	;realign to even number pixels - or they'll bugger off
				ghosts\y=ghosts\y And $3FE
				If (Not((ghosts\x And $1F) Or (ghosts\y And $1F))) And (ghosts\warping =False) Then canchangedir=True Else canchangedir=False
				ghosts\xadd=2
				ghosts\yadd=2
				If canchangedir Then newdir=get_new_ghost_dir(1,x,y,newdir)
			Else
				If ghosts\scarecount<100
					If ghosts\scarecount And ((ghosts\scarecount / 25) + 1)
						ghosts\frame=ghosts\num-1
					Else
						ghosts\frame=3
					EndIf
				EndIf
				If canchangedir Then newdir=get_new_ghost_dir(-1,x,y,newdir)
			EndIf
		Case CHOMPED
			If level(x,y) And ABOVEPEN
				If ghosts\x=176
					newdir=SOUTH
				Else
					If canchangedir Then newdir=(level(x,y) And PAVED) Shr PENSHIFT
				EndIf
			Else
				If level(x,y) And GHOSTPEN
					ghosts\state=PENNED
					ghosts\frame=ghosts\num-1
					ghosts\chompcount=0
					ghosts\pencount=ghosts\num * 4
					newdir=EAST
					ghosts\xadd=2
					ghosts\yadd=2
				Else
					If canchangedir Then newdir=(level(x,y) And PAVED) Shr PENSHIFT
				EndIf
			EndIf
		Case PENNED
			If ghosts\x=176
				ghosts\pencount=ghosts\pencount-1
				If ghosts\pencount=0
					newdir=NORTH
					ghosts\state=NORMAL
					ghosts\safe=False
				EndIf
			Else
				If canchangedir Then newdir=ghosts\direction Xor HORIZONTAL
			EndIf
	End Select
	ghosts\direction=newdir
	Select newdir
		Case NORTH	ghosts\y=ghosts\y - ghosts\yadd
		Case EAST	ghosts\x=ghosts\x + ghosts\xadd
		Case SOUTH	ghosts\y=ghosts\y + ghosts\yadd
		Case WEST	ghosts\x=ghosts\x - ghosts\xadd
	End Select
	GrabImage bg,ghosts\x,ghosts\y,ghosts\num-1
Next
Return
End Function
;####################################
Function move_pacman()
Local key, x, y
	DrawBlock pacbg,pacx,pacy
	For ghosts.ghost=Each ghost
		DrawBlock bg,ghosts\x,ghosts\y,ghosts\num-1
	Next
	Select pacdir
		Case NORTH	pacy=pacy-2: staticcount=0
		Case EAST	pacx=pacx+2: staticcount=0
		Case SOUTH	pacy=pacy+2: staticcount=0
		Case WEST	pacx=pacx-2: staticcount=0
		Case STATIC staticcount=staticcount+1
	End Select
	px=pacx Shr 5: py=pacy Shr 5
	If KeyDown(UPARROW) Then key=NORTH
	If KeyDown(RIGHTARROW) Then key=EAST
	If KeyDown(DOWNARROW) Then key=SOUTH
	If KeyDown(LEFTARROW) Then key=WEST
	If key
		If (key And VERTICAL)
			If Not(pacx And $1F)
				If key=NORTH
					If Not(level(px,py) And NORTH) Then pacdir=NORTH
				Else
					If Not(level(px,py) And SOUTH) Then pacdir=SOUTH
				EndIf
			EndIf
		Else
			If Not(pacy And $1F)
				If key=EAST
					If Not(level(px,py) And EAST) Then pacdir=EAST
				Else
					If Not(level(px,py) And WEST) Then pacdir=WEST: pacframe=12
				EndIf
			EndIf
		EndIf
	EndIf
	If Not((pacx And $1F) Or (pacy And $1F))	;centre of a square
		If level(px, py) And WARP
			If px=0 Then pacx=352: px=11 Else pacx=0: px=0
		EndIf
		If level(px,py) And ANY_PILLS
			If level(px,py) And POWERPILL
				For ghosts.ghost=Each ghost
					If Not(ghosts\safe)
						ghosts\state=SCARED
						ghosts\frame=3
						ghosts\scarecount=ghosts\scarecount+SCARED_TIME
						ghosts\xadd=1
						ghosts\yadd=1
					EndIf
				Next
				leveltotal=leveltotal+50
				numpowers=numpowers-1
			Else
				leveltotal=leveltotal+10
				numpills=numpills-1
			EndIf
			level(px,py)=level(px,py) And NO_PILLS
			SetBuffer ImageBuffer(pacbg)
			Cls
			SetBuffer BackBuffer()
			DrawBlock pacbg,pacx,pacy
			If (numpills + numpowers)=0 Then action=COMPLETED
		EndIf
		If (pacdir<>STATIC) Or (staticcount=16) Then do_paths()
		If level(px, py) And pacdir Then pacdir=STATIC
	EndIf
	If ((pacx And $1F)=$10) Or ((pacy And $1F)=$10) Then do_paths()	;after moving every 16 pixels
	GrabImage pacbg,pacx,pacy
Return
End Function
;####################################
Function new_game()
Local ghostnum, backgrounds
	lives=NUM_LIVES: score=0: oldscore=0
	mapnum=1
	action=False
	maxmultiplier=0
Return
End Function
;####################################
Function new_level()
	FlushKeys()
	leveltotal=0
	If (action<>DIED)
		If ((mapnum-1) Mod NUM_MAPS) =0 Then Restore leveldata
		get_next_map()
		mapnum=mapnum+1
	EndIf
	action=False
	SeedRnd MilliSecs()
	For ghosts.ghost=Each ghost
		ghosts\x=160+((ghosts\num-1) Shl 4)
		ghosts\y=128
		ghosts\state=PENNED
		If ghosts\num=1 Then ghosts\direction=WEST Else ghosts\direction=EAST
		ghosts\xadd=2
		ghosts\yadd=2
		ghosts\frame=ghosts\num-1
		ghosts\warping=False
		ghosts\safe=True
		ghosts\chompcount=0
		ghosts\scarecount=0
		ghosts\warpcount=0
		ghosts\pencount=1+((ghosts\num>1)*ghosts\num)	;delay exits so not bunched
		SetBuffer ImageBuffer(bg,ghosts\num-1)
		Cls
	Next
	SetBuffer ImageBuffer(pacbg)
	Cls
	SetBuffer BackBuffer()
	ghostschomped=0
	wallcolour=(mapnum - 1) Mod 5
	pacdir=EAST
	framecount=0
	pacx=PACSTARTX: pacy=PACSTARTY
	px=pacx Shr 5: py=pacy Shr 5
	draw_walls()
	GrabImage pacbg,pacx,pacy
	For ghosts.ghost=Each ghost
		GrabImage bg,ghosts\x,ghosts\y,ghosts\num-1
	Next
	draw_game()
Return
End Function
;####################################
Function setup_ghosts()
Local ghostnum
For ghostnum=0 To 2
	ghosts.ghost=New ghost
	ghosts\num=ghostnum+1
Next
Return
End Function
;####################################
.colours
Data 255,0,0, 0,160,0, 192,0,255, 128,128,255
Data 64,64,127
Data 96,32,127
Data 127,64,64
Data 64,127,64
Data 127,127,127
Data 127,80,50
.titlescreen
Data "PAC MAN"
Data "Harlequin Software 2007"
Data ""
Data "ENTER TO PLAY"
Data "ESCAPE TO QUIT"
Data ""
Data "KEYS"
Data "UP, DOWN, LEFT, RIGHT"
Data ""
Data "  Pills                 + 10 points"
Data "  Power-pills           + 50 points"
Data "  Eat Ghosts            + 200, 400, 800"
Data "  Bonus for Max points  + 1000 Shl Multiplier"
Data "  Lives remaining       + 500 points each"
Data ""
Data "  F5  - turn off collision detection"
Data "  F9  - show pre-calced path to ghost-pen"
Data "  F10 - show real-time calced path to Pac Man"
.leveldata
;level 1
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00225,$00225,$00421,$00421,$00825,$00825,$00825,$00843,$20000
Data $20000,$0042A,$00229,$00225,$00421,$00826,$0022C,$00421,$00825,$00823,$0042A,$20000
Data $20000,$0042A,$0012A,$00229,$00220,$002A5,$008A5,$00820,$00823,$0012A,$0042A,$20000
Data $1020D,$00220,$00220,$00126,$0012A,$0001D,$00017,$0012A,$0012C,$00820,$00820,$10807
Data $20000,$0012A,$00128,$00823,$00128,$00825,$00225,$00122,$00429,$00122,$0012A,$20000
Data $20000,$0012A,$0012A,$0022C,$00120,$00825,$00225,$00120,$00826,$0012A,$0012A,$20000
Data $20000,$0012A,$0012C,$00225,$00124,$00823,$00229,$00124,$00825,$00826,$0012A,$20000
Data $20000,$0014C,$00825,$00225,$00225,$00124,$00124,$00825,$00825,$00825,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 2
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00225,$00421,$00825,$00225,$00421,$00825,$00825,$00843,$20000
Data $20000,$0012A,$00229,$00221,$00220,$00423,$00429,$00820,$00821,$00823,$0012A,$20000
Data $20000,$00228,$00124,$00126,$00228,$002A4,$008A4,$00822,$0012C,$00824,$00822,$20000
Data $1020D,$00220,$00225,$00225,$00122,$0001D,$00017,$00128,$00825,$00825,$00820,$10807
Data $20000,$00128,$00821,$00423,$00128,$00825,$00225,$00122,$00429,$00821,$00122,$20000
Data $20000,$0012A,$0022C,$00224,$00122,$00429,$00423,$00128,$00824,$00826,$0012A,$20000
Data $20000,$00128,$00225,$00225,$00124,$00824,$00224,$00124,$00825,$00825,$00822,$20000
Data $20000,$0012C,$00825,$00825,$00825,$00847,$0024D,$00225,$00225,$00225,$00126,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 3
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00221,$00421,$00825,$00823,$00229,$00225,$00421,$00821,$00843,$20000
Data $20000,$00128,$00126,$0022C,$00423,$0012C,$00126,$00429,$00826,$0012C,$00822,$20000
Data $20000,$00228,$00423,$00229,$00220,$002A5,$008A5,$00820,$00823,$00429,$00822,$20000
Data $1020D,$00122,$0022C,$00126,$0012A,$0001D,$00017,$0012A,$0012C,$00826,$00128,$10807
Data $20000,$0012A,$00229,$00423,$00128,$00821,$00221,$00122,$00429,$00823,$0012A,$20000
Data $20000,$00128,$00126,$0022C,$00120,$00826,$0012C,$00120,$00826,$0012C,$00822,$20000
Data $20000,$00128,$00423,$00229,$00126,$00429,$00423,$0012C,$00823,$00429,$00822,$20000
Data $20000,$0024C,$00224,$00124,$00825,$00826,$0022C,$00225,$00124,$00824,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 4
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00421,$00825,$00421,$00421,$00825,$00421,$00825,$00843,$20000
Data $20000,$00128,$00823,$0022C,$00421,$00826,$0022C,$00421,$00826,$00229,$00122,$20000
Data $20000,$0012A,$0022C,$00423,$00228,$002A5,$008A5,$00822,$00429,$00826,$0012A,$20000
Data $1020D,$00220,$00225,$00422,$0012A,$0001D,$00017,$0012A,$00428,$00825,$00820,$10807
Data $20000,$00228,$00225,$00220,$00120,$00825,$00225,$00120,$00820,$00825,$00822,$20000
Data $20000,$0012A,$00229,$00126,$0012C,$00823,$00229,$00126,$0012C,$00823,$0012A,$20000
Data $20000,$0012A,$0012C,$00823,$00229,$00124,$00124,$00823,$00229,$00126,$0012A,$20000
Data $20000,$0014C,$00825,$00124,$00124,$00825,$00225,$00124,$00824,$00825,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 5
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00423,$00429,$00825,$00225,$00423,$00429,$00825,$00843,$20000
Data $20000,$00228,$00225,$00220,$00224,$00423,$00429,$00824,$00820,$00825,$00822,$20000
Data $20000,$0012C,$00823,$0012A,$00229,$002A4,$008A4,$00823,$0012A,$00229,$00126,$20000
Data $1020D,$00225,$00122,$0012A,$0012A,$0001D,$00017,$0012A,$0012A,$00128,$00825,$10807
Data $20000,$00229,$00126,$0012A,$0012C,$00821,$00221,$00126,$0012A,$0012C,$00423,$20000
Data $20000,$00228,$00221,$00124,$00221,$00126,$0012C,$00821,$00124,$00821,$00822,$20000
Data $20000,$0012A,$0012C,$00823,$0012C,$00823,$00229,$00126,$00229,$00126,$0012A,$20000
Data $20000,$0014C,$00825,$00124,$00225,$00124,$00124,$00825,$00124,$00825,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 6
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00423,$00429,$00825,$00225,$00423,$00429,$00825,$00843,$20000
Data $20000,$0012A,$00229,$00420,$00824,$00825,$00225,$00224,$00420,$00823,$0012A,$20000
Data $20000,$00228,$00126,$00228,$00221,$002A5,$008A5,$00821,$00822,$0012C,$00822,$20000
Data $1020D,$00122,$00229,$00126,$0012A,$0001D,$00017,$0012A,$0012C,$00823,$00128,$10807
Data $20000,$0022C,$00120,$00823,$00128,$00825,$00225,$00122,$00429,$00120,$00826,$20000
Data $20000,$00229,$00122,$00228,$00126,$00429,$00423,$0012C,$00822,$00128,$00823,$20000
Data $20000,$0012A,$0012C,$00120,$00825,$00822,$00228,$00225,$00120,$00826,$0012A,$20000
Data $20000,$0014C,$00225,$00124,$00825,$00824,$00124,$00225,$00124,$00825,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 7
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00421,$00825,$00825,$00225,$00225,$00421,$00825,$00843,$20000
Data $20000,$00128,$00823,$00428,$00825,$00825,$00225,$00225,$00422,$00229,$00122,$20000
Data $20000,$0012A,$0012A,$0022C,$00225,$002A5,$008A5,$00825,$00826,$0012A,$0012A,$20000
Data $1020D,$00120,$00824,$00823,$0042B,$0001D,$00017,$0042B,$00229,$00124,$00120,$10807
Data $20000,$00128,$00823,$0012C,$00820,$00825,$00225,$00220,$00126,$00229,$00122,$20000
Data $20000,$0012A,$00128,$00825,$00824,$00823,$00229,$00124,$00225,$00122,$0012A,$20000
Data $20000,$0012A,$0012A,$00429,$00821,$00126,$0012C,$00821,$00423,$0012A,$0012A,$20000
Data $20000,$0014C,$00824,$00826,$0012C,$00825,$00225,$00126,$0022C,$00124,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 8
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00421,$00225,$00421,$00825,$00225,$00421,$00825,$00421,$00843,$20000
Data $20000,$0042A,$0042A,$00429,$00826,$00429,$00423,$0022C,$00423,$0042A,$0042A,$20000
Data $20000,$00228,$00224,$00224,$00221,$002A4,$008A4,$00821,$00824,$00824,$00822,$20000
Data $1020D,$00124,$00823,$00229,$00122,$0001D,$00017,$00128,$00823,$00429,$00124,$10807
Data $20000,$00229,$00224,$00122,$0012C,$00821,$00221,$00126,$00128,$00824,$00823,$20000
Data $20000,$00128,$00225,$00124,$00821,$00126,$0012C,$00821,$00124,$00825,$00822,$20000
Data $20000,$0012A,$00229,$00225,$00120,$00825,$00225,$00120,$00825,$00823,$0012A,$20000
Data $20000,$0014C,$00824,$00225,$00124,$00825,$00225,$00124,$00825,$00824,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 9
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00449,$00225,$00225,$00221,$00423,$00429,$00825,$00825,$00421,$00843,$20000
Data $20000,$00228,$00225,$00421,$00826,$0022C,$00224,$00421,$00825,$00826,$0042A,$20000
Data $20000,$00228,$00423,$0042A,$00229,$002A5,$008A5,$00824,$00825,$00825,$00826,$20000
Data $1020D,$00224,$00224,$00224,$00126,$0001D,$00017,$00229,$00221,$00225,$00221,$10207
Data $20000,$00229,$00225,$00221,$00423,$00229,$00221,$00126,$0012C,$00221,$00122,$20000
Data $20000,$0012A,$00229,$00126,$0022C,$00122,$0012C,$00225,$00225,$00126,$0012A,$20000
Data $20000,$00128,$00124,$00225,$00423,$00128,$00825,$00423,$00229,$00225,$00122,$20000
Data $20000,$0024C,$00225,$00225,$00224,$00124,$00225,$00224,$00124,$00225,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 10
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00221,$00221,$00421,$00825,$00225,$00421,$00821,$00821,$00843,$20000
Data $20000,$0012A,$0012A,$0012A,$00428,$00825,$00225,$00422,$0012A,$0012A,$0012A,$20000
Data $20000,$0012A,$0012A,$0012A,$00228,$002A5,$008A5,$00822,$0012A,$0012A,$0012A,$20000
Data $1020D,$00422,$0042A,$0042A,$0012A,$0001D,$00017,$0012A,$0042A,$0042A,$00428,$10807
Data $20000,$00228,$00224,$00224,$00120,$00821,$00221,$00120,$00824,$00824,$00822,$20000
Data $20000,$00128,$00225,$00225,$00122,$0012A,$0012A,$00128,$00825,$00825,$00822,$20000
Data $20000,$00128,$00225,$00225,$00122,$0012A,$0012A,$00128,$00825,$00825,$00822,$20000
Data $20000,$0014C,$00225,$00225,$00124,$00824,$00124,$00124,$00825,$00825,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 11
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00221,$00221,$00221,$00421,$00421,$00821,$00821,$00821,$00843,$20000
Data $20000,$00228,$00220,$00220,$00220,$00420,$00420,$00820,$00820,$00820,$00822,$20000
Data $20000,$00228,$00220,$00220,$00220,$002A4,$008A4,$00820,$00820,$00820,$00822,$20000
Data $1020D,$00120,$00120,$00122,$0012A,$0001D,$00017,$0012A,$00128,$00820,$00820,$10807
Data $20000,$00128,$00120,$00122,$0012C,$00825,$00225,$00126,$00128,$00820,$00822,$20000
Data $20000,$00128,$00120,$00120,$00821,$00821,$00221,$00221,$00120,$00820,$00822,$20000
Data $20000,$00128,$00120,$00120,$00820,$00820,$00120,$00120,$00120,$00820,$00822,$20000
Data $20000,$0014C,$00124,$00124,$00824,$00824,$00124,$00124,$00124,$00824,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000

Comments

Chroma
(Posted 20 years ago)
Very nice work there!

Dustin
(Posted 20 years ago)
Vic's having a little trouble logging in so he asked me to mention that there are 2 bugs. But he'll be posting an update soon with changes in the path finding as well as things like power-ups.

It's a great piece of work in a short amount of code. Looking forward to the update!

Vic 3 Babes
(Posted 20 years ago)
Thanks Dustin - AVG seems to be stopping me logging in, so I'm using a different PC.

Although the updated version (available as Pac Man Update) is much better, I thought you might want to know what the bugs were in this one, and how to fix them.

-------------

There is a bug in the move_ghosts() function. Ghosts can normally only ever change direction when they are exactly in the centre of a square - but the one exception to this rule is when they are entering/exiting the ghost pen. This is easily taken care of when entering the pen, and I thought taken care of when exiting, however, if there are no NORTH walls in the two squares above the ghost-pen, and the square on the left if pointing NORTH to Pac Man, then the ghost will start moving North, and will then never change direction, because it will never be aligned with the centre of a square.

This is a quick-fix which works fine - but might not send the ghost in the optimum direction on all occasions. Replace the Else clause at the end of the first IF with the following.

	Else
		If (ghosts\direction=NORTH) And (ghosts\x=176)	;exiting pen
			If ghosts\y=96
				If pacx > 176 Then newdir=EAST Else newdir=WEST
			EndIf
		EndIf
	EndIf


This wasn't a problem in the version that used a count method for the path to Pac Man, because the ghost simply took the path with the lowest count above the ghost-pen (left or right).

-------------

There is a bug in the Select clause at the top of the do_paths() function - nothing serious - in fact I doubt anyone noticed, but you can see the bug if you press F10 and watch the path data when Pac Man moves North or South - though it works for East and West. The focus square for the path-finding is supposed to be the square that Pac Man is mostly in - and so changes when he has moved across or down a square by 16 pixels. In the Select list, the + and - signs for NORTH and SOUTH should be the other way around.

However, the first call to do_paths() at the end of the move_pacman() function is redundant (see below), and can be removed, which means you can change the select in do_paths() to the following.

Select pacdir
Case NORTH ty=py : tx=px
Case EAST tx=(pacx Shr 5) +1 : ty=pacy Shr 5
Case SOUTH ty=(pacy Shr 5) +1 : tx=pacx Shr 5
Case WEST tx=px : ty=py
End Select

-------------

If (pacdir<>STATIC) Or (staticcount=16) Then do_paths()

The first call to do_paths() at the end of the move_pacman() function is redundant (shown above). Although this version sets bits to indicate NESW directions, the previous (and now revised) version stored a count in bits 18 onwards to indicate how many square away Pac Man was. I also had the ghosts altering these values so that ghosts following them would split-up in a different direction. Calling do_paths() reset the paths that the ghosts had altered - but if Pac Man wasn't moving, the call to do_paths() wasn't made, and this had sorts of weird effects.

For this reason, I added the staticcount variable, and called do_paths() if Pac Man didn't move after a certain amount of time. But after changing the code so that ghosts didn't alter the paths, this call became redundant.

The second call to do_paths is the only one necessary - and calls the function whenever Pac Man is 16 pixels across or down the current 32x32 square.

This means you can remove the staticcount variable, and all references to it.

-------------

There is a field in the Ghost Type that is no longer used, so you can remove it, and references to it.

ghosts\chompcount

---------

The fields, xadd and yadd in the Ghost Type can be amalgamated into one variable - pixeladd, and the code altered accordingly. This is because the ghosts only ever move North or South, never both, so there was no need for two variables.

---------

I've got that many different versions of this game that I'm not sure about this one - better check the version that I posted - but

check_collided() called before characters moved in main loop - should be after

---------

The Last level can't be completed. It could be completed with the path recording method used previously - i.e. storing the count of how many squares away Pac Man was in bits 18 onwards - but when I changed it to NESW, it meant that you could no longer lure the ghosts to the right on the final level, before moving off to the left.

Now that I have gone back to the original method of path storing - the last level can be completed in the updated version.

Vic 3 Babes
(Posted 20 years ago)
This is an updated version which goes back to using a count method for the path-finding. The count of how many squares away an array element is from Pac Man's position is stored in bits 18 onwards, and bits 12-15 are no longer usedl.

I've also added a few bonus items. I only posted this because of the bugs in the first version, and won't be posting any more. However you might like to know that I've also added a new power-up to the full version of the game that I've decided to make.

The editor locks certain walls so that you can't draw or erase them - like the warp exits/ghost-pen. The Editor previously removed this information when it exported the data for the game. I've now altered this slightly, and added a new power-up which gives Pac Man ghost-like abilities - i.e. he can pass through walls - but not locked walls, like the perimter etc. The walls are drawn transparently, and flash briefly when Pac Man is returning to normal.

The power-up in the version below though, is just the mega-pill, which automatically chomps any ghosts on the level. You can also collect EXTRA letters for an extra life.

;Harlequin Software 2007

;#######################################
;REMEBER TO KEEP NUM_MAPS IN SYNC WITH ANY YOU ADD REMOVE!!
;The game loops back to level 1 when you get to the end - could write some code
;to reduce the amount of time that gosts are scared SCARED_TIME every time the levels loop

;CONSTANTS YOU CAN EASILY CHANGE
Const SCARED_TIME=350	;number of frames ghosts should be scared after Pac eats powerpill
Const BONUS_TIME=400	;number of frames before bonus item disappears
Const NUM_LIVES=3		;drawing more than 3 will overwrite help-text on screen
Const NUM_MAPS=11		;if you also have the level editor
Const TIMEWARP=16		;number of frames ghosts spend in warp tunnel - 16 allows pac man to get an extra square
						;away from them - but if you start to put it too high, then pathfinding will be affected
						;because would need to take into account time taken to warp - YOU'LL HAVE TO ADJUST THE
						;RELEVANT CODE IN MOVE_GHOSTS() IF YOU WANT A TIMEWARP OF ZERO.  Oh, Rocky!
Const VWAIT_DELAY=10	;amount to delay before asking if vertical blank - don't put it higher
;than the time between monitor refreshes i.e.
;At 60hz refresh rate - monitor refreshes every 1000/60 = 16.666 millisecs - so 14 is probably safe - but no higher
;it helps to keep CPU usage down - Ctrl-Alt-Del - Performance Tab
;You could take the Vwait out and do a flip false on a timer if you prefer - game is probably too fast at more than 60hz

;Don't change any other constants unless modifying the code
Const EXTRALIFE$="EXTRABM"

Const DIRECTIONS$="NE S   W"
Const NORTH=1, EAST=2, SOUTH=4, WEST=8
Const GHOSTPEN=$10, PILL=$20, POWERPILL=$40, ABOVEPEN=$80
Const ANY_PILLS=$60, NO_PILLS=$FFFFFF9F

Const PAVED=$F00

Const HORIZONTAL=EAST Or WEST, VERTICAL = NORTH Or SOUTH

Const PACPAVED=$FFFC0000, NOT_PACPAVED=$3FFFF
Const PENSHIFT=8, PATHSHIFT=18

Const WARP=$10000
Const ILLEGAL=$20000

Const NORTHorPACPAVEDorILLEGAL = NORTH Or PACPAVED Or ILLEGAL
Const EASTorPACPAVEDorILLEGAL  = EAST Or PACPAVED Or ILLEGAL
Const SOUTHorPACPAVEDorILLEGAL = SOUTH Or PACPAVED Or ILLEGAL
Const WESTorPACPAVEDorILLEGAL  = WEST Or PACPAVED Or ILLEGAL

Const RIGHTARROW=205, LEFTARROW=203, UPARROW=200, DOWNARROW=208
Const F5=63, F9=67, F10=68, ENTER=28

Const PACSTARTX=176, PACSTARTY=160
Const STATIC=0

;ghost states
Const SCARED=1, NORMAL=2, PENNED=4, CHOMPED=$10

Const QUITTED=1, DIED=2, COMPLETED=4
Const ESCAPED=$8, GAMEOVER=$10, EXITGAMELOOP=$18

Const RUBOUT=7
Const BONUSPOINTS=5, MEGAPILL=6

Dim level(11,9)
Dim bgcols(5,2)
;pacx and pacy are screen co-ordinates - px and py are array co-ordinates that get calculated
;as soon as pacx or pacy change becasue they are used in so many places - better than using px
;every time we need the array position - which is quite a lot
Global pacx, pacy, px, py, pacdir, pacframe, mapnum
Global lives, score, oldscore, ghostschomped
Global wallcolour, framecount, action
Global numpills, numpowers, leveltotal, levelmax, maxmultiplier, totaleaten
Global show_paths, collisionoff
Global bonustime, extra, bonusitem, gotbonus

AppTitle "SPºoOKY"
Graphics 384,320,0,2

Global pacman=CreateImage(22,22)
Global spooks=CreateImage(22,22,4)
Global Hwalls=CreateImage(26,4,6)
Global Vwalls=CreateImage(4,26,6)
Global eyes=CreateImage(6,6)
Global pillpics=CreateImage(8,8,8)
Global bg=CreateImage(22,22,3)
Global pacbg=CreateImage(22,22)
Global cover=CreateImage(32,23)
Global scoreboard=CreateImage(150,28)
Global extraletters=CreateImage(15,15,8)

HandleImage pacman,-5,-5
HandleImage pacbg,-5,-5
HandleImage spooks,-5,-5
HandleImage bg,-5,-5
HandleImage extraletters,-9,-9

Type square	;used for flood-fill pathfinding
	Field x, y, count
End Type

Type ghost
	Field x, y, direction, state, frame, num, pixeladd
	Field safe, warpcount, scarecount, pencount
	Field warping
End Type

init()
create_tiles()
SetBuffer ImageBuffer(scoreboard)
Color 0,255,0
SetBuffer BackBuffer()
wallcolour=-1: mapnum=1
Repeat
	If play_game()
		new_game()
		Repeat
			new_level()
			Delay 1000
			Repeat
				If framecount<15 Then framecount=framecount+1 Else framecount=0
				If KeyHit(F10) Then show_paths=1
				If KeyHit(F9) Then show_paths=2
				move_pacman()
				move_ghosts()
				If Not(collisionoff) Then check_collided()
				draw_game()
				If KeyHit(QUITTED) Then action=ESCAPED
				If KeyHit(F5) Then collisionoff=Not(collisionoff)
			Until action
			If action=DIED
				dead()
			Else
				If action=COMPLETED
					score=score+leveltotal+1000
					If leveltotal=levelmax
						score=score+(1000 Shl maxmultiplier)
						maxmultiplier=maxmultiplier+1
					Else
						maxmultiplier=0
					EndIf
				EndIf
			EndIf
		Until action And EXITGAMELOOP
	EndIf
Until action=QUITTED
End
;####################################
Function check_bonus()
	bonustime=bonustime-1
	If (bonustime=0) Or gotbonus
		DrawBlock extraletters,PACSTARTX,PACSTARTY,RUBOUT
		If gotbonus
			If bonusitem >= BONUSPOINTS
				Select bonusitem
					Case BONUSPOINTS	score=score+1000
					Case MEGAPILL		mega_pill()
				End Select
			Else
				extra=extra Xor (1 Shl bonusitem)
				If Not(extra)
					lives=lives+1
					extra=$1F
					Color 0,0,0
					Rect 0,32,26,100,True
					DrawBlock pacman,2+((lives-1) * 25),290
				Else
					DrawBlock extraletters,-2,25+(bonusitem Shl 4),bonusitem
				EndIf
			EndIf
			gotbonus=False
			bonustime=0
		EndIf
		bonusitem=0
	EndIf
Return
End Function
;####################################
Function bonus_item()
	bonustime=BONUS_TIME
	Repeat
		Repeat
			bonusitem=Rand(0,2)	;0 for extra life letters - 1 - bonus points - 2 megapill
		Until (bonusitem>-1) And (bonusitem<3);bug in blitzplus requires this
	Until (1 Shl bonusitem) And extra
	GrabImage extraletters,PACSTARTX,PACSTARTY,RUBOUT
	If bonusitem
		bonusitem=bonusitem+4
	Else
		Repeat
			bonusitem=Rand(0,4)
		Until (bonusitem>-1) And (bonusitem<5)	;bug in blitzplus requires this
	EndIf
	DrawImage extraletters,PACSTARTX,PACSTARTY,bonusitem
Return
End Function
;####################################
Function check_collided()
If action=COMPLETED Then Return
	For ghosts.ghost=Each ghost
		If RectsOverlap(pacx+9,pacy+9,14,14,ghosts\x+9,ghosts\y+9,14,14)
			Select ghosts\state
				Case SCARED
					ghostschomped=ghostschomped+1
					leveltotal=leveltotal+(100 Shl ghostschomped)
					If ghostschomped=3 Then ghostschomped=0
					ghosts\state=CHOMPED
					ghosts\x=ghosts\x And $FFC	;align to 4 pixels for new movement rate
					ghosts\y=ghosts\y And $FFC
					ghosts\scarecount=0
					ghosts\pixeladd=4
					ghosts\safe=True
					If ghosts\warping
						ghosts\warping=False
						ghosts\warpcount=0
					EndIf
				Case NORMAL
					If Not(ghosts\warping) Then action=DIED
			End Select
		EndIf
	Next
	If bonustime
		If RectsOverlap(pacx+9,pacy+9,14,14,PACSTARTX+9,PACSTARTY+9,14,14)
			gotbonus=True
		EndIf
	EndIf
Return
End Function
;####################################
Function create_tiles()
Local r, g, b, index
SetBuffer ImageBuffer(pacman)
	Color 255,255,0
	Oval 0,0,22,22
Restore colours
For index=0 To 3
	Read r, g, b
	SetBuffer ImageBuffer(spooks,index)
	Color r, g, b
	Oval 0,0,22,22
	Rect 0,11,22,11
Next
	Color 0,8,0
	Oval 2,2,18,18
	Rect 2,11,18,9
For index=0 To 5
	Read r,g,b
	SetBuffer ImageBuffer(Hwalls,index)
	ClsColor r,g,b
	Cls
	Color r*1.5,g*1.5,b*1.5
	Line 0,2,25,2
	Color r*2,g*2,b*2
	Line 0,1,25,1
	SetBuffer ImageBuffer(Vwalls,index)
	ClsColor r,g,b
	Cls
	Color r*1.5,g*1.5,b*1.5
	Line 1,0,1,25
	Color r*2,g*2,b*2
	Line 2,0,2,25
	SetBuffer ImageBuffer(pillpics,index)
	Cls
	Color r,g,b
	Oval 0,0,8,8
	Color r*1.5,g*1.5,b*1.5
	Oval 1,1,6,6
	Color r*2,g*2,b*2
	Rect 4,2,2,2
	WritePixel 5,2,$FFFFFF
Next
SetBuffer ImageBuffer(pillpics,6)
	Cls
	Color 255,255,255
	Oval 2,2,4,4
SetBuffer ImageBuffer(pillpics,7)
	Cls
	Color 255,255,0
	Oval 0,0,8,8
SetBuffer ImageBuffer(eyes)
	Color 255,255,255
	Oval 0,0,6,6
	Color 0,8,0
	Rect 2,2,2,2
For index=0 To RUBOUT-1
	SetBuffer ImageBuffer(extraletters,index)
	If index < (RUBOUT-2)
		Color 255,255,0
	Else
		If index=(RUBOUT-2) Then Color 0,255,0 Else Color 255,128,0
	EndIf
	Oval 0,0,15,15
	Color 0,0,0
	Text 3,1,Mid$(EXTRALIFE$,index+1,1)
Next
Return
End Function
;####################################
Function dead()
	Delay 500
	draw_walls()
	Flip False
	DrawImage pacman,pacx,pacy
	For framecount=1 To 11
		DrawBlockRect pacbg,pacx,pacy,0,0,22,framecount Shl 1
		Flip False
		Delay 100
	Next
	lives=lives-1
	If lives=0 Then action=GAMEOVER
	score=score+leveltotal
	leveltotal=0: maxmultiplier=0
Return
End Function
;####################################
Function do_paths()
Local x, y, numsquares, count
	For y=1 To 8
		For x=0 To 11
			level(x,y)=level(x,y) And NOT_PACPAVED	;lose old path
		Next
	Next
	;When pacman is half-in one square and half in another - change the start position
	;of the path to the tile he is moving into - this function called every 16 pixels moved
	Select pacdir
		Case NORTH	ty=py : tx=px
		Case EAST	tx=(pacx Shr 5) +1	: ty=pacy Shr 5
		Case SOUTH	ty=(pacy Shr 5) +1	: tx=pacx Shr 5
		Case WEST	tx=px : ty=py
	End Select
	level(tx,ty)=level(tx,ty) Or PACPAVED
	;above line ensures adjacent square don't affect Pac Man's square, and is reset at end of function
	squares.square=New square
	squares\x=tx
	squares\y=ty
	squares\count=1
	numsquares=1
	Repeat
		squares.square=First square
		x=squares\x: y=squares\y
		count=squares\count
		Delete squares
		numsquares=numsquares-1
		;test north
		If (level(x,y-1) And SOUTHorPACPAVEDorILLEGAL)=False
		;looks really long but is really just If (level(x,y) and $60004)=False
		;which means wall south, already paved or outside play area
			level(x,y-1)=level(x,y-1) Or (count Shl PATHSHIFT)
			squares.square=New square
			squares\x=x
			squares\y=y-1
			squares\count=count+1
			numsquares=numsquares+1
		EndIf
		;test west
		If x>0
			If (level(x-1,y) And EASTorPACPAVEDorILLEGAL)=False
				level(x-1,y)=level(x-1,y) Or (count Shl PATHSHIFT)
				squares.square=New square
				squares\x=x-1
				squares\y=y
				squares\count=count+1
				numsquares=numsquares+1
			EndIf
		Else	;warp exit
			If level(x,y) And WARP
				If (level(11,4) And PACPAVED)=False
					level(11,4)=level(11,4) Or (count Shl PATHSHIFT)
					squares.square=New square
					squares\x=11
					squares\y=4
					squares\count=count+1
					numsquares=numsquares+1
				EndIf
			EndIf
		EndIf
		;test south
		If (level(x,y+1) And NORTHorPACPAVEDorILLEGAL)=False
			level(x,y+1)=level(x,y+1) Or (count Shl PATHSHIFT)
			squares.square=New square
			squares\x=x
			squares\y=y+1
			squares\count=count+1
			numsquares=numsquares+1
		EndIf
		;test east
		If x<11
			If (level(x+1,y) And WESTorPACPAVEDorILLEGAL)=False
				level(x+1,y)=level(x+1,y) Or (count Shl PATHSHIFT)
				squares.square=New square
				squares\x=x+1
				squares\y=y
				squares\count=count+1
				numsquares=numsquares+1
			EndIf
		Else	;warp exit
			If level(x,y) And WARP
				If (level(0,4) And PACPAVED)=False
					level(0,4)=level(0,4) Or (count Shl PATHSHIFT)
					squares.square=New square
					squares\x=0
					squares\y=4
					squares\count=count+1
					numsquares=numsquares+1
				EndIf
			EndIf
		EndIf
	Until numsquares=0
	level(tx,ty)=level(tx,ty) And NOT_PACPAVED
	;reset pac man's square to a count of 0
Return
End Function
;####################################
Function draw_game()
Local x, y, tile, xpos, ypos, eyenum, pathdir, eyex, eyey
	DrawImage pacman,pacx,pacy
	Select pacdir
		Case NORTH	DrawImage eyes,pacx+9,pacy+9
		Case EAST	DrawImage eyes,pacx+17,pacy+9
		Case SOUTH	DrawImage eyes,pacx+9,pacy+17
		Case WEST	DrawImage eyes,pacx+9,pacy+9
		Case STATIC DrawImage eyes,pacx+9,pacy+9
					DrawImage eyes,pacx+17,pacy+9
	End Select
	For ghosts.ghost=Each ghost
		If Not(ghosts\warping)
			If ghosts\state < CHOMPED Then DrawImage spooks,ghosts\x,ghosts\y,ghosts\frame
			DrawImage eyes,ghosts\x+9,ghosts\y+10
			DrawImage eyes,ghosts\x+17,ghosts\y+10
		EndIf
	Next
	If action<>DIED
		DrawBlock cover,0,132
		DrawBlock cover,350,132
	EndIf
	If (score+leveltotal) > oldscore Then draw_score()
	If show_paths
		For y=1 To 9
			For x=0 To 11
				If Not(level(x,y) And ILLEGAL)
					If show_paths=1
						Text x Shl 5,y Shl 5,(level(x,y) Shr PATHSHIFT)
					Else
						pathdir=(level(x,y) And PAVED) Shr PENSHIFT
						Text x Shl 5,y Shl 5,Mid$(DIRECTIONS$,pathdir,1)
					EndIf
				EndIf
			Next
		Next
	EndIf
	Delay VWAIT_DELAY
	Flip
	If show_paths
		FlushKeys()
		WaitKey()
		show_paths=False
		draw_walls()
	EndIf
Return
End Function
;####################################
Function draw_score()
	oldscore=score+leveltotal
	SetBuffer ImageBuffer(scoreboard)
	Cls
	Text 0,0,"Score: "+oldscore
	Text 0,14,"Level: "+mapnum+"  M x "+maxmultiplier
	SetBuffer BackBuffer()
	DrawBlock scoreboard,0,0
Return
End Function
;####################################
Function draw_walls()
Local x, y
Cls
Color bgcols(wallcolour,0),bgcols(wallcolour,1),bgcols(wallcolour,2)
Rect 32,32,320,256
For y=1 To 9
	ypos=y Shl 5
	For x=1 To 11
		DrawImage pillpics,(x Shl 5)-4,ypos-4,wallcolour
	Next
Next
For y=1 To 8
	ypos=y Shl 5
	For x=1 To 10
		tile=level(x,y)
		xpos=x Shl 5
		If tile And NORTH Then DrawBlock Hwalls,xpos+3,ypos-2,wallcolour
		If tile And WEST Then DrawBlock Vwalls,xpos-2,ypos+3,wallcolour
		If tile And ANY_PILLS Then DrawImage pillpics,xpos+12,ypos+12,((tile Shr 6) And 1)+6
	Next
Next
For x=1 To 10
	DrawBlock Hwalls,(x Shl 5)+3,286,wallcolour
Next
For y=1 To 8
	If tile And EAST Then DrawBlock Vwalls,350,(y Shl 5)+3,wallcolour
Next
For x=1 To lives
	DrawBlock pacman,2+((x-1) * 25),290
	DrawImage eyes,19+((x-1) * 25),299
Next
Rect 176,124,32,8
DrawBlock cover,350,133
If (extra And $1F)<>$1F
	For y=0 To 4
		If Not(extra And (1 Shl y)) Then DrawBlock extraletters,-2,25+(y Shl 4),y
	Next
EndIf
draw_score()
Color 192,192,192
Text 200,0,"F5  - collision on/off"
Text 90,290,"F9  - Show paths to ghost-pen (NESW)"
Text 90,304,"F10 - Show paths to Pac Man (values)"
Return
End Function
;####################################
Function get_new_ghost_dir(sign, x, y, currdir)
Local n, e, s, w, z, tile, newdir
	tile=level(x,y)
	;sign reverses polarity - so that ghosts move away from Pac Man when scared
	;calculations are:
	;1 +1000 if there is a wall in this direction?
	;2 +500 if Ghost would have to reverse direction - only do if no choice
	;3 + path value - The count of squares to Pac Man's square
	;4 +1 if path value is 0 (Pac Man's square) and Pac Man's direction isn't opposite
	n=(((tile And NORTH)>0) * 1000) + ((currdir=SOUTH) * 500)
	n=n+(sign * (  (level(x,y-1) Shr PATHSHIFT) + ( ((tile Shr PATHSHIFT)=0) And (pacdir <> SOUTH)) )  )
	e=(((tile And EAST )>0) * 1000) + ((currdir=WEST ) * 500)
	e=e+(sign * (  (level(x+1,y) Shr PATHSHIFT) + ( ((tile Shr PATHSHIFT)=0) And (pacdir <> WEST)) )  )
	s=(((tile And SOUTH)>0) * 1000) + ((currdir=NORTH) * 500)
	s=s+(sign * (  (level(x,y+1) Shr PATHSHIFT) + ( ((tile Shr PATHSHIFT)=0) And (pacdir <> NORTH)) )  )
	w=(((tile And WEST )>0) * 1000) + ((currdir=EAST ) * 500)
	w=w+(sign * (  (level(x-1,y) Shr PATHSHIFT) + ( ((tile Shr PATHSHIFT)=0) And (pacdir <> EAST)) )  )

	z=n: newdir=NORTH
	If e<z Then z=e: newdir=EAST
	If s<z Then z=s: newdir=SOUTH
	If w<z Then z=w: newdir=WEST
Return newdir
End Function
;####################################
Function get_next_map()
Local x, y
	numpills=0: numpowers=0
	For y=0 To 9
		For x=0 To 11
			Read level(x,y)
			If (level(x,y) And PILL) Then numpills=numpills+1 ElseIf (level(x,y) And POWERPILL) Then numpowers=numpowers+1
		Next
	Next
	levelmax=(numpills * 10) + (numpowers * 50) + (1400 * numpowers)
Return
End Function
;####################################
Function init()
Local ghostnum, bgcolour
For ghostnum=0 To 2
	ghosts.ghost=New ghost
	ghosts\num=ghostnum+1
Next
Restore bgcolours
For bgcolour=0 To 5
	Read bgcols(bgcolour,0),bgcols(bgcolour,1),bgcols(bgcolour,2)
Next
SeedRnd MilliSecs()
Return
End Function
;####################################
Function mega_pill()
Local ghostcount=0
	For ghosts.ghost=Each ghost
		If Not(ghosts\safe)
			ghosts\state=CHOMPED
			ghosts\pixeladd=4
			ghosts\x=ghosts\x And $FFC
			ghosts\y=ghosts\y And $FFC
			ghosts\scarecount=0
			ghosts\safe=True
			If ghosts\warping
				ghosts\warping=False
				ghosts\warpcount=0
			EndIf
			ghostcount=ghostcount+1
			score=score+(100 Shl ghostcount)
		EndIf
	Next
Return
End Function
;####################################
Function move_ghosts()
Local newdir, walls, x, y, canchangedir
For ghosts.ghost=Each ghost
	x=ghosts\x Shr 5: y=ghosts\y Shr 5	;makes lines shorter, and speeds up variable accessing
	newdir=ghosts\direction
	canchangedir=False

	If (Not((ghosts\x And $1F) Or (ghosts\y And $1F)))	;aligned to centre of a tile?
		If level(x,y) And WARP
			If Not(ghosts\warping)
				ghosts\warping=True
				ghosts\warpcount=TIMEWARP
				newdir=STATIC
			Else
				ghosts\warpcount=ghosts\warpcount-1
				If ghosts\warpcount=0
					ghosts\warping=False
					If x = 0
						newdir=WEST: ghosts\x=352: x=11
					Else
						newdir=EAST: ghosts\x=0: x=0
					EndIf
				EndIf
			EndIf
		Else
			canchangedir=True
		EndIf
	Else
		If (ghosts\direction=NORTH) And (ghosts\x=176)	;exiting pen
			If ghosts\y=96
				If (level(5,3) Shr PATHSHIFT) > (level(6,3) Shr PATHSHIFT) Then newdir=EAST Else newdir=WEST
			EndIf
		EndIf
	EndIf

	Select ghosts\state
		Case NORMAL
			If canchangedir Then newdir=get_new_ghost_dir(1,x,y,newdir)
		Case SCARED
			ghosts\scarecount=ghosts\scarecount-1
			If (ghosts\scarecount=0)
				ghosts\frame=ghosts\num-1
				ghosts\state=NORMAL
				ghosts\x=ghosts\x And $3FE	;realign to even number pixels - or they'll bugger off
				ghosts\y=ghosts\y And $3FE
				x=ghosts\x Shr 5: y=ghosts\y Shr 5
				If (Not((ghosts\x And $1F) Or (ghosts\y And $1F))) And ((x<>0) And (x<>11)) Then canchangedir=True Else canchangedir=False
				ghosts\pixeladd=2
				If canchangedir Then newdir=get_new_ghost_dir(1,x,y,newdir)
			Else
				If ghosts\scarecount<100
					If ghosts\scarecount And ((ghosts\scarecount / 25) + 1)
						ghosts\frame=ghosts\num-1
					Else
						ghosts\frame=3
					EndIf
				EndIf
				If canchangedir Then newdir=get_new_ghost_dir(-1,x,y,newdir)
			EndIf
		Case CHOMPED
			If level(x,y) And ABOVEPEN
				If ghosts\x=176
					newdir=SOUTH
				Else
					If canchangedir Then newdir=(level(x,y) And PAVED) Shr PENSHIFT
				EndIf
			Else
				If level(x,y) And GHOSTPEN
					ghosts\state=PENNED
					ghosts\frame=ghosts\num-1
					ghosts\pencount=ghosts\num * 4
					newdir=EAST
					ghosts\pixeladd=2
				Else
					If canchangedir Then newdir=(level(x,y) And PAVED) Shr PENSHIFT
				EndIf
			EndIf
		Case PENNED
			If ghosts\x=176
				ghosts\pencount=ghosts\pencount-1
				If ghosts\pencount=0
					newdir=NORTH
					ghosts\state=NORMAL
					ghosts\safe=False
				EndIf
			Else
				If canchangedir Then newdir=ghosts\direction Xor HORIZONTAL
			EndIf
	End Select
	ghosts\direction=newdir
	Select newdir
		Case NORTH	ghosts\y=ghosts\y - ghosts\pixeladd
		Case EAST	ghosts\x=ghosts\x + ghosts\pixeladd
		Case SOUTH	ghosts\y=ghosts\y + ghosts\pixeladd
		Case WEST	ghosts\x=ghosts\x - ghosts\pixeladd
	End Select
	GrabImage bg,ghosts\x,ghosts\y,ghosts\num-1
Next
Return
End Function
;####################################
Function move_pacman()
Local key, x, y
	DrawBlock pacbg,pacx,pacy
	For ghosts.ghost=Each ghost
		DrawBlock bg,ghosts\x,ghosts\y,ghosts\num-1
	Next
	If bonustime Then check_bonus()
	Select pacdir
		Case NORTH	pacy=pacy-2
		Case EAST	pacx=pacx+2
		Case SOUTH	pacy=pacy+2
		Case WEST	pacx=pacx-2
	End Select
	px=pacx Shr 5: py=pacy Shr 5
	If KeyDown(UPARROW) Then key=NORTH
	If KeyDown(RIGHTARROW) Then key=EAST
	If KeyDown(DOWNARROW) Then key=SOUTH
	If KeyDown(LEFTARROW) Then key=WEST
	If key
		If (key And VERTICAL)
			If Not(pacx And $1F)
				If key=NORTH
					If Not(level(px,py) And NORTH) Then pacdir=NORTH
				Else
					If Not(level(px,py) And SOUTH) Then pacdir=SOUTH
				EndIf
			EndIf
		Else
			If Not(pacy And $1F)
				If key=EAST
					If Not(level(px,py) And EAST) Then pacdir=EAST
				Else
					If Not(level(px,py) And WEST) Then pacdir=WEST
				EndIf
			EndIf
		EndIf
	EndIf
	If Not((pacx And $1F) Or (pacy And $1F))
		If level(px, py) And WARP
			If px=0 Then pacx=352: px=11 Else pacx=0: px=0
		EndIf
		If level(px,py) And ANY_PILLS
			If level(px,py) And POWERPILL
				For ghosts.ghost=Each ghost
					If Not(ghosts\safe)
						ghosts\state=SCARED
						ghosts\frame=3
						ghosts\scarecount=ghosts\scarecount+SCARED_TIME
						ghosts\pixeladd=1
					EndIf
				Next
				leveltotal=leveltotal+50
				numpowers=numpowers-1
			Else
				leveltotal=leveltotal+10
				numpills=numpills-1
			EndIf
			totaleaten=totaleaten+1
			If (totaleaten Mod 30)=0 Then bonus_item()
			level(px,py)=level(px,py) And NO_PILLS
			SetBuffer ImageBuffer(pacbg)
			Cls
			SetBuffer BackBuffer()
			DrawBlock pacbg,pacx,pacy
			If (numpills+numpowers)=0 Then action=COMPLETED
		EndIf
		If level(px, py) And pacdir Then pacdir=STATIC
	EndIf
	If ((pacx And $1F)=$10) Or ((pacy And $1F)=$10) Then do_paths()	;after moving every 16 pixels
	GrabImage pacbg,pacx,pacy
Return
End Function
;####################################
Function new_game()
Local ghostnum, backgrounds
	lives=NUM_LIVES: score=0: oldscore=0
	mapnum=0: totaleaten=0
	maxmultiplier=0
	extra=$1F
Return
End Function
;####################################
Function new_level()
	FlushKeys()
	leveltotal=0
	If (action<>DIED)
		wallcolour=mapnum Mod 5
		SetBuffer ImageBuffer(pacbg)
		ClsColor bgcols(wallcolour,0),bgcols(wallcolour,1),bgcols(wallcolour,2)
		SetBuffer BackBuffer()
		If (mapnum Mod NUM_MAPS)=0 Then Restore leveldata
		get_next_map()
		mapnum=mapnum+1
	EndIf
	action=False
	For ghosts.ghost=Each ghost
		ghosts\x=160+((ghosts\num-1) Shl 4)
		ghosts\y=128
		ghosts\state=PENNED
		If ghosts\num=1 Then ghosts\direction=WEST Else ghosts\direction=EAST
		ghosts\pixeladd=2
		ghosts\frame=ghosts\num-1
		ghosts\warping=False
		ghosts\safe=True
		ghosts\scarecount=0
		ghosts\warpcount=0
		ghosts\pencount=1+((ghosts\num>1)*ghosts\num)	;delay exits so not bunched
	Next
	ghostschomped=0
	pacdir=EAST
	framecount=0
	pacx=PACSTARTX: pacy=PACSTARTY
	px=pacx Shr 5: py=pacy Shr 5
	bonustime=0
	draw_walls()
	GrabImage pacbg,pacx,pacy
	For ghosts.ghost=Each ghost
		GrabImage bg,ghosts\x,ghosts\y,ghosts\num-1
	Next
	draw_game()
Return
End Function
;####################################
Function play_game()
Local titletext$, titles
	Cls
	action=False
	Color 255,255,255
	Restore titlescreentext
	For titles=0 To 17
		If titles=7 Then Color 255,255,0
		Read titletext$
		Text 192*(titles<9),16+(titles Shl 4),titletext$,titles<9,False
	Next
	DrawImage scoreboard,0,0
	Delay VWAIT_DELAY
	Flip
	Color 255,255,255
	FlushKeys()
	Repeat
		Delay 1
		If KeyHit(ENTER) Then Return True
		If KeyHit(QUITTED) Then action=QUITTED
	Until action=QUITTED
Return False
End Function
;####################################
.colours
Data 255,0,0, 0,160,0, 192,0,255, 128,128,255
;purple
Data 96,32,127
;red
Data 127,64,64
;green
Data 64,127,64
;grey
Data 127,127,127
;brown
Data 127,80,50
;blue
Data 64,64,127
.bgcolours
Data 32,16,48
Data 64,0,0
Data 0,48,0
Data 32,32,32
Data 64,40,25
Data 0,0,64
.titlescreentext
Data "S P o O K Y"
Data "Harlequin Software 2007"
Data ""
Data "ENTER TO PLAY"
Data "ESCAPE TO QUIT"
Data ""
Data "KEYS"
Data "UP, DOWN, LEFT, RIGHT"
Data ""
Data "      Pills          - 10 points"
Data "      Power-pills    - 50 points"
Data "      Eat Ghosts     - 200, 400, 800"
Data "      Bonus for Max  - 1000 Shl Multiplier"
Data "      Complete level - 1000 points"
Data ""
Data "  F5  - turn on/off collision detection"
Data "  F9  - show pre-calced path to ghost-pen"
Data "  F10 - show real-time calced path to Pac Man"
.leveldata
;level 1
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00225,$00225,$00421,$00421,$00825,$00825,$00825,$00843,$20000
Data $20000,$0042A,$00229,$00225,$00421,$00826,$0022C,$00421,$00825,$00823,$0042A,$20000
Data $20000,$0042A,$0012A,$00229,$00220,$002A5,$008A5,$00820,$00823,$0012A,$0042A,$20000
Data $1020D,$00220,$00220,$00126,$0012A,$0001D,$00017,$0012A,$0012C,$00820,$00820,$10807
Data $20000,$0012A,$00128,$00823,$00128,$00825,$00225,$00122,$00429,$00122,$0012A,$20000
Data $20000,$0012A,$0012A,$0022C,$00120,$00825,$00225,$00120,$00826,$0012A,$0012A,$20000
Data $20000,$0012A,$0012C,$00225,$00124,$00823,$00229,$00124,$00825,$00826,$0012A,$20000
Data $20000,$0014C,$00825,$00225,$00225,$00124,$00124,$00825,$00825,$00825,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 2
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00221,$00421,$00825,$00823,$00229,$00225,$00421,$00821,$00843,$20000
Data $20000,$00128,$00126,$0022C,$00423,$0012C,$00126,$00429,$00826,$0012C,$00822,$20000
Data $20000,$00228,$00423,$00229,$00220,$002A5,$008A5,$00820,$00823,$00429,$00822,$20000
Data $1020D,$00122,$0022C,$00126,$0012A,$0001D,$00017,$0012A,$0012C,$00826,$00128,$10807
Data $20000,$0012A,$00229,$00423,$00128,$00821,$00221,$00122,$00429,$00823,$0012A,$20000
Data $20000,$00128,$00126,$0022C,$00120,$00826,$0012C,$00120,$00826,$0012C,$00822,$20000
Data $20000,$00128,$00423,$00229,$00126,$00429,$00423,$0012C,$00823,$00429,$00822,$20000
Data $20000,$0024C,$00224,$00124,$00825,$00826,$0022C,$00225,$00124,$00824,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 3
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00225,$00421,$00825,$00225,$00421,$00825,$00825,$00843,$20000
Data $20000,$0012A,$00229,$00221,$00220,$00423,$00429,$00820,$00821,$00823,$0012A,$20000
Data $20000,$00228,$00124,$00126,$00228,$002A4,$008A4,$00822,$0012C,$00824,$00822,$20000
Data $1020D,$00220,$00225,$00225,$00122,$0001D,$00017,$00128,$00825,$00825,$00820,$10807
Data $20000,$00128,$00821,$00423,$00128,$00825,$00225,$00122,$00429,$00821,$00122,$20000
Data $20000,$0012A,$0022C,$00224,$00122,$00429,$00423,$00128,$00824,$00826,$0012A,$20000
Data $20000,$00128,$00225,$00225,$00124,$00824,$00224,$00124,$00825,$00825,$00822,$20000
Data $20000,$0012C,$00825,$00825,$00825,$00847,$0024D,$00225,$00225,$00225,$00126,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 4
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00421,$00825,$00421,$00421,$00825,$00421,$00825,$00843,$20000
Data $20000,$00128,$00823,$0022C,$00421,$00826,$0022C,$00421,$00826,$00229,$00122,$20000
Data $20000,$0012A,$0022C,$00423,$00228,$002A5,$008A5,$00822,$00429,$00826,$0012A,$20000
Data $1020D,$00220,$00225,$00422,$0012A,$0001D,$00017,$0012A,$00428,$00825,$00820,$10807
Data $20000,$00228,$00225,$00220,$00120,$00825,$00225,$00120,$00820,$00825,$00822,$20000
Data $20000,$0012A,$00229,$00126,$0012C,$00823,$00229,$00126,$0012C,$00823,$0012A,$20000
Data $20000,$0012A,$0012C,$00823,$00229,$00124,$00124,$00823,$00229,$00126,$0012A,$20000
Data $20000,$0014C,$00825,$00124,$00124,$00825,$00225,$00124,$00824,$00825,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 5
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00423,$00429,$00825,$00225,$00423,$00429,$00825,$00843,$20000
Data $20000,$00228,$00225,$00220,$00224,$00423,$00429,$00824,$00820,$00825,$00822,$20000
Data $20000,$0012C,$00823,$0012A,$00229,$002A4,$008A4,$00823,$0012A,$00229,$00126,$20000
Data $1020D,$00225,$00122,$0012A,$0012A,$0001D,$00017,$0012A,$0012A,$00128,$00825,$10807
Data $20000,$00229,$00126,$0012A,$0012C,$00821,$00221,$00126,$0012A,$0012C,$00423,$20000
Data $20000,$00228,$00221,$00124,$00221,$00126,$0012C,$00821,$00124,$00821,$00822,$20000
Data $20000,$0012A,$0012C,$00823,$0012C,$00823,$00229,$00126,$00229,$00126,$0012A,$20000
Data $20000,$0014C,$00825,$00124,$00225,$00124,$00124,$00825,$00124,$00825,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 6
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00423,$00429,$00825,$00225,$00423,$00429,$00825,$00843,$20000
Data $20000,$0012A,$00229,$00420,$00824,$00825,$00225,$00224,$00420,$00823,$0012A,$20000
Data $20000,$00228,$00126,$00228,$00221,$002A5,$008A5,$00821,$00822,$0012C,$00822,$20000
Data $1020D,$00122,$00229,$00126,$0012A,$0001D,$00017,$0012A,$0012C,$00823,$00128,$10807
Data $20000,$0022C,$00120,$00823,$00128,$00825,$00225,$00122,$00429,$00120,$00826,$20000
Data $20000,$00229,$00122,$00228,$00126,$00429,$00423,$0012C,$00822,$00128,$00823,$20000
Data $20000,$0012A,$0012C,$00120,$00825,$00822,$00228,$00225,$00120,$00826,$0012A,$20000
Data $20000,$0014C,$00225,$00124,$00825,$00824,$00124,$00225,$00124,$00825,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 7
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00225,$00421,$00825,$00825,$00225,$00225,$00421,$00825,$00843,$20000
Data $20000,$00128,$00823,$00428,$00825,$00825,$00225,$00225,$00422,$00229,$00122,$20000
Data $20000,$0012A,$0012A,$0022C,$00225,$002A5,$008A5,$00825,$00826,$0012A,$0012A,$20000
Data $1020D,$00120,$00824,$00823,$0042B,$0001D,$00017,$0042B,$00229,$00124,$00120,$10807
Data $20000,$00128,$00823,$0012C,$00820,$00825,$00225,$00220,$00126,$00229,$00122,$20000
Data $20000,$0012A,$00128,$00825,$00824,$00823,$00229,$00124,$00225,$00122,$0012A,$20000
Data $20000,$0012A,$0012A,$00429,$00821,$00126,$0012C,$00821,$00423,$0012A,$0012A,$20000
Data $20000,$0014C,$00824,$00826,$0012C,$00825,$00225,$00126,$0022C,$00124,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 8
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00421,$00225,$00421,$00825,$00225,$00421,$00825,$00421,$00843,$20000
Data $20000,$0042A,$0042A,$00429,$00826,$00429,$00423,$0022C,$00423,$0042A,$0042A,$20000
Data $20000,$00228,$00224,$00224,$00221,$002A4,$008A4,$00821,$00824,$00824,$00822,$20000
Data $1020D,$00124,$00823,$00229,$00122,$0001D,$00017,$00128,$00823,$00429,$00124,$10807
Data $20000,$00229,$00224,$00122,$0012C,$00821,$00221,$00126,$00128,$00824,$00823,$20000
Data $20000,$00128,$00225,$00124,$00821,$00126,$0012C,$00821,$00124,$00825,$00822,$20000
Data $20000,$0012A,$00229,$00225,$00120,$00825,$00225,$00120,$00825,$00823,$0012A,$20000
Data $20000,$0014C,$00824,$00225,$00124,$00825,$00225,$00124,$00825,$00824,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 9
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00449,$00225,$00225,$00221,$00423,$00429,$00825,$00825,$00421,$00843,$20000
Data $20000,$00228,$00225,$00421,$00826,$0022C,$00224,$00421,$00825,$00826,$0042A,$20000
Data $20000,$00228,$00423,$0042A,$00229,$002A5,$008A5,$00824,$00825,$00825,$00826,$20000
Data $1020D,$00224,$00224,$00224,$00126,$0001D,$00017,$00229,$00221,$00225,$00221,$10207
Data $20000,$00229,$00225,$00221,$00423,$00229,$00221,$00126,$0012C,$00221,$00122,$20000
Data $20000,$0012A,$00229,$00126,$0022C,$00122,$0012C,$00225,$00225,$00126,$0012A,$20000
Data $20000,$00128,$00124,$00225,$00423,$00128,$00825,$00423,$00229,$00225,$00122,$20000
Data $20000,$0024C,$00225,$00225,$00224,$00124,$00225,$00224,$00124,$00225,$00146,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 10
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00221,$00221,$00421,$00825,$00225,$00421,$00821,$00821,$00843,$20000
Data $20000,$0012A,$0012A,$0012A,$00428,$00825,$00225,$00422,$0012A,$0012A,$0012A,$20000
Data $20000,$0012A,$0012A,$0012A,$00228,$002A5,$008A5,$00822,$0012A,$0012A,$0012A,$20000
Data $1020D,$00422,$0042A,$0042A,$0012A,$0001D,$00017,$0012A,$0042A,$0042A,$00428,$10807
Data $20000,$00228,$00224,$00224,$00120,$00821,$00221,$00120,$00824,$00824,$00822,$20000
Data $20000,$00128,$00225,$00225,$00122,$0012A,$0012A,$00128,$00825,$00825,$00822,$20000
Data $20000,$00128,$00225,$00225,$00122,$0012A,$0012A,$00128,$00825,$00825,$00822,$20000
Data $20000,$0014C,$00225,$00225,$00124,$00824,$00124,$00124,$00825,$00825,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
;level 11
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000
Data $20000,$00249,$00221,$00221,$00221,$00421,$00421,$00821,$00821,$00821,$00843,$20000
Data $20000,$00228,$00220,$00220,$00220,$00420,$00420,$00820,$00820,$00820,$00822,$20000
Data $20000,$00228,$00220,$00220,$00220,$002A4,$008A4,$00820,$00820,$00820,$00822,$20000
Data $1020D,$00120,$00120,$00122,$0012A,$0001D,$00017,$0012A,$00128,$00820,$00820,$10807
Data $20000,$00128,$00120,$00122,$0012C,$00825,$00225,$00126,$00128,$00820,$00822,$20000
Data $20000,$00128,$00120,$00120,$00821,$00821,$00221,$00221,$00120,$00820,$00822,$20000
Data $20000,$00128,$00120,$00120,$00820,$00820,$00120,$00120,$00120,$00820,$00822,$20000
Data $20000,$0014C,$00124,$00124,$00824,$00824,$00124,$00124,$00124,$00824,$00846,$20000
Data $20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000,$20000


Code Archives Forum