OK, last installment.
First thing first, I got rid of all the globals. Globals can be dangerous and sort of defeats the purpose of the safety of encapsulation of code and variables that the functions provide.
As you've seen previously, you can pass a variable or datatype to a function so that it can do stuff with it and return a value.
eg:
---
first = 3
second = 4
result = add(first, second)
function add(a, b)
c = a + b
endFunction c
do
print(str(result))
sync()
loop
---
This will print 7
The problem is though, you can only return one variable from a function. Which is no good if you intend for a function to do many changes to a set of data.
This is where datatypes and arrays come in so handy. There are two ways to skin this particular cat.
You can use assignment as in this example:
type variablesObject
first
second
resultAdd
resultMultiply
endType
variables as variablesObject
variables.first = 3
variables.second = 4
variables = doStuff(variables)
function doStuff(var as variablesObject)
var.resultAdd = var.first + var.second
var.resultMultiply = var.first * var.second
endFunction var
do
print(str(variables.resultAdd))
print(str(variables.resultMultiply))
Sync()
loop
This will print 7 and 12
The other method is to reference the original datatype when passing it into the function.
Like this:
type variablesObject
first
second
resultAdd
resultMultiply
endType
variables as variablesObject
variables.first = 3
variables.second = 4
doStuff(variables)
function doStuff(var ref as variablesObject)
var.resultAdd = var.first + var.second
var.resultMultiply = var.first * var.second
endFunction
do
print(str(variables.resultAdd))
print(str(variables.resultMultiply))
Sync()
loop
Again this will print 7 and 12. Remove the reference however, and nothing is done to the datatype outside of the function, so it will return 0 and 0. The advantage of this method is that it'll keep the changes you make even if you pass into it several datatypes, unlike the first method where you can only return one datatype.
like so:
type variablesObject
first
second
endType
type resultsObject
add
multiply
endType
variables as variablesObject
results as resultsObject
variables.first = 3
variables.second = 4
doStuff(variables, results)
function doStuff( var ref as variablesObject,
res ref as resultsObject)
res.add = var.first + var.second
res.multiply = var.first * var.second
var.first = 10
var.second = 5
endFunction
do
print(str(results.add))
print(str(results.multiply))
print(str(variables.first))
print(str(variables.second))
Sync()
loop
This prints 7, 12, 10 and 5. Having made changes to both the variables and results datatypes inside the function.
So why get rid of globals you may ask? Well, in a smaller project like yours, it doesn't matter much - it is quite managable. In bigger projects though you will soon create problems for yourself if using a plethora of globals. you'll create bugs you will spend the better part of a day if not more to chase down and squat, because a particular global variable is used throughout your project, and somewhere it is doing something it shouldn't that later breaks some other part of your code. This is called implicit coupling, and is something you want to avoid. All couplings should be explicit.
Using local variables over global ones, also stops you making silly mistakes. For instance say you got a global called controlFlag - this controls the state of a host of other functions. Then in one function you need a local variable to control a local flag. You name it controlFlag and assign it a value. Guess what happens? Your entire program breaks. this is called namespace pollution.
With local variables you can use the same name as often as you want in different functions without it affecting any other function. It is encapsulated. Globals however leak out all over the place and will likely cause problems somewhere down the line.
Ok, theory done with, back to your program and the task at hand - catching that cat
Your vetTime() function have been changed to the following:
function vetTime( ctrl ref as controlGlobals,
spr as spriteID,
img as imageID,
nme ref as enemyObject[])
key = 0
for i = 1 to ctrl.enemyPointer
if getSpriteCollision (spr.enemy[i], spr.kitty) = 1
nme[i].x = random (10,775)
nme[i].y = random (10,775)
setSpritePosition(spr.enemy[i], nme[i].x, nme[i].y)
dec ctrl.catches
if ctrl.catches = 0
key = gameLose(spr, img)
endIf
endif
next i
endFunction key
Like when moving the enemies, the check for seeing if any enemy touches the cat is inside a for - next loop. If caught, the enemy is moved to a random position, number of catches (lives) is reduced and if it have reached zero, the GAME OVER screen is shown through the gameLose() function.
This as you can see returns a value that is stored in key, which in turn is sent up the pyramid of functions to gameLoop() where that value will exit gameLoop() and throw you back to the main menu.
A little look at gameLose() whilst we're at it:
function gameLose(spr as spriteID, img as imageID)
key = 0
createSprite(spr.caught, img.caught)
repeat
print("GAME OVER")
print("")
print("Press Escape to continue")
key = getKey()
sync()
until key = 99
deleteSprite(spr.caught)
endFunction key
Nothing too fancy going on here. It displays the game over graphics, and I added some text to it. Then it waits for user input - only one being allowed is to hit the escape key. This is done by once again calling the getKey() function. That is the beauty of functions, it saves you typing out a particular taks more than once. Reduced code redundancy makes for less work and fewer possibilities for bugs to creep into your code.
Here is the working code in full:
// Project: Off to the vet
// Created: 2016-04-08
setWindowTitle("Off to the vet")
setWindowSize(1024, 768, 0)
setVirtualResolution(800, 600)
setOrientationAllowed(1, 1, 1, 1)
#constant maxEnemies 10
type imageID
kitty as integer
wall as integer
bed as integer
enemy as integer
enemy2 as integer
start as integer
caught as integer
background as integer
endType
type spriteID
kitty as integer
wall as integer
bed as integer
enemy as integer[maxEnemies]
start as integer
caught as integer
background as integer
endType
type controlGlobals
score as integer
time as integer
catches as integer
enemyPointer as integer
bedCounter as integer
endType
type kittyObject
x as integer
y as integer
endType
type bedObject
x as integer
y as integer
endType
type wallObject
x as integer
y as integer
endType
type enemyObject
x as integer
y as integer
endType
image as imageID
sprite as spriteID
control as controlGlobals
kitty as kittyObject
bed as bedObject
wall as wallObject
enemy as enemyObject[maxEnemies]
spriteImageIDs(image, sprite)
getMedia(image)
createSprite(sprite.background, image.background)
setSpriteDepth(sprite.background, 999)
local choice = 0
repeat
defaultGlobals(kitty, wall, bed, control)
choice = drawMainMenu(sprite, image)
if choice = 1
gameLoop(sprite, image, kitty, enemy, bed, wall, control)
choice = 0
endif
until choice = 99
function drawMainMenu(spr as spriteID, img as imageID)
key = 0
createSprite(spr.start, img.start)
// my start image got a white background, so I set the text here to be green
setPrintColor(0, 255, 0)
repeat
print("Press Escape To Quit")
print("Press Space to Start")
key = getKey()
sync()
until key = 1 or key = 99
deleteSprite(spr.start)
endFunction key
function gameLoop( spr as spriteID,
img as imageID,
cat ref as kittyObject,
nme ref as enemyObject[],
bd ref as bedObject,
wll ref as wallObject,
ctrl ref as controlGlobals)
key = 0
oldclock = getSeconds()
setPrintColor(255,0,0)
drawGameSprites(spr, img, nme)
setSpritePosition(spr.kitty, cat.x, cat.y)
setSpritePosition(spr.bed, bd.x, bd.y)
setSpritePosition(spr.wall, wll.x, wll.y)
repeat
printc("score: " + str(ctrl.score) + " - ")
printc("catches: " + str(ctrl.catches) + " - ")
print("time: " + str(ctrl.time))
key = getKey()
movePlayer(key, spr, img, cat, bd, ctrl, nme)
enemyChaser(ctrl.enemyPointer, spr, nme, cat)
key = vetTime(ctrl, spr, img, nme)
oldclock = gameTimer(oldclock, ctrl)
if ctrl.time = 0
key = 99
endif
sync()
until key = 99
clearSprite(spr.kitty, spr.bed)
clearSprite(spr.enemy[1], spr.enemy[ctrl.enemyPointer])
endFunction
function getKey()
keyPressed = 0
if getRawKeyPressed(27) // Escape key
keyPressed = 99
elseif getRawKeyPressed(32) // Spacebar
keyPressed = 1
elseif getRawKeyState(38) // Arrow down
keyPressed = 10
elseif getRawKeyState(40) // Arrow up
keyPressed = 11
elseif getRawKeyState(37) // Arrow Left
keyPressed = 12
elseif getRawKeyState(39) // Arrow Right
keyPressed = 13
endif
endFunction keyPressed
function movePlayer( direction,
spr as spriteID,
img as imageID,
cat ref as kittyObject,
bd ref as bedObject,
ctrl ref as controlGlobals,
nme ref as enemyObject[])
lastXpos = cat.x
lastYpos = cat.y
offset = 5
select direction
case 10 // down
dec cat.y, offset
endCase
case 11 // up
inc cat.y, offset
endCase
case 12 // left
dec cat.x, offset
endCase
case 13 // right
inc cat.x, offset
endCase
endSelect
setSpritePosition(spr.kitty, cat.x, cat.y)
wallCollision(lastXpos, lastYpos, spr, cat)
bedCollection(spr, img, bd, ctrl, nme)
endFunction
function wallCollision( lastX,
lastY,
spr as spriteID,
cat ref as kittyObject)
if getSpriteCollision(spr.kitty, spr.wall) = 1
cat.x = lastX
cat.y = lastY
endIf
endFunction
function bedCollection( spr as spriteID,
img as imageID,
bd ref as bedObject,
ctrl ref as controlGlobals,
nme ref as enemyObject[])
if getSpriteCollision(spr.kitty, spr.bed) = 1
playSound(100)
bd.x = random(0, 775)
bd.y = random(0, 575)
setSpritePosition(spr.bed, bd.x, bd.y)
inc ctrl.score
inc ctrl.bedCounter
endIf
if ctrl.bedCounter = 10 and ctrl.enemyPointer < maxEnemies
inc ctrl.enemyPointer
ctrl.bedCounter = 0
spawnEnemy(ctrl.enemyPointer, spr, img, nme)
endif
endFunction
function gameTimer(oldSec, ctrl ref as controlGlobals)
newSec = GetSeconds()
if newSec > oldSec
dec ctrl.time
endif
endFunction newSec
function enemyChaser( pointer,
spr as spriteID,
nme ref as enemyObject[],
cat as kittyObject)
offset = 1
for i = 1 to pointer
if nme[i].x < cat.x
inc nme[i].x, offset
endIf
if nme[i].x > cat.x
dec nme[i].x, offset
endIf
if nme[i].y < cat.y
inc nme[i].y, offset
endIf
if nme[i].y > cat.y
dec nme[i].y, offset
endIf
setSpritePosition(spr.enemy[i], nme[i].x, nme[i].y)
next i
endFunction
function vetTime( ctrl ref as controlGlobals,
spr as spriteID,
img as imageID,
nme ref as enemyObject[])
key = 0
for i = 1 to ctrl.enemyPointer
if getSpriteCollision (spr.enemy[i], spr.kitty) = 1
nme[i].x = random (10,775)
nme[i].y = random (10,775)
setSpritePosition(spr.enemy[i], nme[i].x, nme[i].y)
dec ctrl.catches
if ctrl.catches = 0
key = gameLose(spr, img)
endIf
endif
next i
endFunction key
function gameLose(spr as spriteID, img as imageID)
key = 0
createSprite(spr.caught, img.caught)
repeat
print("GAME OVER")
print("")
print("Press Escape to continue")
key = getKey()
sync()
until key = 99
deleteSprite(spr.caught)
endFunction key
function drawGameSprites( spr as spriteID,
img as imageID,
nme ref as enemyObject[])
createSprite(spr.kitty, img.kitty)
createSprite(spr.wall, img.wall)
createSprite(spr.bed, img.bed)
spawnEnemy(1, spr, img, nme)
endFunction
function spawnEnemy( enemyID,
spr as spriteID,
img as imageID,
nme ref as enemyObject[])
nme[enemyID].x = 10
nme[enemyID].y = 10
if mod(enemyID, 2) = 0
createSprite(spr.enemy[enemyID], img.enemy2)
else
createSprite(spr.enemy[enemyID], img.enemy)
endif
setSpritePosition(spr.enemy[enemyID], nme[enemyID].x, nme[enemyID].y)
endFunction
function clearSprite(start, stop)
for i = start to stop
if getSpriteExists(i)
deleteSprite(i)
endif
next i
endFunction
function spriteImageIDs(img ref as imageID, spr ref as spriteID)
img.kitty = 1
img.wall = 2
img.bed = 3
img.enemy = 4
img.enemy2 = 5
img.start = 6
img.caught = 7
img.background = 8
spr.kitty = 1
spr.wall = 2
spr.bed = 3
spr.start = 4
spr.caught = 5
spr.background = 6
for i = 0 to maxEnemies - 1
spr.enemy[i] = i + 10
next i
endFunction
function defaultGlobals( kitty ref as kittyObject,
wall ref as wallObject,
bed ref as bedObject,
control ref as controlGlobals)
kitty.x = 200
kitty.y = 200
wall.x = 0
wall.y = 0
bed.x = random(0, 775)
bed.y = random(0, 575)
control.score = 0
control.time = 60
control.catches = 3
control.enemyPointer = 1
control.bedCounter = 0
endFunction
function getMedia(img as imageID)
loadImage(img.kitty, "KITTY.jpg")
loadImage(img.wall, "wall.jpg")
loadImage(img.bed, "bed.jpg")
loadImage(img.enemy, "enemy.jpg")
loadImage(img.enemy2, "enemy2.jpg")
loadImage(img.start, "Start.png")
loadImage(img.caught, "CAUGHT.png")
loadImage(img.background, "Background.jpg")
loadSound(100, "catmeow.wav")
endFunction
If you find something a bit baffling, try printing out the entire thing and follow it using a pencil to mark the jump out points and trace what happens to what where.
Happy coding