I think we pretty much sorted with the climb - the climb times out after 10 seconds, so click when is near something verticle
and it checks top of the sprite now so if hits a ceiling, will fall down cancelling a climb mode
// Project: followers sprite engine
// Created: 2019-01-26
// show all errors
SetErrorMode(2)
#constant screenwidth=1024
#constant screenheight=490
#constant fullscreen=1
#constant screenrate=0
#constant ALPHA_CHANNEL_CHECK=40
// set window properties
SetWindowTitle( "followers sprite engine" )
SetWindowSize( screenwidth, screenheight, fullscreen )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( screenwidth, screenheight ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( screenrate, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
global followercount,land,landimage,landmesh,worldpositionx#,platformmesh,exploding,minisprite,imgmesh,meshwidth
global currenttime, in, out, actiontext,outtext,intext,timetext,time#, mouse, actioncurrentlyselected,level
type _actionsprites
spr
countervalue
counter
endtype
global actionsprites as _actionsprites[]
type _followers
spr
followerx#
followery#
mine
build
dig
stopper
climber
exploder
direction
explosionparticle
action$
hitground
actiontimer#
endtype
global followers as _followers[]
level=1
SetupLevel(level)
do
displayscreen()
updatefollowers()
checkkeypresses()
Print( ScreenFPS() )
print(GetRawLastKey())
Render2DFront()
Sync()
loop
function checkkeypresses()
if GetRawKeyPressed(27) then exit
if getpointerx()<5 then inc worldpositionx#
if getpointerx()>screenwidth-5 then dec worldpositionx#
if GetRawKeyState(37) then inc worldpositionx#
if GetRawKeyState(39) then dec worldpositionx#
if GetRawKeyPressed(65) then addfollower() // A
if GetRawKeyPressed(83) // S
followers[0].stopper = -followers[0].stopper
endif
if GetRawKeyPressed(77)
followers[0].mine=-followers[0].mine // M
followers[0].actiontimer#=timer()
endif
if GetRawKeyPressed(68)
followers[0].dig = -followers[0].dig // D
followers[0].actiontimer#=timer()
endif
if GetRawKeyPressed(66)
followers[0].build = -followers[0].build // B
followers[0].actiontimer#=timer()
endif
if GetRawKeyPressed(69) // E
followers[0].exploder = -followers[0].exploder // B
followers[0].actiontimer#=timer()
endif
if GetRawKeyPressed(76) // L
inc level
setuplevel(level)
endif
endfunction
function displayscreen()
SetSpritePosition(mouse,getpointerx(),getpointery())
SetSpritePosition(land,worldpositionx#,0)
SetSpritePosition(minisprite,screenwidth/2,screenheight/2+180)
SetTextPosition(Actiontext,150,screenheight/2+110)
SetTextPosition(outtext,350,screenheight/2+110)
SetTextPosition(intext,550,screenheight/2+110)
SetTextPosition(timetext,750,screenheight/2+110)
for a=0 to actionsprites.length
SetSpriteSize(actionsprites[a].spr,50,50)
SetSpritePosition(actionsprites[a].spr,a*55,screenheight/2+180)
SetTextPosition(actionsprites[a].counter,a*55,screenheight/2+160)
if GetSpriteCollision(mouse,actionsprites[a].spr)=1
x=GetSpriteX(actionsprites[a].spr)-2
y=GetSpriteY(actionsprites[a].spr)-2
c=MakeColor(255,0,0)
DrawBox(x,y,x+54,y+54,c,c,c,c,1)
if GetRawMouseLeftPressed()
actioncurrentlyselected=a
endif
endif
next
x=GetSpriteX(actionsprites[actioncurrentlyselected].spr)-2
y=GetSpriteY(actionsprites[actioncurrentlyselected].spr)-2
c=MakeColor(255,0,0)
DrawBox(x,y,x+54,y+54,c,c,c,c,1)
endfunction
//***************************************************
// Follower Update and other type Functions
//***************************************************
function checktimers(fid)
if timer()-Time#>1
time#=timer()
dec currenttime
SetTextString(timetext,"TIME : " + str(currenttime))
endif
if followers[fid].mine=-1 // if mining then stop after a certain time
if timer()-followers[fid].actiontimer#>50
followers[fid].actiontimer#=timer()
followers[fid].mine = -followers[fid].mine
endif
endif
if followers[fid].build=-1 // if building then stop after a certain time
if timer()-followers[fid].actiontimer#>50
followers[fid].actiontimer#=timer()
followers[fid].build = -followers[fid].build
endif
endif
if followers[fid].dig=-1 // if digging then stop after a certain time
if timer()-followers[fid].actiontimer#>10
followers[fid].actiontimer#=timer()
followers[fid].dig = -followers[fid].dig
endif
endif
if followers[fid].climber=-1 // if climber then stop after a certain time
if timer()-followers[fid].actiontimer#>10
followers[fid].actiontimer#=timer()
followers[fid].climber = -followers[fid].climber
endif
endif
// exploding timer
if GetParticlesExists(followers[fid].explosionparticle) = 1
if timer()-followers[fid].actiontimer#>1
// followers[fid].actiontimer#=timer()
print("BANG")
SetParticlesFrequency(followers[fid].explosionparticle,0)
exploding=1
endif
if timer()-followers[fid].actiontimer#>1.1
followers[fid].actiontimer#=timer()
DeleteParticles(followers[fid].explosionparticle)
SetSpriteVisible(followers[fid].spr,0)
followers[fid].exploder = -followers[fid].exploder
endif
endif
endfunction
function checkmousecollisionwithfollowers(fid)
if GetSpriteCollision(mouse,followers[fid].spr)=1
if GetRawMouseLeftPressed()
dec actionsprites[actioncurrentlyselected].countervalue
SetTextString(actionsprites[actioncurrentlyselected].counter,str(actionsprites[actioncurrentlyselected].countervalue))
select actioncurrentlyselected
case 0
followers[fid].build=-1
endcase
case 1
followers[fid].climber=-1 // not done yet
endcase
case 2
followers[fid].dig=-1
endcase
case 3
followers[fid].exploder=-1
endcase
case 4
// followers[fid].float=-1 // not done yet - but will automatically float when falls in the air, so maybe not required
endcase
case 5
followers[fid].mine=-1
endcase
case 6
followers[fid].stopper=-1
endcase
case 7
// pause() // not done yet
endcase
case 8
// explodeallandresetlevel() // not done yet
endcase
endselect
followers[fid].actiontimer#=timer()
endif
endif
endfunction
function updatefollowers()
for fid = 0 to followers.length
SetSpritePosition(followers[fid].spr, followers[fid].followerx#+worldpositionx#, followers[fid].followery#)
checkmousecollisionwithfollowers(fid)
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid) = 3
followers[fid].hitground=1
followers[fid].action$="Walker"
endif
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid) = 0 and followers[fid].build=1
inc followers[fid].followery#,.1
followers[fid].hitground=0
else
if followers[fid].mine=1 and followers[fid].hitground=1 and followers[fid].build=1 and followers[fid].climber=1
// mining off // we dont want to move the follower up or down if mining
// if followers[fid].direction=1
print("Checking " + str(GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)))
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=1 // left body
followers[fid].direction = -followers[fid].direction
else
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=4 // left foot
dec followers[fid].followery#,.2
endif
endif
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=2 // right body
followers[fid].direction = -followers[fid].direction
else
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=5 // right foot
dec followers[fid].followery#,.2
endif
endif
endif
if followers[fid].action$<>"Floater" then checkactions(fid)
endif
SetTextString(actiontext,followers[0].action$ + " " + str(fid+1))
checktimers(fid)
next
endfunction
function addfollower()
f as _followers
f.spr = CreateSprite(0)
SetSpriteSize(f.spr,16,32)
f.followerx# = screenwidth/2
f.followery# = 70
f.direction=1
f.mine=1 // not mining
f.build=1 // not building
f.dig=1 // not digging
f.stopper = 1 // not a stopper
f.exploder = 1 // not an exploder
f.climber=1
f.hitground=0
f.action$="Floater"
f.actiontimer#=timer()
followers.insert(f)
inc out
SetTextString(outtext,"OUT: " + str(out))
inc followercount
endfunction
function checkactions(fid)
if followers[fid].mine=-1
followers[fid].action$="Miner"
mine(land,landimage,landmesh,fid)
else
if followers[fid].build=-1
followers[fid].action$="Builder"
build(land,landimage,landmesh,fid)
else
if followers[fid].dig=-1
followers[fid].action$="Digger"
dighole(land,landimage,landmesh,followers[fid].spr)
else
if followers[fid].stopper = -1
followers[fid].action$="Stopper"
stopper(fid)
else
if followers[fid].climber = -1
followers[fid].action$="Climber"
climb(fid)
else
// if a sprite is set to explode then explode it - but dont repeat after 1st try
if GetSpriteVisible(followers[fid].spr) = 1 and followers[fid].exploder = -1
followers[fid].action$="Exploder"
exploder(fid)
else
if followers[fid].direction = 1
followers[fid].action$="Walker"
inc followers[fid].followerx#,.2 // move normal speed
endif
if followers[fid].direction = -1
followers[fid].action$="Walker"
dec followers[fid].followerx#,.2 // move normal speed
endif
endif
endif
endif
endif
endif
endif
endfunction
//*************************************************************
// Action functions
//*************************************************************
function climb(fid)
// check if hits something on the body part
if followers[fid].direction = 1
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=2 // right body
// hit a wall, so move the follower UP
dec followers[fid].followery#,.2
else
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=6 // hit ceiling then convert back to a Floater and cancel the climb
inc followers[fid].followery#,.2
followers[fid].action$="Floater"
followers[fid].climber = -followers[fid].climber
else
inc followers[fid].followerx#,.2
endif
endif
else
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=1 // left body
// hit a wall, so move the follower UP
dec followers[fid].followery#,.2
else
if GetSpriteCollisionOnImage(landmesh,followers[fid].spr,fid)=6 // hit ceiling then convert back to a Floater and cancel the climb
inc followers[fid].followery#,.2
followers[fid].action$="Floater"
followers[fid].climber = -followers[fid].climber
else
dec followers[fid].followerx#,.2
endif
endif
endif
endfunction
function exploder(followerid)
x=getspritex(followers[followerid].spr) + GetSpriteWidth(followers[followerid].spr)/2
y=getspritey(followers[followerid].spr) + GetSpriteHeight(followers[followerid].spr)/2
if followers[followerid].exploder = -1 // not already exploding
//sleep(100)
followers[followerid].explosionparticle = CreateParticles(x,y)
life#=1
SetParticlesSize(followers[followerid].explosionparticle,5)
SetParticlesLife(followers[followerid].explosionparticle,life#)
SetParticlesColorInterpolation(followers[followerid].explosionparticle,1)
SetParticlesFrequency(followers[followerid].explosionparticle,1000)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.1,0,0,255,255)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.2,255,255,0,200)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.3,255,0,0,100)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.4,255,255,255,50)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.5,255,255,255,30)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.6,200,200,200,10)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.7,100,100,100,0)
AddParticlesColorKeyFrame(followers[followerid].explosionparticle,.8,0,0,0,0)
SetParticlesVelocityRange(followers[followerid].explosionparticle,1,20)
followers[followerid].actiontimer#=timer()
followers[followerid].exploder = 0
followers[followerid].direction = 99// dont move it
explodehole(land,landimage,landmesh,followers[followerid].spr)
endif
endfunction
function stopper (fid)
for b=0 to followers.length
if b<>fid and GetSpriteCollision(followers[b].spr,followers[fid].spr)=1
followers[b].direction =- followers[b].direction
if followers[b].direction = -1
dec followers[b].followerx#,1
else
inc followers[b].followerx#,1
endif
endif
next
endfunction
function build (land,landimage,imgmesh,fid)
x#=GetSpriteX(followers[fid].spr)-worldpositionx# // platform along the followers foot
y#=getspritey(followers[fid].spr)+GetSpriteHeight(followers[fid].spr) // platform at the foot of the sprite
if followers[fid].direction=-1
dec followers[fid].followery#,.005
dec followers[fid].followerx#,.01
else
dec followers[fid].followery#,.005
inc followers[fid].followerx#,.01
endif
width=GetMemblockInt(imgmesh,0)
for xx=x# to x#+GetSpriteWidth(followers[fid].spr)
for yy=y#+2 to y#+3
SetPixel(imgmesh,xx,yy,255,0,0,255)
next
next
updateimages(land,landimage,imgmesh)
endfunction
function mine(land,landimage,imgmesh,fid)
if followers[fid].direction = 1
inc followers[fid].followerx#,.05 // slow down when mining
else
dec followers[fid].followerx#,.05 // slow down when mining
endif
x#=GetSpriteX(followers[fid].spr)-worldpositionx#
y#=getspritey(followers[fid].spr)
if followers[fid].direction=-1 // going left
for xx=x#-3 to x#
for yy=y# to y#+GetSpriteHeight(followers[fid].spr)-1
SetPixel(imgmesh,xx,yy,0,0,0,0)
next
next
else // going right
for xx=x# to x#+3
for yy=y# to y#+GetSpriteHeight(followers[fid].spr)-1
// check if there is nothing to mine to the right of sprite
SetPixel(imgmesh,xx,yy,0,0,0,0)
next
next
endif
updateimages(land,landimage,imgmesh)
endfunction
function explodehole(land,landimage,imgmesh,spr)
x#=GetSpriteX(spr)-worldpositionx#
y#=getspritey(spr)
for a=0 to 6000
xx=random(x#-20,x#+GetSpriteWidth(spr)+20)
yy=random(y#-20,y#+GetSpriteHeight(spr)+20)
SetPixel(imgmesh,xx,yy,0,0,0,0)
next
updateimages(land,landimage,imgmesh)
endfunction
function dighole(land,landimage,imgmesh,spr)
x#=GetSpriteX(spr)-worldpositionx#
y#=getspritey(spr)+GetSpriteHeight(spr)
for xx=x#-3 to x#+GetSpriteWidth(spr)+3
for yy=y# to y#+5
SetPixel(imgmesh,xx,yy,0,0,0,0)
next
next
updateimages(land,landimage,imgmesh)
endfunction
//*************************************************************
// Image Collision and other image functions
//*************************************************************
// GetSpriteCollisionOnImage - checks if the spr hits any image where
// Returns 0 where Alpha < 128
// Returns 1 where Alpha > 128
// Where = 1 for bottomm of the sprite
// 2 for the bottomleft of the sprite
// 3 for the bottomright of the sprite
function GetSpriteCollisionOnImage(imgmesh,spr,followerid)
minx=GetSpriteX(spr) - worldpositionx#
miny=getspritey(spr)
maxx=GetSpriteX(spr)+GetSpriteWidth(spr) - worldpositionx#
maxy=getspritey(spr)+GetSpriteHeight(spr)
// ret 0 = space
ret=0
// return 0 if surround is totally space
// return 1 if hits wall on the left
// return 2 if hits wall on the right
// return 3 if hits floor
// return 4 if hits anything on the left at folloewers feet - so can walk up
// return 5 if hits anything on the right at folloewers feet - so can walk up
// return 6 if hits anything above - like a ceiling or something
// check the floor first
// ret 3 bottom hit
for x=minx-1 to maxx+1
for y=maxy to maxy+1
offset = 12 + ((y * meshwidth) + x) * 4
a=GetMemblockByte(imgmesh,offset+3)
if a>ALPHA_CHANNEL_CHECK then ret=3
next
next
// then check the left and right side of the follower - this will cancel the RET value from hitting the floor - cause it will
// always hit the floor anyway, but these next two will check if hits a wall on its left or right
// ret 1 - left side hit
if followers[followerid].direction=-1
// ret 4 left foot side
for x=minx-1 to minx
for y=maxy-4 to maxy-1
offset = 12 + ((y * meshwidth) + x) * 4
a=GetMemblockByte(imgmesh,offset+3)
if a>ALPHA_CHANNEL_CHECK then ret=4
next
next
// going left check body
for x=minx-1 to minx
for y=miny to maxy-5
offset = 12 + ((y * meshwidth) + x) * 4
a=GetMemblockByte(imgmesh,offset+3)
if a>ALPHA_CHANNEL_CHECK then ret=1
next
next
// if it still returns a foot hit and no body hit then we can walk up stuff
// if its body area too then its hit a wall
else // going right
// if hits foot first and then body
// ret 5 left foot side
for x=maxx to maxx+1
for y=maxy-4 to maxy-1
offset = 12 + ((y * meshwidth) + x) * 4
a=GetMemblockByte(imgmesh,offset+3)
if a>ALPHA_CHANNEL_CHECK then ret=5
next
next
// ret 2 right body hit
for x=maxx to maxx+1
for y=miny to maxy-5
offset = 12 + ((y * meshwidth) + x) * 4
a=GetMemblockByte(imgmesh,offset+3)
if a>ALPHA_CHANNEL_CHECK then ret=2
next
next
// if it still returns a foot hit and no body hit then we can walk up stuff
// if its body area too then its hit a wall
endif
// ret 6 top hit
for x=minx+1 to maxx-1
for y=miny-1 to miny
offset = 12 + ((y * meshwidth) + x) * 4
a=GetMemblockByte(imgmesh,offset+3)
if a>ALPHA_CHANNEL_CHECK then ret=6
next
next
endfunction ret
function updateimages(land,landimage,imgmesh)
DeleteImage(landimage)
CreateImageFromMemblock(landimage, imgmesh)
SetSpriteImage(land,landimage)
SetSpriteImage(minisprite,landimage)
endfunction
function SetPixel(imgmesh, xx,yy, red, green, blue, alpha)
offset = 12 + ((yy * meshwidth) + xx) * 4
SetMemblockByte(imgmesh, offset, red)
SetMemblockByte(imgmesh, offset+1, green)
SetMemblockByte(imgmesh, offset+2, blue)
SetMemblockByte(imgmesh, offset+3,alpha)
endfunction
//******************************************************
// Level Setup Functions
//******************************************************
function setuplevel(level)
DeleteAllImages()
DeleteAllText()
DeleteAllSprites()
followers.length=-1
actionsprites.length=-1
createmouse()
landimage = LoadImage("level " + str(level) + ".png")
land = CreateSprite(landimage)
landmesh = CreateMemblockFromImage(land)
meshwidth = GetMemblockInt(landmesh,0)
minisprite = CloneSprite(land)
SetSpriteSize(minisprite,screenwidth/2,50)
actiontext=CreateText("Floater 1") : SetTextSize(actiontext,32) : SetTextColor(actiontext,0,100,0,255)
outtext=CreateText("OUT"): SetTextSize(outtext,32): SetTextColor(outtext,0,100,0,255)
intext=CreateText("IN"): SetTextSize(intext,32): SetTextColor(intext,0,100,0,255)
timetext=CreateText("TIME 500"): SetTextSize(timetext,32): SetTextColor(timetext,0,100,0,255)
currenttime=500
setupactionsprites()
addfollower()
endfunction
function CreateMouse()
swap()
DrawLine(3,0,3,6,MakeColor(255,255,255),MakeColor(255,255,255))
DrawLine(0,3,6,3,MakeColor(255,255,255),MakeColor(255,255,255))
render()
mouse = CreateSprite(GetImage(0,0,6,6))
SetSpriteDepth(mouse,0)
SetRawMouseVisible(0)
endfunction
function setupactionsprites()
ActionS as _actionsprites
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_build.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_climb.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_dig.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_explode.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_float.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_mine.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=10 : actions.spr = CreateSprite(LoadImage("\media\action_stopper.png")) :actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=0 : actions.spr = CreateSprite(LoadImage("\media\action_pause.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
actions.countervalue=1 : actions.spr = CreateSprite(LoadImage("\media\action_explodeall.png")) : actions.counter=CreateText(str(actions.countervalue)) : actionsprites.insert(actions)
for a=0 to actionsprites.length
SetTextSize(actionsprites[a].counter,16)
SetSpriteDepth(actionsprites[a].spr,1)
next
endfunction
Just a bit of adjustments to make, but its getting there