#!/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, Vm from world import GameWorld, Chl, ChlMsg, ChlAct, ChlSys, Chat, MiChat, Joiner 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 DrawingLastModified(self): drawingLast = None for drawing in self.rgdrawing: if drawingLast == None or drawing.dtLastModified > drawingLast.dtLastModified: drawingLast = drawing return drawingLast def StLastModified(self): drawingLast = self.DrawingLastModified() 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() def FPlayable(self): return False @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() def StType(self): return "Board" def W(self): return config.W def H(self): return config.H def FPlayable(self): return len([botdef for botdef in self.defs.rgbotdef if botdef.fPlayer]) > 0 def draw(self, ascr): for layer in reversed(self.rglayer): ascr.PutAscr(layer.ascr) def DrawObjectDefs(self, ascr, bgColor = None): for botdef in self.defs.rgbotdef: ach = botdef.ach if bgColor != None: oach = ansi.Ach(ach) oach.bgCol = bgColor ach = oach.ToAch() ascr.PutAch(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() def LayerObj(self): return self.rglayer[0] def AchAtPos(self, pos, layer = None): if layer == None: layer = self.LayerObj() return layer.ascr.GetAch(pos.X(), pos.Y()) 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 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) chat = self.game.drawing.chat self.miChat = MiMenu([None, MiChat(self.client, 13, chat), MiTypein(Partial(chat.SendMsg, self.client)), 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.AchAtPos(self.pos) != 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 to ANSI Gallery", 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): 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 blinkCursor(self, fBlink): self.fBlink = fBlink if not fBlink and self.cDontBlink > 0: self.cDontBlink = self.cDontBlink - 1 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.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 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.AchAtPos(self.pos) 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()) self.game.drawing.defs.rgbotdef.append(botdef) evDone = Event(self.game) miBotName = MiTypein(None, "BotName", 80) miBotName.SetValue(botdef.stName) miMenu = MiMenu([None, MiStatic("Bot Name"), miBotName, MiToggle("Is Player", botdef.fPlayer), MiButton("Edit Code (live)", lambda: self.client.joinGame(self.game.GameSyntEdit(botdef.syntOnTouch))), None, MiButton("Save", lambda: evDone.fire(True)), None, None, MiButtonConfirm("Delete", "Hit RETURN again to confirm!", lambda: evDone.fire(False)), 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() else: self.game.drawing.defs.rgbotdef.remove(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.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 StLayerName(self, layer): return layer.StName() 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", "cursor") self.color = 0 self.fBlink = False 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, self.color if not self.fBlink else None) def blinkCursor(self, fBlink): self.fBlink = fBlink if fBlink: self.color = (self.color + 1) % 8 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 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): if board.FPlayable(): self.owner.SwitchToLobby(LobbyWorld(self.owner, self.client, self.project, board)) else: self.client.leaveGame((board, self.game.GameFromDrawing(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 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 LobbyWorld(LobbyI): def InitPersistent(self, owner, client, project, board): self.project = project self.board = board LobbyI.InitPersistent(self, owner, client, None, MiButton("<-- Back to drawings", self.Back), None) def CreateMiCmds(self): miBtnNew = MiButton("New Game", self.NewGame) miBtnEdit = MiButton("Edit board", self.Edit) return MiMenu([None, None, miBtnNew, None, miBtnEdit, None, None, None, None, MiDgText(self.StMetadata)]) def RgObj(self): return self.game.RggameWorld(self.board) def OnObjSelected(self, game): self.client.leaveGame((self.board, game)) def NewGame(self): self.OnObjSelected(self.game.StartNewWorld(self.board)) def Edit(self): self.client.leaveGame((self.board, self.game.GameFromDrawing(self.board))) def Back(self): self.owner.SwitchToLobby(LobbyBoard(self.owner, self.client, self.project, self.board)) def RgstMetadata(self, game): return [client.cld.user for client in game.RgclientConnected()] 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, game = client.joinGame(self.gameLobby) client.Cldg(self.gameLobby).drawingSelected = drawing client.joinGame(game) 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) self.mpboard_rggame = {} 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 StartNewWorld(self, board): if board not in self.mpboard_rggame: self.mpboard_rggame[board] = [] game = GameWorld(board) game.rgdgdie.append(Partial(self.OnWorldDie, game)) self.mpboard_rggame[board].append(game) return game def OnWorldDie(self, game): rggame = self.mpboard_rggame[game.board] rggame.remove(game) if len(rggame) == 0: del self.mpboard_rggame[game.board] def RggameWorld(self, board): if board in self.mpboard_rggame: return self.mpboard_rggame[board] return [] def CclientDrawing(self, drawing): cclient = 0 if drawing in self.mpdrawing_game: cclient += len(self.mpdrawing_game[drawing].RgclientConnected()) for gameWorld in self.RggameWorld(drawing): cclient += len(gameWorld.RgclientConnected()) return cclient 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 Chat(self): return self.drawing.chat 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)