marmots/whiteboard.py

942 lines
35 KiB
Python
Executable file

#!/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 <https://www.gnu.org/licenses/>.
#
# 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 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()
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("<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()
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 = "<Tab>: Menu"
if self.GetLayer() == self.game.drawing.LayerObj():
botdef = self.BotdefAtPos(self.pos)
if botdef:
stStatus += " | <Enter>: Edit bot"
elif self.game.drawing.AchAtPos(self.pos) != ansi.achBlank:
stStatus += " | <Enter>: 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 StStatus(self): return "Hit <Tab> 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):
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 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)