Sorry about that. Here is the code:
from pandac.PandaModules import NodePath, DirectionalLight, AmbientLight, VBase4, Vec4, Vec3
from pandac.PandaModules import OdeWorld, OdeBody, OdeMass, Quat, OdePlaneGeom, BitMask32
from pandac.PandaModules import OdeSimpleSpace, OdeJointGroup, OdeBoxGeom
from direct.showbase.ShowBase import ShowBase
from direct.gui.OnscreenText import OnscreenText
from direct.gui.DirectGui import *
from pandac.PandaModules import TextNode
from direct.task import Task
import random, sys, time
FRAMESKIP = 4
# Panda Render Window
class PandaApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# global things to do
base.disableMouse() # turn off internal camera control
base.setBackgroundColor(0.2, 0.2, 0.2) # not that we care really, can't see it
# std random startup stuff
random.seed()
# main physics world lives here, set the gravity to earth std
self.myWorld = OdeWorld()
self.myWorld.setGravity(0, 0, -9.81)
# Create a space and add a contactgroup for our physics system
self.space = OdeSimpleSpace()
self.space.enable()
self.space.setAutoCollideWorld(self.myWorld)
self.contactgroup = OdeJointGroup()
self.space.setAutoCollideJointGroup(self.contactgroup)
# The surface table is needed for autoCollide
self.myWorld.initSurfaceTable(1)
self.myWorld.setSurfaceEntry(0, 0, 20, 3.8, 1.1, 0.9, 0.00001, 0.0, 0.002)
# Load Sounds (notes) for dice models
self.loadSounds()
# load dice models and set them for dynamics
self.LoadModels()
# load the room we bounce in, set the physics floor and walls
self.LoadRoom()
# setup global collision system callback
self.space.setCollisionEvent("ode-collision")
base.accept("ode-collision", self.onCollision)
# add a directional light
dlight = DirectionalLight('dlight')
dlight.setColor(VBase4(0.9, 0.9, 0.9, 1))
dlnp = render.attachNewNode(dlight)
dlnp.setHpr(0, -60, 0)
render.setLight(dlnp)
# and an ambient light too
alight = AmbientLight('alight')
alight.setColor(VBase4(0.15, 0.15, 0.15, 1))
alnp = render.attachNewNode(alight)
render.setLight(alnp)
# camera coords
self.cameraX = 0
self.cameraY = -25
self.cameraZ = 11
self.cameraH = 0
self.cameraP = -16
self.cameraR = 0
# fix the camera here
self.camera.setPos(self.cameraX, self.cameraY, self.cameraZ)
self.camera.setHpr(self.cameraH, self.cameraP, self.cameraR)
# add keyboard controls
self.accept('space', self.throwDice)
self.accept('escape', sys.exit)
self.accept('c', self.addCredits)
self.accept('b', self.addBet)
# setup some flags and things
self.frame = 0
self.computedWin = True
self.addForce = False
self.runSimulation = False
self.inSimulation = False
self.rotationRunning = False
self.collision = False
self.winlist = {}
self.positionlist = {}
self.prevlist = {}
# money and such
self.credits = 10
self.bet = 1
self.winnings = 0
# wins per face
self.ones = 0
self.twos = 0
self.threes = 0
self.fours = 0
self.fives = 0
self.sixes = 0
# setup the screen text displays and cute little dice button
self.drawScreenText()
# start everything and we are done with initialize
taskMgr.doMethodLater(0.1, self.startRot, "StartRotation")
taskMgr.doMethodLater(0.7, self.startGrav, "StartGravity")
def loadSounds(self):
f0 = loader.loadSfx("models/f.wav")
g0 = loader.loadSfx("models/g.wav")
b0 = loader.loadSfx("models/bb.wav")
c0 = loader.loadSfx("models/c.wav")
e0 = loader.loadSfx("models/eb.wav")
f1 = loader.loadSfx("models/f1.wav")
g1 = loader.loadSfx("models/g1.wav")
b0 = loader.loadSfx("models/bb1.wav")
c1 = loader.loadSfx("models/c1.wav")
self.sounds = [f0, g0, c0, f1, g1, c1, f0, g0, c1 ]
self.soundIndex = 0
# Text on the Screen
def drawScreenText(self):
text = ""
self.textObject = OnscreenText(text=text, \
pos=(-1.05,-0.95), \
scale = 0.10, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
text = "Credits:"
self.CreditsText = OnscreenText(text=text, \
pos=(-1.3, 0.9), \
scale = 0.1, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
text = "%d" % self.credits
self.CreditsNum = OnscreenText(text=text, \
pos=(-0.9, 0.9), \
scale = 0.13, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
text = "Bet:"
self.BetText = OnscreenText(text=text, \
pos=(.94, 0.9), \
scale = 0.1, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
text = "%d" % self.bet
self.BetNum = OnscreenText(text=text, \
pos=(1.14, 0.9), \
scale = 0.13, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
text = "Win: 0"
self.textWin = OnscreenText(text=text, \
pos=(.94,-0.95), \
scale = 0.10, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
text = "Idle"
self.textStatus = OnscreenText(text=text, \
pos=(.94,-0.85), \
scale = 0.04, \
fg=(1, 1, 1, 1), \
align=TextNode.ALeft, \
mayChange=1)
# Add button
buttonText = ("", "", "Throw", "")
self.bDie = DirectButton(text = buttonText, \
geom = self.displayDie, \
geom_pos = (0,0,0), \
geom_scale = (0.1, 0.1, 0.1), \
geom_hpr = (0, 0, 0), \
relief = None, \
text_fg = (1, 1, 1, 1), \
text_scale = (0.3, 0.3), \
text_shadow = (0, 0, 0, 1), \
text_style = ScreenTitle, \
frameColor = (1, 1, 1, 1), \
borderWidth = (0.05, 0.05), \
#image = 'symbols\symbol_1.png', \
pos = (-1.2, 0, -.90), \
scale = 0.3, \
hpr = (0,0,0), \
command=self.throwDice)
#start up the physics task
def startGrav(self, task):
self.startGravitySim()
# spin the dice weirdly rotate task
def startRot(self, task):
self.startRotation()
# add some credits to our pot
def addCredits(self):
self.credits = self.credits + 1
s = "%d" % self.credits
self.CreditsNum.setText(s)
# bet some credits on this spin
def addBet(self):
self.bet = self.bet + 1
if self.bet > 5:
self.bet = 1
s = "%d" % self.bet
self.BetNum.setText(s)
# spin the models randomly
def rotateModels(self, task):
self.frame = self.frame + 1
if self.frame >= FRAMESKIP:
self.frame = 0
for die in self.dice:
x = random.randint(0,359)
y = random.randint(0,359)
z = random.randint(0,359)
die[0].setHpr(x,y,z)
die[3].setPosition(die[0].getPos(render))
die[3].setQuaternion(die[0].getQuat(render))
if self.collision == False:
return Task.cont
else:
return Task.done
# start the dice rotating
def startRotation(self):
if self.rotationRunning == False:
self.rotationRunning = True
self.inSimulation = False
self.taskMgr.add(self.rotateModels, "rotateModels")
# force (explode) the dice
def forceDie(self):
for i in range(0,9):
body = self.dice[i][3]
a = random.randint(-17000000,17000000)
b = random.randint(-17000000,17000000)
c = random.randint(10000000,33000000) # up is always big to overcome gravity
body.setForce(Vec3(a, b, c))
# do a throw
def throwDice(self):
if self.credits - self.bet < 0:
return
if self.computedWin == False:
return
self.computedWin = False
self.textStatus.setText("Throw Dice")
#print "THROW DICE"
self.credits = self.credits - self.bet
s = "%d" % self.credits
self.CreditsNum.setText(s)
self.winnings = 0
w = "Win: %d" % self.winnings
self.textWin.setText(w)
for i in range(0,9):
self.prevlist[i] = (0.0, 0.0, 0.0)
self.moving = 0
self.forceDie()
taskMgr.doMethodLater(0.2, self.evaluateWinList, "EvaluateWinList")
def checkMotion(self):
moving = False
for d in self.winlist.keys():
(l,m,n) = self.winlist[d]
a = "%03.0f" % l
b = "%03.0f" % m
c = "%03.0f" % n
(o,p,q) = self.prevlist[d]
e = "%03.0f" % o
f = "%03.0f" % p
g = "%03.0f" % q
if a != e:
moving = True
if b != f:
moving = True
if c != g:
moving = True
for d in self.winlist.keys():
self.prevlist[d] = self.winlist[d]
return moving
# evaluate (count up faces) dice as they come to rest on the floor, a task that runs every frame
def evaluateWinList(self, task):
if self.checkMotion() == False:
self.moving = self.moving + 1
else:
self.moving = 0
ones = 0
twos = 0
threes = 0
fours = 0
fives = 0
sixes = 0
a = b = c = 0
for d in self.winlist.keys():
l, m, n = self.winlist[d]
a = int(l+0.5)
b = int(m+0.5)
c = int(n+0.5)
if b == 0 and c == -89:
ones = ones + 1
elif b == 0 and c == 0:
twos = twos + 1
elif b == 90:
threes = threes + 1
elif b < -88:
fours = fours + 1
elif b == 0 and c != 90:
fives = fives + 1
elif b == 0 and c == 90:
sixes = sixes + 1
if self.ones == ones and self.twos == twos and self.threes == threes \
and self.fours == fours and self.fives == fives and self.sixes == sixes:
t = ""
if ones > 3:
t = t + "%d ones " % ones
if twos > 3:
t = t + "%d twos " % twos
if threes > 3:
t = t + "%d threes " % threes
if fours > 3:
t = t + "%d fours " % fours
if fives > 3:
t = t + "%d fives " % fives
if sixes > 3:
t = t + "%d sixes " % sixes
if ones > 0 and twos > 0 and threes > 0 and fours > 0 and fives > 0 and sixes > 0:
t = t + "six sequence"
self.textObject.setText(t)
else:
self.ones = ones
self.twos = twos
self.threes = threes
self.fours = fours
self.fives = fives
self.sixes = sixes
self.bDie["geom_hpr"] = (a,b,c)
if self.moving > 5:
taskMgr.doMethodLater(0.1, self.computeTotals, "compute total wins")
return Task.done
return Task.cont
def computeTotals(self, task):
self.textStatus.setText("Compute win")
self.winnings = 0
# five of a kind or greater pays bet * number of a kind
if self.ones > 3:
self.winnings = self.winnings + (self.ones * self.bet)
if self.twos > 3:
self.winnings = self.winnings + self.twos * self.bet
if self.threes > 3:
self.winnings = self.winnings + (self.threes * self.bet)
if self.fours > 3:
self.winnings = self.winnings + self.fours * self.bet
if self.fives > 3:
self.winnings = self.winnings + self.fives * self.bet
if self.sixes > 3:
self.winnings = self.winnings + self.sixes * self.bet
if self.ones > 0 and self.twos > 0 and self.threes > 0 and self.fours > 0 and self.fives > 0 and self.sixes > 0:
self.winnings = self.winnings + (2 * self.bet)
w = "Win: %d" % self.winnings
self.textWin.setText(w)
self.credits = self.credits + self.winnings
s = "%d" % self.credits
self.CreditsNum.setText(s)
self.computedWin = True
return Task.done
# start the gravity/collision simulation
def startGravitySim(self):
self.addForce = False
if self.runSimulation == False:
self.deltaTimeAccumulator = 0.0
self.stepSize = 1.0 / 90.0
self.runSimulation = True
taskMgr.doMethodLater(0.1, self.simulationTask, "Physics Simulation")
# callback for collisions of the dice
def onCollision(self, entry):
geom1 = entry.getGeom1()
geom2 = entry.getGeom2()
body1 = entry.getBody1()
body2 = entry.getBody2()
for np, geom, sound, body in self.dice:
if geom == geom1 or geom == geom2:
velocity = body1.getAngularVel()
if velocity[0] > 2.0 and sound.status != sound.PLAYING:
sound.setVolume(velocity[0] / 80.0)
sound.play()
self.collision = True
# run the gravity/collision simulation task
def simulationTask(self, task):
self.inSimulation = True
self.space.autoCollide()
#self.myWorld.quickStep(globalClock.getDt())
self.myWorld.quickStep(0.016)
i = 0
for np, geom, sound, body in self.dice:
self.positionlist[i] = np.getPos()
self.winlist[i] = np.getHpr()
i = i + 1
if not np.isEmpty():
np.setPosQuat(render, geom.getBody().getPosition(), Quat(geom.getBody().getQuaternion()))
self.contactgroup.empty() # Clear the contact joints
if self.runSimulation == True:
return task.cont
# set already existing models to standard places
def setModels(self):
i = 0
z = 2.0
for x in [-1.0, 0.0, 1.0]:
die = self.dice[i][0]
die.setPos(x, 0.4, z)
die.setHpr(0,0,0)
body = self.dice[i][3]
body.setPosition(die.getPos(render))
body.setQuaternion(die.getQuat(render))
i = i + 1
z = 3.0
for x in [-1.0, 0.0, 1.0]:
die = self.dice[i][0]
die.setPos(x, 0.0, z)
die.setHpr(0,0,0)
body = self.dice[i][3]
body.setPosition(die.getPos(render))
body.setQuaternion(die.getQuat(render))
i = i + 1
z = 4.0
for x in [-1.0, 0.0, 1.0]:
die = self.dice[i][0]
die.setPos(x, 0.4, z)
die.setHpr(0,0,0)
body = self.dice[i][3]
body.setPosition(die.getPos(render))
body.setQuaternion(die.getQuat(render))
i = i + 1
# build individual die, set mass, collision and physics
def buildModel(self, x, y, modelDice):
die = modelDice.copyTo(self.root)
die.setPos(x, 0.0, y)
die.setScale(0.25, 0.25, 0.25)
diceBody = OdeBody(self.myWorld)
mass = OdeMass()
wt = random.randint(10000, 14240)
mass.setBox(wt, 1, 1, 1)
diceBody.setMass(mass)
diceBody.setPosition(die.getPos(render))
diceBody.setQuaternion(die.getQuat(render))
diceBody.enable()
diceGeom = OdeBoxGeom(self.space, 1, 1, 1)
diceGeom.setCollideBits(BitMask32(0x00000001))
diceGeom.setCategoryBits(BitMask32(0x00000001))
diceGeom.setBody(diceBody)
Sound = self.sounds[self.soundIndex]
self.soundIndex = self.soundIndex + 1
return die, diceGeom, Sound, diceBody
# import the dice model base and build all nine
def LoadModels(self):
self.displayDie = loader.loadModel("models/die") # for button
self.displayDie.setDepthTest(True)
self.displayDie.setDepthWrite(True)
modelDice = loader.loadModel("models/die")
self.root = render.attachNewNode("Root")
self.dice = []
y = 2.0
for x in [-1.0, -0.4, 1.0]:
die, diceGeom, Sound, body = self.buildModel(x, y, modelDice)
self.dice.append((die, diceGeom, Sound, body))
y = 3.0
for x in [-1.0, 0.0, 1.0]:
die, diceGeom, Sound, body = self.buildModel(x, y, modelDice)
self.dice.append((die, diceGeom, Sound, body))
y = 4.0
for x in [-1.0, 0.4, 1.0]:
die, diceGeom, Sound, body = self.buildModel(x, y, modelDice)
self.dice.append((die, diceGeom, Sound, body))
def LoadRoom(self):
# put dice in a box, model only
room = loader.loadModel("models/room")
room.reparentTo(render)
room.setScale(1, 2, 1)
room.setPos(0, -1, 0)
room.setTwoSided(True)
# install physics - floor
groundGeom = OdeBoxGeom(self.space, (2000, 2000, 1))
groundGeom.setCollideBits(BitMask32( 0xffffffff))
groundGeom.setCategoryBits(BitMask32(0xffffffff))
# wall left
wall0Geom = OdeBoxGeom(self.space, (1, 2000, 2000))
wall0Geom.setPosition(-6.5,0,0)
wall0Geom.setCollideBits(BitMask32(0xffffffff))
wall0Geom.setCategoryBits(BitMask32(0xffffffff))
# wall right
wall1Geom = OdeBoxGeom(self.space, (1, 2000, 2000))
wall1Geom.setPosition(6.5,0,0)
wall1Geom.setCollideBits(BitMask32(0xffffffff))
wall1Geom.setCategoryBits(BitMask32(0xffffffff))
# wall back
wall2Geom = OdeBoxGeom(self.space, (2000, 1, 2000))
wall2Geom.setPosition(0,6.5,0)
wall2Geom.setCollideBits(BitMask32(0xffffffff))
wall2Geom.setCategoryBits(BitMask32(0xffffffff))
# wall behind cam
wall3Geom = OdeBoxGeom(self.space, (2000, 1, 2000))
wall3Geom.setPosition(0,-6.8,0)
wall3Geom.setCollideBits(BitMask32(0xffffffff))
wall3Geom.setCategoryBits(BitMask32(0xffffffff))
# ceiling
wall4Geom = OdeBoxGeom(self.space, (2000, 2000, 1))
wall4Geom.setPosition(0,0,11)
wall4Geom.setCollideBits(BitMask32(0xffffffff))
wall4Geom.setCategoryBits(BitMask32(0xffffffff))
# MAIN - start everything
panda = PandaApp()
run()