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 Draw(self, ascr): ascr.PutAscr(self.ascr) 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.rgboard = [] self.rgscript = [] def NewBoard(self, stName, user): board = Board(self, stName, user) self.rgboard.insert(0, board) return board def BoardTouched(self, board): if board != self.rgboard[0]: self.rgboard.remove(board) self.rgboard.insert(0, board) self.owner.BoardTouched(board) def StName(self): return self.owner.StNameProject(self) def StLastModified(self): boardLast = None for board in self.rgboard: if boardLast == None or board.dtLastModified > boardLast.dtLastModified: boardLast = board if boardLast != None: return boardLast.StLastModified() return "never" @Version(3) class Board(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.StNameBoard(self) 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 W(self): return config.W def H(self): return config.H def Touch(self): self.SetTimestamp() self.owner.BoardTouched(self) def SetTimestamp(self): self.dtLastModified = datetime.now() def draw(self, ascr): for layer in reversed(self.rglayer): layer.Draw(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("
") fl.write(ascr.Hst()) fl.write("
") 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() @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.whiteboard = self.game.rgtoken("whiteboard")[0].board self.pos = ansi.Pos(self.whiteboard.W(), self.whiteboard.H()) 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 = MiMenu([None, miFg, miBg, None, miChars, None, MiButton("Save", self.Save), None, miQuit, None]) self.miChat = MiMenu([None, MiChat(client, 13, self.whiteboard.chat), MiTypein(self.SendMsg), None, miQuit, None]) self.rgmiLayer = RgmiProjected(self.whiteboard.rglayer, DgProjectMiButton(self.SelectLayer)) self.miLayers = 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])) self.ovPopup = OvPopup(self, self.client, MiTab.MiTabbed(["Drawing", self.cf], ["Layers", self.miLayers], ["Chat", self.miChat])) miStatus = MiMenuHoriz([MiStatic("Hit for menu"), MiValue(miChars)]) miStatus.ListenForNotice(self.miChat) self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM) self.SelectLayer(self.whiteboard.rglayer[0]) WhiteboardDraw(self, client) def Save(self): self.whiteboard.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.whiteboard.LayerByName(stLayer) != None: stLayer = None ilayer = ilayer + 1 layerNew = self.whiteboard.NewLayer(stLayer) if layerNew != None: self.SelectLayer(layerNew) return True def LayerUp(self): self.whiteboard.MoveLayer(self.GetLayer(), True) return True def LayerDown(self): self.whiteboard.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.whiteboard.chat.Add(ChlAct(self.client, st[4:])) else: self.whiteboard.chat.Add(ChlMsg(self.client, st)) def OnChat(self, chl): if type(chl) != ChlSys: self.miChat.FlagNotice() def run(self): with self.whiteboard.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.pos.Y() < self.whiteboard.H() / 2: self.ovStatus.kplace = OvStatic.BOTTOM else: self.ovStatus.kplace = OvStatic.TOP def PutWb(self, ch): self.Put(ch, self.GetLayer().ascr) self.whiteboard.Touch() def Put(self, ch, ascr): 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(), self.pos.Y()) 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 draw(self, ascr, client): if self.fBlink or self.cDontBlink > 0: 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) elif self.fBlink: self.Put(str(self.client.Cldg().iclient)[0], ascr) 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"): self.owner.GetLayer().Draw(ascr) else: self.owner.whiteboard.draw(ascr) 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.rgtoken("whiteboard")[0].board 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) self.lobbyCurrent = LobbyProject(self, client) def SwitchToLobby(self, lobbyNew): lobbyOld = self.lobbyCurrent self.lobbyCurrent = lobbyNew lobbyOld.die() class LobbyI(TokenClient): def InitPersistent(self, owner, client, *rgmiListTop): TokenClient.InitPersistent(self, owner, client) rgmiList = RgmiProjected(self.RgObj(), DgProjectMiButton(self.OnObjSelected)) if rgmiListTop != None: rgmiList = Rgseq(rgmiListTop, rgmiList) self.miDrawings = MiMenu(rgmiList) self.miCmds = MiMenu([None, None, MiTypein(self.NewI, "ObjectName"), MiButton(self.StNewObj(), self.NewI), None, None, None, None, None, None, None, None, MiStatic("Last modified:"), MiDgText(self.StLastModifiedI)]) OvMenu(self, client, MiMenuHoriz([self.miDrawings, self.miCmds], [65, 35]), OvMenu.FILL) def StNewObj(self): "Text for the 'New X' text field" raise "not implemented" 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 New(self, stName): "Create a new object with the given name" raise "not implemented" def StLastModified(self, obj): "Return a string stating when the object was last modified" raise "not implemented" def StLastModifiedI(self): return self.StLastModified(self.miDrawings.Value()) def NewI(self, stName = None): if stName == None: stName = self.miCmds.Value("ObjectName") if stName != "": self.New(stName) class LobbyBoard(LobbyI): def InitPersistent(self, owner, client, project): self.project = project LobbyI.InitPersistent(self, owner, client, MiButton("<-- Back to projects", self.Back), None) def StNewObj(self): return "New Board" def RgObj(self): return self.project.rgboard def OnObjSelected(self, board): self.client.leaveGame(board) def New(self, stName): self.OnObjSelected(self.project.NewBoard(stName, self.client.cld.user)) def StLastModified(self, board): if board != None: return board.StLastModified() return self.project.StLastModified() def Back(self): self.owner.SwitchToLobby(LobbyProject(self.owner, self.client)) class LobbyProject(LobbyI): 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 StLastModified(self, project): return project.StLastModified() class RunnerWB(Runner): def InitPersistent(self): self.gameLobby = GameLobby() Runner.InitPersistent(self) def RunGame(self, client): if (client.joinGame(GameLogin())): while True: board = client.joinGame(self.gameLobby) client.joinGame(self.gameLobby.GameFromBoard(board)) 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.mpboard_gameWB = {} def GetRgclsTokTrans(self): return [[AutoJoiner, Lobby]] def GameFromBoard(self, board): if not (board in self.mpboard_gameWB): gameWB = GameWB(self, board) def KillBoard(): del self.mpboard_gameWB[board] gameWB.rgdgdie.append(KillBoard) self.mpboard_gameWB[board] = gameWB return gameWB return self.mpboard_gameWB[board] def CclientBoard(self, board): if board in self.mpboard_gameWB: return len(self.mpboard_gameWB[board].rgclient) return 0 def StNameProject(self, project): return project.stName + " (by " + project.user + ") [" + str(sum([self.CclientBoard(board) for board in project.rgboard])) + "]" def StNameBoard(self, board): return board.stName + " (by " + board.user + ") [" + str(self.CclientBoard(board)) + "]" def NewProject(self, stName, user): proj = Project(self, stName, user) self.rgproject.insert(0, proj) return proj def BoardTouched(self, board): project = board.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.rgboard.append(board) del self.rgboard self.rgproject = [project] class GameWB(Game): def InitPersistent(self, gameLobby, board): self.gameLobby = gameLobby self.board = board Game.InitPersistent(self) def InitTransient(self): Game.InitTransient(self) if not self.FDead(): self.board.chat.AttachToGame(self) self.rgdgdie.append(self.board.chat.DetachFromGame) def GetRgclsTokPers(self): return [[Whiteboard, self.board]] def GetRgclsTokTrans(self): return [MsgScroller, Joiner, [AutoJoiner, Cursor], [AliveWithPlayers, CursorBlinker], AutoKiller] def GetRgtagDraw(self): return ["background", "solid", "cursor", "menu", "msgscroller"] if __name__ == "__main__": Run(RunnerWB, "whiteboard.marm")