Jeremy Penner
19fe12a12a
add support for returning to the spot where you left off in the lobby move meta information out of drawing name and off to the side
889 lines
33 KiB
Python
889 lines
33 KiB
Python
from engine import *
|
|
from basetoken import *
|
|
from tpers import *
|
|
from datetime import *
|
|
from util import *
|
|
import telnet
|
|
import time
|
|
import login
|
|
import weakref
|
|
|
|
class Layer(TPrs):
|
|
def InitPersistent(self, ascr, stName):
|
|
self.ascr = ascr
|
|
self.stName = stName
|
|
|
|
def StName(self):
|
|
return self.stName
|
|
|
|
def SetName(self, st):
|
|
self.stName = st
|
|
|
|
def FSelectable(self):
|
|
return True
|
|
|
|
class Project(Ownable):
|
|
def InitPersistent(self, owner, stName, user):
|
|
Ownable.InitPersistent(self, owner)
|
|
self.stName = stName
|
|
self.user = user
|
|
self.rgdrawing = []
|
|
self.rgscript = []
|
|
|
|
def NewBoard(self, stName, user):
|
|
board = Board(self, stName, user)
|
|
self.rgdrawing.insert(0, board)
|
|
return board
|
|
|
|
def NewSprite(self, stName, user, w, h):
|
|
sprite = Sprite(self, stName, user, w, h)
|
|
self.rgdrawing.insert(0, sprite)
|
|
return sprite
|
|
|
|
def DrawingTouched(self, drawing):
|
|
if drawing != self.rgdrawing[0]:
|
|
self.rgdrawing.remove(drawing)
|
|
self.rgdrawing.insert(0, drawing)
|
|
self.owner.DrawingTouched(drawing)
|
|
def StName(self):
|
|
return self.owner.StNameProject(self)
|
|
def StLastModified(self):
|
|
drawingLast = None
|
|
for drawing in self.rgdrawing:
|
|
if drawingLast == None or drawing.dtLastModified > drawingLast.dtLastModified:
|
|
drawingLast = drawing
|
|
if drawingLast != None:
|
|
return drawingLast.StLastModified()
|
|
return "never"
|
|
def RgstMetadata(self):
|
|
return ["by " + self.user, "", "Last modified:", self.StLastModified()]
|
|
|
|
class Drawing(Ownable):
|
|
def InitPersistent(self, owner, stName, user, rglayer=None, dtLastModified=None):
|
|
Ownable.InitPersistent(self, owner)
|
|
if rglayer == None:
|
|
rglayer = [Layer(ansi.Ascr(self.W(), self.H()), "Layer 1")]
|
|
self.rglayer = rglayer
|
|
self.stName = stName
|
|
self.user = user
|
|
self.chat = Chat()
|
|
if dtLastModified == None:
|
|
self.SetTimestamp()
|
|
else:
|
|
self.dtLastModified = dtLastModified
|
|
|
|
def StName(self):
|
|
return self.owner.owner.StNameDrawing(self)
|
|
|
|
def RgstMetadata(self):
|
|
return [self.StType(), "by " + self.user, "", "Last modified", self.StLastModified()]
|
|
|
|
def StLastModified(self):
|
|
return self.dtLastModified.strftime("%b %d, %Y at %I:%M%p")
|
|
|
|
def LayerByName(self, stName):
|
|
for layer in self.rglayer:
|
|
if layer.StName().lower() == stName.lower():
|
|
return layer
|
|
return None
|
|
|
|
def NewLayer(self, stName):
|
|
if len(self.rglayer) < 8:
|
|
layerNew = Layer(ansi.Ascr(self.W(), self.H()), stName)
|
|
self.rglayer.append(layerNew)
|
|
return layerNew
|
|
|
|
def MoveLayer(self, layer, fUp):
|
|
ilayer = self.rglayer.index(layer)
|
|
if fUp and ilayer > 0:
|
|
ilayer = ilayer - 1
|
|
elif not fUp and ilayer < len(self.rglayer) - 1:
|
|
ilayer = ilayer + 1
|
|
self.rglayer.remove(layer)
|
|
self.rglayer.insert(ilayer, layer)
|
|
|
|
def Touch(self):
|
|
self.SetTimestamp()
|
|
self.owner.DrawingTouched(self)
|
|
|
|
def SetTimestamp(self):
|
|
self.dtLastModified = datetime.now()
|
|
|
|
@Version(3)
|
|
class Board(Drawing):
|
|
def StType(self):
|
|
return "Board"
|
|
def W(self): return config.W
|
|
def H(self): return config.H
|
|
|
|
def draw(self, ascr):
|
|
for layer in reversed(self.rglayer):
|
|
ascr.PutAscr(layer.ascr)
|
|
|
|
def Save(self):
|
|
fn = config.DIR_ANSI + "/" + "_".join([str(x) for x in time.localtime()])
|
|
ascr = ansi.Ascr(self.W(), self.H())
|
|
self.draw(ascr)
|
|
with open(fn + ".html", "w") as fl:
|
|
fl.write("<html><body bgcolor='#000000'><center>")
|
|
fl.write(ascr.Hst())
|
|
fl.write("</center></body></html>")
|
|
with open(fn + ".ans", "w") as fl:
|
|
fl.write(ascr.Ast())
|
|
|
|
def UpgradeFrom(self, versionOld):
|
|
if versionOld < 2:
|
|
gameWB = [gameWB for gameWB in self.owner.rggameWB if gameWB.rgtoken("whiteboard")[0].board == self][0]
|
|
self.stName = gameWB.stName
|
|
self.user = gameWB.user
|
|
if versionOld < 3:
|
|
self.chat = Chat()
|
|
|
|
class Sprite(Drawing):
|
|
def InitPersistent(self, owner, stName, user, w, h):
|
|
self.w = w
|
|
self.h = h
|
|
Drawing.InitPersistent(self, owner, stName, user, [Layer(ansi.Ascr(w, h), "Frame 1")])
|
|
|
|
def StType(self): return "Sprite"
|
|
def W(self): return self.w
|
|
def H(self): return self.h
|
|
|
|
def draw(self, ascr, x, y, iframe):
|
|
ascr.PutAscr(self.rglayer[iframe].ascr, x, y)
|
|
|
|
def RgstMetadata(self):
|
|
return [self.StType(), "by " + self.user, "", str(self.w) + "x" + str(self.h), str(len(self.rglayer)) + " frames", "", "Last modified", self.StLastModified()]
|
|
|
|
@Version(5)
|
|
class Whiteboard(Token):
|
|
def InitPersistent(self, game, board):
|
|
Token.InitPersistent(self, game, "whiteboard")
|
|
self.board = board
|
|
|
|
def UpgradeFrom(self, versionOld):
|
|
if versionOld < 3:
|
|
dt = None
|
|
else:
|
|
dt = self.dtLastModified
|
|
del self.dtLastModified
|
|
if versionOld < 4:
|
|
rglayer = [Layer(self.ascr, "Layer 1")]
|
|
del self.ascr
|
|
self.untag("drawable")
|
|
self.untag("solid")
|
|
else:
|
|
rglayer = self.rglayer
|
|
del self.rglayer
|
|
if versionOld < 5:
|
|
self.board = Board(self.game.gameLobby, self.game.stName, self.game.user, rglayer, dt)
|
|
|
|
class Chl(object):
|
|
def __init__(self, client, st):
|
|
self.user = client.cld.user
|
|
self.st = st.rstrip()
|
|
|
|
class ChlMsg(Chl):
|
|
def StUser(self):
|
|
return "<" + self.user + ">"
|
|
def Col(self):
|
|
return ansi.WHITE
|
|
|
|
class ChlAct(Chl):
|
|
def StUser(self):
|
|
return "* " + self.user
|
|
def Col(self):
|
|
return ansi.MAGENTA
|
|
|
|
class ChlSys(Chl):
|
|
def StUser(self):
|
|
return self.user
|
|
def Col(self):
|
|
return ansi.BLUE
|
|
|
|
class Chat(TPrs):
|
|
def InitTransient(self):
|
|
TPrs.InitTransient(self)
|
|
self.cchl = 100
|
|
self.DetachFromGame()
|
|
|
|
def InitPersistent(self):
|
|
TPrs.InitPersistent(self)
|
|
self.rgchl = []
|
|
|
|
def AttachToGame(self, game):
|
|
self.game = game
|
|
self.evChat = Event(game)
|
|
|
|
def DetachFromGame(self):
|
|
self.game = None
|
|
self.evChat = None
|
|
|
|
def Add(self, chl):
|
|
if len(chl.st) > 0:
|
|
self.rgchl.insert(0, chl)
|
|
if len(self.rgchl) > self.cchl:
|
|
self.rgchl = self.rgchl[:self.cchl]
|
|
self.evChat.fire(chl)
|
|
|
|
def CChl(self):
|
|
return len(self.rgchl)
|
|
def CUsers(self):
|
|
return len(self.game.rgclient)
|
|
def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0):
|
|
yDraw = y + h - 1
|
|
for chl in self.rgchl[ichl:]:
|
|
stUser = chl.StUser()
|
|
colFg = chl.Col()
|
|
wMsg = w - len(stUser) - 1
|
|
rgstMsg = RgstWrapped(chl.st, wMsg)
|
|
|
|
rgstMsg.reverse()
|
|
for istMsg, stMsg in enumerate(rgstMsg):
|
|
if istMsg == len(rgstMsg) - 1:
|
|
st = "{0} {1:{2}}".format(stUser, stMsg, wMsg)
|
|
else:
|
|
st = "{0:{1}} {2:{3}}".format("", len(stUser), stMsg, wMsg)
|
|
ascr.PutSt(st, x, yDraw, colFg, colBg)
|
|
yDraw = yDraw - 1
|
|
if yDraw == y - 1:
|
|
return
|
|
while yDraw >= y:
|
|
ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg)
|
|
yDraw = yDraw - 1
|
|
|
|
def DrawUsers(self, ascr, x, y, w, h, colBg=ansi.BLACK, iuser=0):
|
|
yDraw = y
|
|
for client in sorted(self.game.rgclient, lambda c1, c2: cmp(c1.Cldg().iclient, c2.Cldg().iclient))[iuser:]:
|
|
ascr.PutSt("{0:{1}}".format(str(client.Cldg().iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg)
|
|
yDraw = yDraw + 1
|
|
if yDraw == y + h:
|
|
break
|
|
while yDraw < y + h:
|
|
ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg)
|
|
yDraw = yDraw + 1
|
|
|
|
class Cursor(TokenClient):
|
|
def InitPersistent(self, game, client):
|
|
TokenClient.InitPersistent(self, game, client, "drawable", "cursor")
|
|
self.fBlink = False
|
|
self.cDontBlink = 0
|
|
self.pos = ansi.Pos(self.game.drawing.W(), self.game.drawing.H())
|
|
self.SetupMenus()
|
|
self.SelectLayer(self.game.drawing.rglayer[0])
|
|
self.CreateDrawingToken()
|
|
|
|
def CreateDrawingToken(self):
|
|
WhiteboardDraw(self, self.client)
|
|
|
|
def SetupMenus(self):
|
|
miFg = MiColour("Foreground", ansi.rgcolorFg, ansi.WHITE)
|
|
miBg = MiColour("Background", ansi.rgcolorBg, ansi.BLACK)
|
|
miChars = MiChars([[49, 50, 51, 52, 53, 54, 55, 56, 57, 48], #digits
|
|
[176, 177, 178, 219, 223, 220, 221, 222, 254, 249], #blocks
|
|
[218, 191, 192, 217, 196, 179, 195, 180, 193, 194], #single lines
|
|
[201, 187, 200, 188, 205, 186, 204, 185, 202, 203], #double lines
|
|
[213, 184, 212, 190, 205, 179, 198, 181, 207, 209], #sing vert, doub horiz
|
|
[214, 183, 211, 189, 196, 186, 199, 182, 208, 210], #doub vert, sing horiz
|
|
[197, 206, 216, 215, 173, 168, 169, 170, 174, 175], #crosses
|
|
[ 1, 2, 3, 4, 5, 6, 14, 15, 19, 20], #smiles
|
|
[224, 225, 226, 227, 228, 229, 230, 231, 232, 233], #greek
|
|
[234, 235, 236, 237, 238, 239, 240, 241, 242, 243], #crap
|
|
[244, 245, 246, 247, 248, 249, 251, 252, 253, 167], #more crap
|
|
[155, 156, 157, 158, 159, 171, 172, 166, 32, 32], #$
|
|
[142, 132, 131, 143, 134, 133, 160, 145, 146, 32], #a
|
|
[128, 135, 137, 136, 138, 144, 130, 164, 165, 32], #c, e, n
|
|
[139, 140, 141, 161, 153, 148, 147, 149, 162, 32], #i, o
|
|
[154, 129, 150, 151, 163, 32, 32, 32, 32, 32], #u
|
|
], miFg, miBg)
|
|
|
|
miQuit = MiButton("Back to Menu", self.client.leaveGame)
|
|
self.cf = self.MenuDrawing(miFg, miBg, miChars, miQuit)
|
|
self.miChat = MiMenu([None, MiChat(self.client, 13, self.game.drawing.chat), MiTypein(self.SendMsg), None, miQuit, None])
|
|
self.rgmiLayer = RgmiProjected(self.game.drawing.rglayer, DgProjectMiButton(self.SelectLayer))
|
|
self.miLayers = self.MenuLayers(miQuit)
|
|
self.ovPopup = OvPopup(self, self.client, MiTab.MiTabbed(["Drawing", self.cf], [self.StLayerTab(), self.miLayers], ["Chat", self.miChat]))
|
|
miStatus = MiMenuHoriz([MiStatic("Hit <Tab> for menu"), MiValue(miChars)])
|
|
miStatus.ListenForNotice(self.miChat)
|
|
self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM)
|
|
|
|
def MenuDrawing(self, miFg, miBg, miChars, miQuit):
|
|
return MiMenu([None, miFg, miBg, None, miChars, None, MiButton("Save", self.Save), None, miQuit, None])
|
|
|
|
def MenuLayers(self, miQuit):
|
|
return MiMenu(Rgseq([None], self.rgmiLayer, [None, MiButton("New Layer", self.NewLayer), MiButton("Move up", self.LayerUp), MiButton("Move down", self.LayerDown),
|
|
None, MiToggle("Show One"), None, MiStatic("Rename selected layer:"), MiTypein(self.RenameLayer, "LayerName", 80), None, miQuit, None]))
|
|
def StLayerTab(self): return "Layers"
|
|
def Save(self):
|
|
self.game.drawing.Save()
|
|
msgscroller = self.game.rgtoken("msgscroller")[0]
|
|
msgscroller.evPost.fire("Saved picture")
|
|
|
|
def SelectLayer(self, layer):
|
|
self.ovPopup.SetSelection("layer", self.rgmiLayer.MiFromValue(layer))
|
|
miRename = self.miLayers.MiByName("LayerName").SetValue(layer.StName())
|
|
return True
|
|
|
|
def GetLayer(self):
|
|
return self.ovPopup.GetSelection("layer").Value()
|
|
|
|
def NewLayer(self):
|
|
ilayer = 1
|
|
stLayer = None
|
|
while stLayer == None:
|
|
stLayer = "Layer " + str(ilayer)
|
|
if self.game.drawing.LayerByName(stLayer) != None:
|
|
stLayer = None
|
|
ilayer = ilayer + 1
|
|
layerNew = self.game.drawing.NewLayer(stLayer)
|
|
if layerNew != None:
|
|
self.SelectLayer(layerNew)
|
|
return True
|
|
|
|
def LayerUp(self):
|
|
self.game.drawing.MoveLayer(self.GetLayer(), True)
|
|
return True
|
|
|
|
def LayerDown(self):
|
|
self.game.drawing.MoveLayer(self.GetLayer(), False)
|
|
return True
|
|
|
|
def RenameLayer(self, st):
|
|
if st != "":
|
|
self.GetLayer().SetName(st)
|
|
return True
|
|
|
|
def blinkCursor(self, fBlink):
|
|
self.fBlink = fBlink
|
|
if not fBlink and self.cDontBlink > 0:
|
|
self.cDontBlink = self.cDontBlink - 1
|
|
|
|
def SendMsg(self, st):
|
|
if st.lower().startswith("/me ") and len(st.rstrip()) > 3:
|
|
self.game.drawing.chat.Add(ChlAct(self.client, st[4:]))
|
|
else:
|
|
self.game.drawing.chat.Add(ChlMsg(self.client, st))
|
|
|
|
def OnChat(self, chl):
|
|
if type(chl) != ChlSys:
|
|
self.miChat.FlagNotice()
|
|
|
|
def run(self):
|
|
with self.game.drawing.chat.evChat.oob(self, self.OnChat):
|
|
while True:
|
|
key = self.client.evKey.receive(self)
|
|
fDontBlink = True
|
|
if key == ansi.K_LEFT:
|
|
self.pos.Left()
|
|
elif key == ansi.K_RIGHT:
|
|
self.pos.Right()
|
|
elif key == ansi.K_UP:
|
|
self.pos.Up()
|
|
elif key == ansi.K_DOWN:
|
|
self.pos.Down()
|
|
elif key == ansi.K_BACKSPACE:
|
|
self.pos.Left()
|
|
self.PutWb(ansi.achBlank)
|
|
elif key == ansi.K_DEL:
|
|
self.PutWb(ansi.achBlank)
|
|
self.pos.Right()
|
|
elif key == ansi.K_TAB:
|
|
fDontBlink = False
|
|
self.ovPopup.evShow.fire()
|
|
self.ovPopup.evDone.receive(self)
|
|
elif key == ansi.K_HOME:
|
|
self.Skip(self.pos.Left)
|
|
elif key == ansi.K_END:
|
|
self.Skip(self.pos.Right)
|
|
elif key == ansi.K_PGUP:
|
|
self.Skip(self.pos.Up)
|
|
elif key == ansi.K_PGDN:
|
|
self.Skip(self.pos.Down)
|
|
elif ansi.FKeyPrintable(key):
|
|
if key.isdigit():
|
|
self.PutWb(chr(self.cf.Value("Characters")[IchFromCh(key)]))
|
|
else:
|
|
self.PutWb(key)
|
|
self.pos.Right()
|
|
else:
|
|
print "weird key:", ansi.StrKey(key)
|
|
fDontBlink = False
|
|
|
|
if fDontBlink:
|
|
self.cDontBlink = 2 if self.fBlink else 1
|
|
|
|
if (self.game.drawing.H() < config.H) or (self.pos.Y() < self.game.drawing.H() / 2):
|
|
self.ovStatus.kplace = OvStatic.BOTTOM
|
|
else:
|
|
self.ovStatus.kplace = OvStatic.TOP
|
|
|
|
def PutWb(self, ch):
|
|
self.Put(ch, self.GetLayer().ascr)
|
|
self.game.drawing.Touch()
|
|
|
|
def Put(self, ch, ascr, xzOffset = 0, yzOffset = 0):
|
|
if type(ch) == str:
|
|
ach = ansi.MkAch(ch, self.cf.Value("Foreground"), self.cf.Value("Background"))
|
|
else:
|
|
ach = ch
|
|
ascr.PutAch(ach, self.pos.X() + xzOffset, self.pos.Y() + yzOffset)
|
|
|
|
def GetAch(self):
|
|
return self.GetLayer().ascr.GetAch(self.pos.X(), self.pos.Y())
|
|
|
|
def Skip(self, dgMove):
|
|
self.SkipI(dgMove, self.GetAch())
|
|
|
|
def SkipI(self, dgMove, ach):
|
|
fStop = False
|
|
while not fStop:
|
|
fStop = not dgMove() or self.GetAch() != ach
|
|
|
|
def XzYzScreenPic(self):
|
|
layer = self.GetLayer()
|
|
return ((config.W - layer.ascr.W()) / 2, (config.H - layer.ascr.H()) / 2)
|
|
|
|
def FDrawCursor(self):
|
|
return self.fBlink or self.cDontBlink > 0
|
|
|
|
def draw(self, ascr, client):
|
|
if self.FDrawCursor():
|
|
if client == self.client:
|
|
chCursor = chr(176)
|
|
ach = ascr.GetAch(self.pos.X(), self.pos.Y())
|
|
if ach != None and ansi.ChFromAch(ach) == chCursor:
|
|
chCursor = chr(177)
|
|
self.Put(chCursor, ascr, *self.XzYzScreenPic())
|
|
elif self.fBlink:
|
|
self.Put(str(self.client.Cldg().iclient)[0], ascr, *self.XzYzScreenPic())
|
|
|
|
class CursorSprite(Cursor):
|
|
def InitPersistent(self, game, client):
|
|
Cursor.InitPersistent(self, game, client)
|
|
self.blinkerAnimate = None
|
|
|
|
def CreateDrawingToken(self):
|
|
SpriteDraw(self, self.client)
|
|
|
|
def MenuDrawing(self, miFg, miBg, miChars, miQuit):
|
|
return MiMenu([None, miFg, miBg, None, miChars, None, miQuit, None])
|
|
|
|
def MenuLayers(self, miQuit):
|
|
return MiMenu(Rgseq([None], self.rgmiLayer, [None, MiButton("New Frame", self.NewLayer), MiButton("Move up", self.LayerUp), MiButton("Move down", self.LayerDown),
|
|
None, MiStatic("Rename selected frame:"), MiTypein(self.RenameLayer, "LayerName", 80), None, MiButton("Animate", self.Animate), None, miQuit, None]))
|
|
|
|
def StLayerTab(self): return "Frames"
|
|
def NewLayer(self):
|
|
stLayer = "Frame " + str(len(self.game.drawing.rglayer) + 1)
|
|
layerNew = self.game.drawing.NewLayer(stLayer)
|
|
if layerNew != None:
|
|
self.SelectLayer(layerNew)
|
|
return True
|
|
|
|
def Animate(self):
|
|
if (self.blinkerAnimate == None):
|
|
self.blinkerAnimate = Blinker(self, self.SelectLayer, self.game.drawing.rglayer, interval = 300)
|
|
self.ovPopup.SetSelection("animate", self.miLayers.MiByName("Animate"))
|
|
else:
|
|
self.blinkerAnimate.die()
|
|
self.blinkerAnimate = None
|
|
self.ovPopup.SetSelection("animate", None)
|
|
def FDrawCursor(self):
|
|
if self.blinkerAnimate:
|
|
return False
|
|
return Cursor.FDrawCursor(self)
|
|
class WhiteboardDraw(TokenClient):
|
|
def InitPersistent(self, owner, client):
|
|
TokenClient.InitPersistent(self, owner, client, "drawable", "solid")
|
|
|
|
def draw(self, ascr, client):
|
|
if client == self.client:
|
|
if self.owner.miLayers.Value("Show One"):
|
|
ascr.PutAscr(self.owner.GetLayer().ascr)
|
|
else:
|
|
self.game.drawing.draw(ascr)
|
|
|
|
class SpriteDraw(TokenClient):
|
|
def InitPersistent(self, owner, client):
|
|
TokenClient.InitPersistent(self, owner, client, "drawable", "solid")
|
|
|
|
def draw(self, ascr, client):
|
|
if client == self.client:
|
|
(xz, yz) = self.owner.XzYzScreenPic()
|
|
x = xz + 1
|
|
y = yz + 1
|
|
layer = self.owner.GetLayer()
|
|
ascr.PutAscr(layer.ascr, x, y)
|
|
# draw border
|
|
ascr.PutAch(ansi.MkAch(chr(218), ansi.WHITE | ansi.FBRIGHT), x - 1, y - 1) # top-left
|
|
ascr.PutAch(ansi.MkAch(chr(191), ansi.WHITE | ansi.FBRIGHT), x + layer.ascr.W(), y - 1) # top-right
|
|
ascr.PutAch(ansi.MkAch(chr(192), ansi.WHITE | ansi.FBRIGHT), x - 1, y + layer.ascr.H()) # bottom-left
|
|
ascr.PutAch(ansi.MkAch(chr(217), ansi.WHITE | ansi.FBRIGHT), x + layer.ascr.W(), y + layer.ascr.H()) # bottom-right
|
|
for yT in (y - 1, y + layer.ascr.H()):
|
|
ascr.PutSt(chr(196) * layer.ascr.W(), x, yT, ansi.WHITE | ansi.FBRIGHT) # -
|
|
for xT in (x - 1, x + layer.ascr.W()):
|
|
for yT in xrange(y, y + layer.ascr.H()):
|
|
ascr.PutAch(ansi.MkAch(chr(179), ansi.WHITE | ansi.FBRIGHT), xT, yT) # |
|
|
|
|
class CursorBlinker(Token):
|
|
def InitPersistent(self, owner):
|
|
Token.InitPersistent(self, owner)
|
|
Blinker(self, self.BlinkAllCursors)
|
|
|
|
def BlinkAllCursors(self, fBlink):
|
|
for tok in self.game.rgtoken("cursor"):
|
|
tok.blinkCursor(fBlink)
|
|
|
|
class MiColour(MiList):
|
|
def InitPersistent(self, stName, rgcolor, col):
|
|
self.rgcolor = rgcolor
|
|
MiList.InitPersistent(self, rgcolor, rgcolor.index(col))
|
|
self.stName = stName
|
|
|
|
def StName(self):
|
|
return self.stName
|
|
|
|
def DrawValue(self, ascr, x, y, selor):
|
|
for icol in range(len(self.rgcolor)):
|
|
col = self.rgcolor[icol]
|
|
ascr.PutAch(ansi.MkAch(chr(219) if icol != self.ival else chr(254), col, ansi.BLACK if col != ansi.BLACK else ansi.WHITE), x + icol, y)
|
|
|
|
class MiChars(MiList):
|
|
def InitPersistent(self, rgchar, miFg, miBg):
|
|
MiList.InitPersistent(self, rgchar)
|
|
self.miFg = miFg
|
|
self.miBg = miBg
|
|
def StName(self):
|
|
return "Characters"
|
|
def DrawValue(self, ascr, x, y, selor):
|
|
for ich, ch in enumerate(self.Value()):
|
|
ascr.PutSt(ChFromIch(ich) + ":", x, y, self.ColFg(), self.ColBg(selor))
|
|
ascr.PutAch(ansi.MkAch(chr(ch), self.miFg.Value(), self.miBg.Value()), x + 2, y)
|
|
x = x + 4
|
|
|
|
def ChFromIch(ich):
|
|
if ich == 9:
|
|
return '0'
|
|
return str(ich + 1)
|
|
|
|
def IchFromCh(ch):
|
|
if ch == '0':
|
|
return 9
|
|
return int(ch) - 1
|
|
|
|
class MiChat(Mi):
|
|
KWNDMSG = 0
|
|
KWNDUSERS = 1
|
|
def InitPersistent(self, client, height, chat):
|
|
Mi.InitPersistent(self)
|
|
self.client = client
|
|
self.chat = chat
|
|
self.height = height
|
|
assert height > 2
|
|
self.kwnd = self.KWNDMSG
|
|
self.imsg = 0
|
|
self.iuser = 0
|
|
|
|
def StName(self):
|
|
return "Chat"
|
|
|
|
def HandleKey(self, key):
|
|
if self.kwnd == self.KWNDUSERS:
|
|
if key == ansi.K_LEFT:
|
|
self.kwnd = self.KWNDMSG
|
|
elif (key == 'a' or key == 'A') and self.iuser > 0:
|
|
self.iuser = self.iuser - 1
|
|
elif (key == 'z' or key == 'Z') and self.iuser < self.chat.CUsers() - 1:
|
|
self.iuser = self.iuser + 1
|
|
else:
|
|
return False
|
|
else: #KWNDMSG
|
|
if key == ansi.K_RIGHT:
|
|
self.kwnd = self.KWNDUSERS
|
|
elif (key == 'a' or key == 'A') and self.imsg < self.chat.CChl() - 1:
|
|
self.imsg = self.imsg + 1
|
|
elif (key == 'z' or key == 'Z') and self.imsg > 0:
|
|
self.imsg = self.imsg - 1
|
|
else:
|
|
return False
|
|
return True
|
|
|
|
def Height(self, w):
|
|
return self.height
|
|
|
|
def ColBgDefault(self):
|
|
return ansi.BLACK
|
|
def ColBgSelected(self, fPri):
|
|
return ansi.BLACK
|
|
def ColFg(self):
|
|
return ansi.WHITE
|
|
|
|
def FIgnoreParent(self):
|
|
return True
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
wChat = (w / 4) * 3
|
|
wUser = (w / 4) - 1
|
|
self.chat.DrawMsg(ascr, x, y, wChat, self.height - 1, self.ColBg(selor), self.imsg)
|
|
for yT in range(y, y + self.height - 1):
|
|
ascr.PutAch(ansi.MkAch(chr(179)), x + wChat, yT)
|
|
ascr.PutSt("{0:{1}<{2}}{3}{0:{1}<{4}}".format("", chr(196), wChat, chr(193), wUser), x, y + self.height - 1)
|
|
self.chat.DrawUsers(ascr, x + wChat + 1, y, wUser, self.height - 1, self.ColBg(selor), self.iuser)
|
|
if selor.KSel() == Selor.PRI:
|
|
if self.kwnd == self.KWNDMSG:
|
|
xBar = x + wChat - 1
|
|
pct = 1 - (self.imsg / float(self.chat.CChl()))
|
|
else: # KWNDUSERS
|
|
xBar = x + w - 1
|
|
pct = self.iuser / float(self.chat.CUsers())
|
|
yBar = int(y + (pct * (self.height - 4)))
|
|
ascr.PutAch(ansi.MkAch("A", ansi.RED, ansi.WHITE), xBar, yBar)
|
|
ascr.PutAch(ansi.MkAch(chr(186), ansi.RED, ansi.WHITE), xBar, yBar + 1)
|
|
ascr.PutAch(ansi.MkAch("Z", ansi.RED, ansi.WHITE), xBar, yBar + 2)
|
|
|
|
class Joiner(LeaveJoinToken):
|
|
def InitPersistent(self, owner):
|
|
Token.InitPersistent(self, owner)
|
|
self.msgScroller = self.game.rgtoken("msgscroller")[0]
|
|
self.board = self.game.drawing
|
|
|
|
def OnJoin(self, client):
|
|
with client.Cldg().SetTransiently() as cldg:
|
|
cldg.iclient = -1
|
|
iclient = 1
|
|
rgiclient = [clientT.Cldg().iclient for clientT in self.game.rgclient]
|
|
while iclient in rgiclient:
|
|
iclient = iclient + 1
|
|
cldg.iclient = iclient
|
|
self.msgScroller.evPost.fire(client.cld.user + "(#" + str(cldg.iclient) + ") has joined! :)")
|
|
self.board.chat.Add(ChlSys(client, "has joined"))
|
|
|
|
def OnLeave(self, client):
|
|
self.msgScroller.evPost.fire(client.cld.user + " has left! :(")
|
|
self.board.chat.Add(ChlSys(client, "has left"))
|
|
|
|
class Lobby(TokenClient):
|
|
def InitPersistent(self, owner, client):
|
|
TokenClient.InitPersistent(self, owner, client)
|
|
drawing = client.Cldg(self.game).drawingSelected
|
|
if drawing == None:
|
|
self.lobbyCurrent = LobbyProject(self, client)
|
|
else:
|
|
self.lobbyCurrent = LobbyBoard(self, client, drawing.owner, drawing)
|
|
|
|
def SwitchToLobby(self, lobbyNew):
|
|
lobbyOld = self.lobbyCurrent
|
|
self.lobbyCurrent = lobbyNew
|
|
lobbyOld.die()
|
|
|
|
class LobbyI(TokenClient):
|
|
def InitPersistent(self, owner, client, objSelected = None, *rgmiListTop):
|
|
TokenClient.InitPersistent(self, owner, client)
|
|
|
|
rgmiList = RgmiProjected(self.RgObj(), DgProjectMiButton(self.OnObjSelected))
|
|
if rgmiListTop != None:
|
|
rgmiList = Rgseq(rgmiListTop, rgmiList)
|
|
self.miDrawings = MiMenu(rgmiList)
|
|
if objSelected:
|
|
mi = self.miDrawings.MiByName(objSelected.StName())
|
|
self.miDrawings.selMi.SetSelected(mi)
|
|
self.miCmds = self.CreateMiCmds()
|
|
OvMenu(self, client, MiMenuHoriz([self.miDrawings, self.miCmds], [65, 35]), OvMenu.FILL)
|
|
|
|
def RgObj(self):
|
|
"Return the list of objects we are selecting from"
|
|
raise "not implemented"
|
|
|
|
def OnObjSelected(self, obj):
|
|
"Called when an object is selected by the user"
|
|
raise "not implemented"
|
|
|
|
def RgstMetadata(self, obj):
|
|
"Return a list of strings containing user-readable metadata about the object"
|
|
raise "not implemented"
|
|
|
|
def StMetadata(self):
|
|
try:
|
|
return "\n".join(self.RgstMetadata(self.miDrawings.Value()))
|
|
except:
|
|
return ""
|
|
|
|
def MiTypein_MiButton(self, stBtnLabel, stMiTypein, dgOnSelect):
|
|
def NewI(stName = None):
|
|
if stName == None:
|
|
stName = self.miCmds.Value(stMiTypein)
|
|
if stName != "":
|
|
dgOnSelect(stName)
|
|
return (MiTypein(NewI, stMiTypein), MiButton(stBtnLabel, NewI))
|
|
|
|
class LobbyBoard(LobbyI):
|
|
def InitPersistent(self, owner, client, project, drawing = None):
|
|
self.project = project
|
|
LobbyI.InitPersistent(self, owner, client, drawing, MiButton("<-- Back to projects", self.Back), None)
|
|
|
|
def CreateMiCmds(self):
|
|
miTypeinBoard, miBtnNewBoard = self.MiTypein_MiButton("New Board", "BoardName", self.NewBoard)
|
|
miTypeinSpr, miBtnNewSpr = self.MiTypein_MiButton("New Sprite", "SpriteName", self.NewSprite)
|
|
miTypeinW = MiTypein(None, "w")
|
|
miTypeinW.SetValue("1")
|
|
miTypeinH = MiTypein(None, "h")
|
|
miTypeinH.SetValue("1")
|
|
self.miDimensions = MiMenuHoriz([MiStatic("W:"), miTypeinW, MiStatic("H:"), miTypeinH])
|
|
|
|
return MiMenu([None, None, miTypeinBoard, miBtnNewBoard, None, None, miTypeinSpr, self.miDimensions, miBtnNewSpr, None, None, None, None, None, MiDgText(self.StMetadata)])
|
|
|
|
def RgObj(self):
|
|
return self.project.rgdrawing
|
|
def OnObjSelected(self, board):
|
|
self.client.leaveGame(board)
|
|
def NewBoard(self, stName):
|
|
self.OnObjSelected(self.project.NewBoard(stName, self.client.cld.user))
|
|
def NewSprite(self, stName):
|
|
try:
|
|
w = int(self.miDimensions.Value("w"))
|
|
h = int(self.miDimensions.Value("h"))
|
|
if 0 < w < config.W and 0 < h < config.H:
|
|
self.OnObjSelected(self.project.NewSprite(stName, self.client.cld.user, w, h))
|
|
except:
|
|
pass
|
|
def RgstMetadata(self, board):
|
|
if board != None:
|
|
return board.RgstMetadata()
|
|
return self.project.RgstMetadata()
|
|
|
|
def Back(self):
|
|
self.owner.SwitchToLobby(LobbyProject(self.owner, self.client, self.project))
|
|
|
|
class LobbyProject(LobbyI):
|
|
def CreateMiCmds(self):
|
|
miTypein, miBtnNew = self.MiTypein_MiButton("New Project", "ProjName", self.New)
|
|
return MiMenu([None, None, miTypein, miBtnNew, None, None, None, None, None, None, None, None, MiDgText(self.StMetadata)])
|
|
|
|
def StNewObj(self):
|
|
return "New Project"
|
|
def RgObj(self):
|
|
return self.game.rgproject
|
|
def OnObjSelected(self, project):
|
|
self.owner.SwitchToLobby(LobbyBoard(self.owner, self.client, project))
|
|
def New(self, stName):
|
|
project = self.game.NewProject(stName, self.client.cld.user)
|
|
self.OnObjSelected(project)
|
|
def RgstMetadata(self, project):
|
|
return project.RgstMetadata()
|
|
|
|
class RunnerWB(Runner):
|
|
def InitPersistent(self):
|
|
self.gameLobby = GameLobby()
|
|
Runner.InitPersistent(self)
|
|
|
|
def RunGame(self, client):
|
|
if (client.joinGame(GameLogin())):
|
|
client.Cldg(self.gameLobby).drawingSelected = None
|
|
while True:
|
|
drawing = client.joinGame(self.gameLobby)
|
|
client.Cldg(self.gameLobby).drawingSelected = drawing
|
|
client.joinGame(self.gameLobby.GameFromDrawing(drawing))
|
|
|
|
class GameLogin(Game):
|
|
def GetRgclsTokTrans(self):
|
|
return [[AutoJoiner, login.LoginTerm]]
|
|
|
|
@Version(6)
|
|
class GameLobby(Game):
|
|
def InitPersistent(self):
|
|
self.rgproject = []
|
|
Game.InitPersistent(self)
|
|
|
|
def InitTransient(self):
|
|
Game.InitTransient(self)
|
|
self.mpdrawing_game = {}
|
|
|
|
def GetRgclsTokTrans(self):
|
|
return [[AutoJoiner, Lobby]]
|
|
|
|
def GameFromDrawing(self, drawing):
|
|
if not (drawing in self.mpdrawing_game):
|
|
if drawing.StType() == "Board":
|
|
game = GameWB(self, drawing)
|
|
elif drawing.StType() == "Sprite":
|
|
game = GameSprEdit(self, drawing)
|
|
else:
|
|
assert False, "Lobby not prepared to deal with drawing of type " + drawing.StType()
|
|
def KillBoard():
|
|
del self.mpdrawing_game[drawing]
|
|
game.rgdgdie.append(KillBoard)
|
|
self.mpdrawing_game[drawing] = game
|
|
return game
|
|
return self.mpdrawing_game[drawing]
|
|
|
|
def CclientDrawing(self, drawing):
|
|
if drawing in self.mpdrawing_game:
|
|
return len(self.mpdrawing_game[drawing].rgclient)
|
|
return 0
|
|
|
|
def StNameProject(self, project):
|
|
return project.stName + " [" + str(sum([self.CclientDrawing(drawing) for drawing in project.rgdrawing])) + "]"
|
|
|
|
def StNameDrawing(self, drawing):
|
|
return drawing.stName + " [" + str(self.CclientDrawing(drawing)) + "]"
|
|
|
|
def NewProject(self, stName, user):
|
|
proj = Project(self, stName, user)
|
|
self.rgproject.insert(0, proj)
|
|
return proj
|
|
|
|
def DrawingTouched(self, drawing):
|
|
project = drawing.owner
|
|
if project != self.rgproject[0]:
|
|
self.rgproject.remove(project)
|
|
self.rgproject.insert(0, project)
|
|
#haaaack
|
|
self.ensureRun() # redraw
|
|
|
|
def UpgradeFrom(self, versionOld):
|
|
if versionOld < 3:
|
|
for gameWB in self.rggameWB:
|
|
gameWB.gameLobby = self
|
|
if versionOld < 4:
|
|
self.rgboard = [game.rgtoken("whiteboard")[0].board for game in self.rggameWB]
|
|
for gameWB in self.rggameWB:
|
|
gameWB.finish()
|
|
if versionOld < 5:
|
|
del self.rggameWB
|
|
if versionOld < 6:
|
|
project = Project(self, "Drawings", "Willy Marmot")
|
|
for board in self.rgboard:
|
|
board.TransferToOwner(project)
|
|
project.rgdrawing.append(board)
|
|
del self.rgboard
|
|
self.rgproject = [project]
|
|
self.mpuser_cldg = {} # ugh, upgrading parent classes is broken in our model :(
|
|
|
|
class GameWB(Game):
|
|
def InitPersistent(self, gameLobby, board):
|
|
self.gameLobby = gameLobby
|
|
self.drawing = board
|
|
Game.InitPersistent(self)
|
|
|
|
def InitTransient(self):
|
|
Game.InitTransient(self)
|
|
if not self.FDead():
|
|
self.drawing.chat.AttachToGame(self)
|
|
self.rgdgdie.append(self.drawing.chat.DetachFromGame)
|
|
|
|
def ClsCursor(self):
|
|
return Cursor
|
|
|
|
def GetRgclsTokTrans(self):
|
|
return [MsgScroller, Joiner, [AutoJoiner, self.ClsCursor()], [AliveWithPlayers, CursorBlinker], AutoKiller]
|
|
|
|
def GetRgtagDraw(self):
|
|
return ["background", "solid", "cursor", "menu", "msgscroller"]
|
|
|
|
class GameSprEdit(GameWB):
|
|
def ClsCursor(self):
|
|
return CursorSprite
|
|
|
|
if __name__ == "__main__":
|
|
Run(RunnerWB, "whiteboard.marm")
|