marmots/whiteboard.py

949 lines
35 KiB
Python
Raw Normal View History

2020-06-28 18:20:31 +00:00
#!/usr/bin/env python2
2020-06-28 15:27:56 +00:00
# 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
2011-03-19 00:10:02 +00:00
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
2011-03-19 00:10:02 +00:00
import telnet
import time
import login
import weakref
import sys
2011-03-19 00:10:02 +00:00
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
2011-09-14 22:30:44 +00:00
class Project(Ownable):
def InitPersistent(self, owner, stName, user):
Ownable.InitPersistent(self, owner)
self.stName = stName
self.user = user
self.rgdrawing = []
2011-09-14 22:30:44 +00:00
self.rgscript = []
2011-09-16 12:57:31 +00:00
def NewBoard(self, stName, user):
board = Board(self, stName, user)
self.rgdrawing.insert(0, board)
2011-09-16 12:57:31 +00:00
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)
2011-09-14 22:30:44 +00:00
def StName(self):
return self.owner.StNameProject(self)
2020-08-13 21:06:41 +00:00
def DrawingLastModified(self):
drawingLast = None
for drawing in self.rgdrawing:
if drawingLast == None or drawing.dtLastModified > drawingLast.dtLastModified:
drawingLast = drawing
2020-08-13 21:06:41 +00:00
return drawingLast
def StLastModified(self):
drawingLast = self.DrawingLastModified()
if drawingLast != None:
return drawingLast.StLastModified()
2011-09-14 22:30:44 +00:00
return "never"
def RgstMetadata(self):
return ["by " + self.user, "", "Last modified:", self.StLastModified()]
class Drawing(Ownable):
2011-03-19 00:10:02 +00:00
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
2011-03-19 00:10:02 +00:00
def StName(self):
return self.owner.owner.StNameDrawing(self)
def RgstMetadata(self):
return [self.StType(), "by " + self.user, "", "Last modified", self.StLastModified()]
2011-03-19 00:10:02 +00:00
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):
2011-03-19 00:10:02 +00:00
if len(self.rglayer) < 8:
if ascr == None:
ascr = ansi.Ascr(self.W(), self.H())
layerNew = Layer(ascr, stName)
2011-03-19 00:10:02 +00:00
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)
2011-03-19 00:10:02 +00:00
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
2011-03-19 00:10:02 +00:00
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)
2011-04-12 15:37:52 +00:00
2011-03-19 00:10:02 +00:00
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())
2011-03-19 00:10:02 +00:00
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()
2011-03-19 00:10:02 +00:00
def LayerObj(self):
2020-08-02 02:22:48 +00:00
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()]
2011-03-19 00:10:02 +00:00
@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)
2011-04-12 15:37:52 +00:00
2011-03-19 00:10:02 +00:00
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):
2011-03-19 00:10:02 +00:00
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
2011-09-30 12:47:41 +00:00
[ 1, 2, 3, 4, 5, 6, 14, 15, 19, 20], #smiles
2011-03-19 00:10:02 +00:00
[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)
2011-03-19 00:10:02 +00:00
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)])
2011-03-19 00:10:02 +00:00
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),
2020-08-02 02:22:48 +00:00
None, MiToggle("Show One"), None,
MiStatic("Rename selected layer:"), MiTypein(self.RenameLayer, "LayerName", 80), None, miQuit, None]))
def StLayerTab(self): return "Layers"
2011-03-19 00:10:02 +00:00
def Save(self):
self.game.drawing.Save()
2011-03-19 00:10:02 +00:00
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
2011-03-19 00:10:02 +00:00
def NewLayer(self):
ilayer = 1
stLayer = None
while stLayer == None:
stLayer = "Layer " + str(ilayer)
if self.game.drawing.LayerByName(stLayer) != None:
2011-03-19 00:10:02 +00:00
stLayer = None
ilayer = ilayer + 1
layerNew = self.game.drawing.NewLayer(stLayer)
2011-03-19 00:10:02 +00:00
if layerNew != None:
self.SelectLayer(layerNew)
return True
def LayerUp(self):
self.game.drawing.MoveLayer(self.GetLayer(), True)
2011-03-19 00:10:02 +00:00
return True
def LayerDown(self):
self.game.drawing.MoveLayer(self.GetLayer(), False)
2011-03-19 00:10:02 +00:00
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):
2011-03-19 00:10:02 +00:00
while True:
key = self.EvKey().receive(self)
2011-03-19 00:10:02 +00:00
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()
2011-03-19 00:10:02 +00:00
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):
2011-03-19 00:10:02 +00:00
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()
2011-03-19 00:10:02 +00:00
def Put(self, ch, ascr, xzOffset = 0, yzOffset = 0):
2011-03-19 00:10:02 +00:00
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)
2011-03-19 00:10:02 +00:00
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)
2011-03-19 00:10:02 +00:00
def draw(self, ascr, client):
if self.FDrawCursor():
2011-03-19 00:10:02 +00:00
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())
2011-03-19 00:10:02 +00:00
elif self.fBlink:
self.Put(str(self.Cldg().iclient)[0], ascr, *self.XzYzScreenPic())
2011-03-19 00:10:02 +00:00
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]))
2020-08-11 13:49:07 +00:00
def StLayerName(self, layer): return layer.StName()
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)
2011-03-19 00:10:02 +00:00
class WhiteboardDraw(TokenClient):
def InitPersistent(self, owner, client):
TokenClient.InitPersistent(self, owner, client, "drawable", "solid", "cursor")
self.color = 0
self.fBlink = False
2011-03-19 00:10:02 +00:00
def draw(self, ascr, client):
if client == self.client:
fDrawObjects = True
2011-03-19 00:10:02 +00:00
if self.owner.miLayers.Value("Show One"):
ascr.PutAscr(self.owner.GetLayer().ascr)
fDrawObjects = self.owner.GetLayer() == self.owner.LayerObj()
2011-03-19 00:10:02 +00:00
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) # |
2011-03-19 00:10:02 +00:00
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)
2011-04-12 15:37:52 +00:00
2011-03-19 00:10:02 +00:00
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)
2011-04-12 15:37:52 +00:00
2011-03-19 00:10:02 +00:00
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)
2011-09-14 22:30:44 +00:00
def SwitchToLobby(self, lobbyNew):
2011-09-16 12:57:31 +00:00
lobbyOld = self.lobbyCurrent
2011-09-14 22:30:44 +00:00
self.lobbyCurrent = lobbyNew
2011-09-16 12:57:31 +00:00
lobbyOld.die()
2011-09-14 22:30:44 +00:00
class LobbyI(TokenClient):
def InitPersistent(self, owner, client, objSelected = None, *rgmiListTop):
2011-09-14 22:30:44 +00:00
TokenClient.InitPersistent(self, owner, client)
2011-09-16 12:57:31 +00:00
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()
2011-03-19 00:10:02 +00:00
OvMenu(self, client, MiMenuHoriz([self.miDrawings, self.miCmds], [65, 35]), OvMenu.FILL)
2011-09-14 22:30:44 +00:00
def RgObj(self):
"Return the list of objects we are selecting from"
raise "not implemented"
2011-09-14 22:30:44 +00:00
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"
2011-09-14 22:30:44 +00:00
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):
2011-10-09 23:28:10 +00:00
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]
2011-09-14 22:30:44 +00:00
class LobbyBoard(LobbyI):
def InitPersistent(self, owner, client, project, drawing = None):
2011-09-14 22:30:44 +00:00
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)])
2011-09-14 22:30:44 +00:00
def RgObj(self):
return self.project.rgdrawing
2011-09-14 22:30:44 +00:00
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):
2011-09-16 12:57:31 +00:00
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):
2011-09-16 12:57:31 +00:00
if board != None:
return board.RgstMetadata()
return self.project.RgstMetadata()
2011-09-16 12:57:31 +00:00
def Back(self):
self.owner.SwitchToLobby(LobbyProject(self.owner, self.client, self.project))
2011-09-16 12:57:31 +00:00
2011-09-14 22:30:44 +00:00
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)])
2011-09-14 22:30:44 +00:00
def RgObj(self):
return self.game.rgproject
2011-09-14 22:30:44 +00:00
def OnObjSelected(self, project):
self.owner.SwitchToLobby(LobbyBoard(self.owner, self.client, project))
2011-09-14 22:30:44 +00:00
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()]
2011-09-14 22:30:44 +00:00
2011-03-19 00:10:02 +00:00
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
2011-03-19 00:10:02 +00:00
while True:
drawing, game = client.joinGame(self.gameLobby)
client.Cldg(self.gameLobby).drawingSelected = drawing
client.joinGame(game)
2011-03-19 00:10:02 +00:00
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)
2011-03-19 00:10:02 +00:00
class GameLobby(Game):
def InitPersistent(self):
2011-09-14 22:30:44 +00:00
self.rgproject = []
2011-03-19 00:10:02 +00:00
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 = {}
2011-03-19 00:10:02 +00:00
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):
2020-08-02 18:28:37 +00:00
cclient = 0
if drawing in self.mpdrawing_game:
2020-08-02 18:28:37 +00:00
cclient += len(self.mpdrawing_game[drawing].RgclientConnected())
for gameWorld in self.RggameWorld(drawing):
cclient += len(gameWorld.RgclientConnected())
return cclient
2011-03-19 00:10:02 +00:00
2011-09-14 22:30:44 +00:00
def StNameProject(self, project):
return project.stName + " [" + str(sum([self.CclientDrawing(drawing) for drawing in project.rgdrawing])) + "]"
2011-03-19 00:10:02 +00:00
def StNameDrawing(self, drawing):
return drawing.stName + " [" + str(self.CclientDrawing(drawing)) + "]"
2011-09-16 12:57:31 +00:00
def NewProject(self, stName, user):
proj = Project(self, stName, user)
self.rgproject.insert(0, proj)
return proj
def DrawingTouched(self, drawing):
project = drawing.owner
2011-09-16 12:57:31 +00:00
if project != self.rgproject[0]:
self.rgproject.remove(project)
self.rgproject.insert(0, project)
2011-03-19 00:10:02 +00:00
#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
2011-09-14 22:30:44 +00:00
if versionOld < 6:
project = Project(self, "Drawings", "Willy Marmot")
for board in self.rgboard:
board.TransferToOwner(project)
project.rgdrawing.append(board)
2011-09-14 22:30:44 +00:00
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)
2011-03-19 00:10:02 +00:00
class GameWB(Game):
def InitPersistent(self, gameLobby, board):
self.gameLobby = gameLobby
self.drawing = board
2011-03-19 00:10:02 +00:00
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)
2011-03-19 00:10:02 +00:00
def Chat(self):
return self.drawing.chat
def ClsCursor(self):
return Cursor
2011-03-19 00:10:02 +00:00
def GetRgclsTokTrans(self):
return [MsgScroller, Joiner, [AutoJoiner, self.ClsCursor()], [AliveWithPlayers, CursorBlinker], AutoKiller]
2011-03-19 00:10:02 +00:00
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]]]
2011-03-19 00:10:02 +00:00
class GameSprEdit(GameWB):
def ClsCursor(self):
return CursorSprite
2011-03-19 00:10:02 +00:00
if __name__ == "__main__":
if len(sys.argv) > 1:
config.override(sys.argv[1])
Run(RunnerWB, config.ODBPATH)