#!/usr/bin/env python2 # This file is part of MarMOTS. # # MarMOTS is free software: you can redistribute it and/or modify it under the terms of the GNU Affero # General Public License as published by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # MarMOTS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General # Public License for more details. # # You should have received a copy of the GNU Affero General Public License along with MarMOTS. If not, # see . # # Copyright 2009, 2010, 2011, 2020 Jeremy Penner from engine import * from basetoken import * from tpers import * from datetime import * from util import * from scripting import Botdef, Flagdef, Defs, PselState, Pov import telnet import time import login import weakref import sys 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, ascr = None): if len(self.rglayer) < 8: if ascr == None: ascr = ansi.Ascr(self.W(), self.H()) layerNew = Layer(ascr, 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(4) class Board(Drawing): def InitPersistent(self, owner, stName, user, rglayer=None, dtLastModified=None): super(Board, self).InitPersistent(owner, stName, user, rglayer=rglayer, dtLastModified=dtLastModified) self.defs = Defs() self.layerObj = self.rglayer[0] 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 DrawObjectDefs(self, ascr): for botdef in self.defs.rgbotdef: ascr.PutAch(botdef.ach, botdef.x, botdef.y) 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() if versionOld < 4: self.defs = Defs() self.layerObj = self.rglayer[0] 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.MakeTransient("game", None) self.MakeTransient("evChat", None) def InitPersistent(self): TPrs.InitPersistent(self) self.rgchl = [] def AttachToGame(self, game): with self.SetTransiently(): self.game = game self.evChat = Event(game) def DetachFromGame(self): with self.SetTransiently(): 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.RgclientConnected()) 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.RgclientConnected(), lambda c1, c2: cmp(c1.Cldg(self.game).iclient, c2.Cldg(self.game).iclient))[iuser:]: ascr.PutSt("{0:{1}}".format(str(client.Cldg(self.game).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.StLayerName)) 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([MiDgText(self.StStatus), MiValue(miChars)]) miStatus.ListenForNotice(self.miChat) self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM) def StLayerName(self, layer): return ('[Bot] ' if layer == self.game.drawing.layerObj else '') + layer.StName() def StStatus(self): stStatus = ": Menu" if self.GetLayer() == self.game.drawing.layerObj: botdef = self.BotdefAtPos(self.pos) if botdef: stStatus += " | : Edit bot" elif self.game.drawing.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y()) != ansi.achBlank: stStatus += " | : Make bot" return stStatus def BotdefAtPos(self, pos): for botdef in self.game.drawing.defs.rgbotdef: if botdef.x == pos.X() and botdef.y == pos.Y(): return botdef 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"), MiButton("Set as bot layer", self.SetBotLayer), 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): selLayer = self.ovPopup.GetSelection("layer") return selLayer.Value().Value() if selLayer else None 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 SetBotLayer(self): self.game.drawing.layerObj = self.GetLayer() 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(self.game).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 key == ansi.K_NEWLINE or key == ansi.K_RETURN: self.EditBot() 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 EditBot(self): if self.GetLayer() == self.game.drawing.layerObj: botdef = self.BotdefAtPos(self.pos) ach = self.game.drawing.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y()) fNewBot = ach != ansi.achBlank if not botdef else False if not fNewBot and not botdef: return if fNewBot: botdef = Botdef('Bot ' + str(len(self.game.drawing.defs.rgbotdef) + 1) + ' (' + ansi.Ach(ach).ch + ')', ach, self.pos.X(), self.pos.Y()) evDone = Event(self.game) def DgDone(val): return lambda: evDone.fire(val) miBotName = MiTypein(None, "BotName", 80) miBotName.SetValue(botdef.stName) miMenu = MiMenu([None, MiStatic("Bot Name"), miBotName, None, MiToggle("Is Player", botdef.fPlayer), None, MiButton("Save", DgDone(True)), MiButton("Cancel", DgDone(False)), None, MiButton("Edit Code (live)", lambda: self.client.joinGame(self.game.GameSyntEdit(botdef.syntOnTouch))), None]) ov = OvMenu(self, self.client, miMenu, OvMenu.MIDDLE) try: result = evDone.receive(self) finally: ov.die() if result: botdef.stName = miBotName.Value() botdef.fPlayer = miMenu.MiByName("Is Player").Value() if fNewBot: self.game.drawing.defs.rgbotdef.append(botdef) 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(self.game).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 StStatus(self): return "Hit for menu" def StLayerTab(self): return "Frames" def NewLayer(self): stLayer = "Frame " + str(len(self.game.drawing.rglayer) + 1) layer = self.GetLayer() ascr = ansi.Ascr(self.game.drawing.W(), self.game.drawing.H()) ascr.PutAscr(layer.ascr) layerNew = self.game.drawing.NewLayer(stLayer, ascr) 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: fDrawObjects = True if self.owner.miLayers.Value("Show One"): ascr.PutAscr(self.owner.GetLayer().ascr) fDrawObjects = self.owner.GetLayer() == self.owner.layerObj else: self.game.drawing.draw(ascr) if fDrawObjects: self.game.drawing.DrawObjectDefs(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(self.game).SetTransiently() as cldg: cldg.iclient = -1 iclient = 1 rgiclient = [clientT.Cldg(self.game).iclient for clientT in self.game.RgclientConnected()] 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_DgNew(self, stBtnLabel, stMiTypein, dgOnSelect): def NewI(*rgarg): stName = self.miCmds.Value(stMiTypein) if stName != "": dgOnSelect(stName) return (MiTypein(NewI, stMiTypein), MiButton(stBtnLabel, NewI), NewI) def MiTypein_MiButton(self, stBtnLabel, stMiTypein, dgOnSelect): return self.MiTypein_MiButton_DgNew(stBtnLabel, stMiTypein, dgOnSelect)[:2] 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, dgNewSprite = self.MiTypein_MiButton_DgNew("New Sprite", "SpriteName", self.NewSprite) miTypeinW = MiTypein(dgNewSprite, "w") miTypeinW.SetValue("1") miTypeinH = MiTypein(dgNewSprite, "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]] class Mpsubgame(TPrs): def InitPersistent(self, game, dgMakeGame): self.game = game self.dgMakeGame = dgMakeGame self.mpkey_subgame = {} def SubgameFromKey(self, key): if not (key in self.mpkey_subgame): subgame = self.dgMakeGame(key) subgame.rgdgdie.append(Partial(self.KillSubgame, key)) self.mpkey_subgame[key] = subgame return self.mpkey_subgame[key] def KillSubgame(self, key): del self.mpkey_subgame[key] def __contains__(self, key): return key in self.mpkey_subgame def __getitem__(self, key): return self.mpkey_subgame[key] @Version(7) class GameLobby(Game): def InitPersistent(self): self.rgproject = [] Game.InitPersistent(self) def InitTransient(self): Game.InitTransient(self) self.mpdrawing_game = Mpsubgame(self, self.MakeGameFromDrawing) self.mpdrawingsynt_game = Mpsubgame(self, self.MakeGameSyntEdit) def GetRgclsTokTrans(self): return [[AutoJoiner, Lobby]] def GameFromDrawing(self, drawing): return self.mpdrawing_game.SubgameFromKey(drawing) def MakeGameFromDrawing(self, drawing): if drawing.StType() == "Board": return GameWB(self, drawing) elif drawing.StType() == "Sprite": return GameSprEdit(self, drawing) else: assert False, "Lobby not prepared to deal with drawing of type " + drawing.StType() # we accidentally persisted references to this method that are immediately removed by Chat.InitTransient() def KillBoard(self, drawing): pass def GameFromSynt(self, drawing, synt): return self.mpdrawingsynt_game.SubgameFromKey((drawing, synt)) def MakeGameSyntEdit(self, drawing_synt): drawing, synt = drawing_synt return GameSyntEdit(drawing, synt) def CclientDrawing(self, drawing): if drawing in self.mpdrawing_game: return len(self.mpdrawing_game[drawing].RgclientConnected()) 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] # ugh, upgrading parent classes is broken in our model :( if versionOld < 6: Game.UpgradeFrom(self, 1) elif versionOld < 7: Game.UpgradeFrom(self, 2) 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"] def GameSyntEdit(self, synt): return self.gameLobby.GameFromSynt(self.drawing, synt) class GameSyntEdit(Game): def InitPersistent(self, board, synt): Game.InitPersistent(self) self.board = board self.synt = synt def GetRgclsTokTrans(self): return [[PselState, self.board.defs, self.synt], [AutoJoiner, [Pov, self.board.defs, self.synt]]] class GameSprEdit(GameWB): def ClsCursor(self): return CursorSprite if __name__ == "__main__": if len(sys.argv) > 1: config.override(sys.argv[1]) Run(RunnerWB, config.ODBPATH)