Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Touch pool
-- TouchPool v1
----- Game for two players---
--You have to touch as many of your balls (solid or stripe) as possible, one point per touch
--You lose a point each time the white touches an opponent's ball
--You lose your turn when you don't score at least 2 in your shot
--Touching the black ball adds 8 (for either player)
--First to 50 wins
radius=25 --size of balls
forceFactor=20
maxForce=20000
function setup()
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
physics.gravity(0,0) --turn off gravity for a tabletop surface
MENU,PLAYING,FINISHED=1,2,3
DrawStates={DrawMenu,DrawPlaying,DrawEnded}
--use a generic game variable, which allows me to create several games
game=GT
ChangeState(MENU)
end
function ChangeState(s)
state=s
if state==PLAYING then
SetupBalls(radius)
end
end
function draw()
background(49, 143, 55, 255)
DrawStates[state]()
end
function DrawMenu()
pushStyle()
font("ArialRoundedMTBold")
fontSize(48)
fill(255)
textMode(CENTER)
text("Touch Pool",WIDTH/2,HEIGHT*2/3)
textMode(CORNER)
fontSize(16)
local txt=[[
A game for 2 players
Make the white ball touch as many of your balls (solid or stripe) as possible.
Each touch counts as +1 point, and touches of the opponent's balls count as -1 point.
You have to score at least +2 to have it count, otherwise you lose your turn.
The black ball counts +5 if either player hits it (as long as you have scored +2 from your own balls).
First to 100 points wins.
SHOOTING
Touch and drag your finger anywhere on the screen to set your aim and power.
You will see an aiming line, and the strength is shown by a marker inside the white ball.
]]
text(txt,WIDTH/6,HEIGHT/6)
popStyle()
end
function DrawPlaying()
DrawBalls()
DrawAim()
DrawMessages()
end
function DrawEnded()
pushStyle()
font("ArialRoundedMTBold")
fontSize(48)
fill(255)
textMode(CENTER)
text("Touch Pool",WIDTH/2,HEIGHT*2/3)
fontSize(24)
text("End of Game",WIDTH/2,HEIGHT*2/3-50)
text(message,WIDTH/2,HEIGHT*.5)
textMode(CENTER)
fill(255,255,0)
text("Touch to play again",WIDTH/2,HEIGHT*.5-100)
popStyle()
end
function DrawBalls()
for i,b in pairs(balls) do
pushMatrix()
translate(b.p.x,b.p.y)
rotate(b.p.angle)
sprite(b.img,0,0)
--draw the reflection (we need to rotate back first)
rotate(-b.p.angle)
sprite(reflect,0,0)
popMatrix()
adjust(b.p) --adjust ball friction
end
end
--adjusts "damping" (friction) of ball b
--if we don't do this, they take forever to stop moving
--so this code makes friction increase as speed slows
function adjust(p)
if p.linearVelocity:len()<50 then
if p.linearVelocity:len()<10 then p.linearDamping=0.98 else p.linearDamping=0.5 end
else p.linearDamping=0.1 end
p.angularDamping=p.linearDamping
end
--draws the aim and strength of the current touch
function DrawAim()
if touchStart then
--direction & strength of current touch = current touch position - start position
local v=(vec2(touchPos.x,touchPos.y)-touchStart)
local d=v:normalize() --direction
local L=v:len() --strength
pushStyle()
strokeWidth(1)
stroke(255, 255, 0, 255)
local b=balls[16].p
--draw a line from the centre of the white ball along the direction we are aiming in
local b1=vec2(b.x+d.x*10000,b.y+d.y*10000) --we'll use this to draw a long line
line(b.x,b.y,b1.x,b1.y)
--now draw a fainter line, the width of the ball
strokeWidth(radius*2)
stroke(255, 255, 0, 30)
line(b.x,b.y,b1.x,b1.y)
--now draw a yellow circle inside the yellow ball, to show its strength
stroke(255,255,0,255)
strokeWidth(10)
--ellipse(b.x,b.y,2*radius*math.min(1,0.1+0.9*v:len()/500)) message=2*radius*math.min(1,v:len()/500)
local f=L*forceFactor/maxForce
local a=radius*math.min(1,0.1+0.9*f)
line(b.x,b.y,b.x+d.x*a,b.y+d.y*a)
popStyle()
end
end
function DrawMessages()
if EndOfTurn() then
score,message=game.update()
played=false
end
fill(255)
fontSize(24)
textMode(CORNER)
text("Score: "..score[1].." vs "..score[2],25,HEIGHT-50)
if message then text(message,25,HEIGHT-75) end
end
function EndOfTurn()
if not played then return false end
for i=1,16 do
if balls[i].p.linearVelocity:len()>1 then return false end
end
return true
end
function touched(t)
if state==MENU and t.state==ENDED then
ChangeState(PLAYING)
--each game may have its own scoring and messaging, so ask the game for initial details
score,message=game.setup()
elseif state==PLAYING then HandleAimTouch(t)
elseif state==FINISHED and t.state==ENDED then ChangeState(MENU) end
end
function HandleAimTouch(t)
if t.state==BEGAN then
touchStart=vec2(t.x,t.y)
touchPos=touchStart
elseif t.state==MOVING then touchPos=vec2(t.x,t.y)
elseif t.state==ENDED then
Shoot(touchStart,touchPos)
touchStart=nil
end
end
function Shoot(v1,v2)
balls[16].p:applyForce((v2-v1)*forceFactor)
played=true
message=""
end
function collide(c)
if c.state~="BEGAN" and lastCollisionID~=c.id then
if c.bodyA.info==16 and c.bodyB.info then
game.touch(c.bodyB.info)
lastCollisionID=c.id
elseif c.bodyB.info==16 and c.bodyA.info then
game.touch(c.bodyA.info)
lastCollisionID=c.id
end
end
end
function SetupBalls(r)
local e=color(200) --offwhite color for balls
local c={color(255,255,0),color(0,0,255),color(255,0,0),color(61, 50, 152, 255),
color(255, 126, 0, 255),color(0,255,0),color(152, 73, 52, 255),color(0)}
local p=vec2(WIDTH*.6,HEIGHT/2)
local g=r*2
local pos={p,vec2(p.x+g,p.y-g*.5),vec2(p.x+g,p.y+g*.5),
vec2(p.x+g*2,p.y-g*.5),vec2(p.x+g*2,p.y),vec2(p.x+g*2,p.y+g*.5),
vec2(p.x+g*3,p.y-g*1.5),vec2(p.x+g*3,p.y-g*.5),vec2(p.x+g*3,p.y+g*.5),vec2(p.x+g*3,p.y+g*1.5),
vec2(p.x+g*4,p.y-g*1.5),vec2(p.x+g*4,p.y-g*.5),vec2(p.x+g*4,p.y),
vec2(p.x+g*4,p.y+g*.5),vec2(p.x+g*4,p.y+g*1.5)}
reflect=image(r*2,r*2)
setContext(reflect)
local n=30
for i=1,n do --create concentric circles, largest to smallest
local a=(n+1-i)/n
fill(255,255,255,150*a*a)
ellipse(r*.55,r*1.35,i) --off centre circles
end
setContext()
if balls==nil then
balls={}
for i=1,16 do
balls[i]={}
balls[i].img=image(r*2,r*2)
setContext(balls[i].img)
if i<9 then
fill(c[i])
ellipse(r,r,r*2)
elseif i<16 then
fill(e)
ellipse(r,r,r*2)
stroke(c[i-8])
strokeWidth(r*1.2)
line(r*0.6,r,r*1.4,r)
strokeWidth(0)
else
fill(e)
ellipse(r,r,r*2)
end
fill(e)
ellipse(r,r,r*1)
fill(0) stroke(0)
if i<16 then
fontSize(18)
text(i,r,r)
if i==6 or i==9 then strokeWidth(2) line(r*.8,r*.6,r*1.2,r*.6) strokeWidth(0) end
end
setContext()
end
end
for i=1,16 do
if balls[i].p ==nil then balls[i].p=physics.body(CIRCLE,r) end
balls[i].p.bullet=true
balls[i].p.restitution=0.6
balls[i].p.linearDamping=0.1
balls[i].p.friction=0
balls[i].p.angularDamping=balls[i].p.linearDamping
balls[i].p.info=i
if i<16 then
u=math.random(1,#pos)
balls[i].p.x,balls[i].p.y=pos[u].x,pos[u].y
table.remove(pos,u)
else balls[i].p.x,balls[i].p.y=WIDTH*0.3,HEIGHT/2 end
end
if w==nil then
w={physics.body(EDGE,vec2(0,0),vec2(WIDTH,0)),
physics.body(EDGE,vec2(0,0),vec2(0,HEIGHT)),
physics.body(EDGE,vec2(WIDTH,0),vec2(WIDTH,HEIGHT)),
physics.body(EDGE,vec2(0,HEIGHT),vec2(WIDTH,HEIGHT))}
end
for i=1,4 do
w[i].restitution=0.8
w[i].friction=0
end
collectgarbage()
end
GT={}
GT.WinScore=50
function GT.setup()
GT.score={0,0}
GT.touches=0
GT.bonus=0
GT.currentPlayer=1 --solid player starts
return GT.score,"Solid player starts"
end
function GT.update()
local m
if GT.currentPlayer==1 then
if GT.touches>1 then
GT.score[1]=GT.score[1]+GT.touches+GT.bonus
m="Solids scored "..GT.touches+GT.bonus
if GT.score[1]>=GT.WinScore then
m="Solids Won by "..GT.score[1].." to "..GT.score[2]
ChangeState(FINISHED)
end
else
GT.currentPlayer=3-GT.currentPlayer
m="End of turn - Stripes to play"
end
else
if GT.touches<-1 then
GT.score[2]=GT.score[2]-GT.touches+GT.bonus
m="Stripes scored "..-GT.touches+GT.bonus
if GT.score[2]>=GT.WinScore then
m="Stripes Won by "..GT.score[2].." to "..GT.score[1]
ChangeState(FINISHED)
end
else
GT.currentPlayer=3-GT.currentPlayer
m="End of turn - Solids to play"
end
end
GT.touches=0 --reset number of touches
GT.bonus=0
GT.played=false
return GT.score,m
end
function GT.touch(n)
GT.played=true
if n<8 then GT.touches=GT.touches+1
elseif n==8 then GT.bonus=GT.bonus+5
else GT.touches=GT.touches-1 end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.