WIP robot creation in board editor!

- refactor client - now has a _stack_ of current Games, the top of which
  is active - prevents shutdown of whiteboard when "leaving" to edit
  source code
- add tpers.StaticIndex / inspector.py for interactively querying object
  graphs
This commit is contained in:
Jeremy Penner 2020-07-19 21:31:54 -04:00
parent f7c33a23a6
commit 10edbacf86
7 changed files with 353 additions and 90 deletions

View file

@ -73,7 +73,7 @@ class Terminal(Token):
self.fBlink = fBlink self.fBlink = fBlink
def getKey(self): def getKey(self):
return self.client.evKey.receive(self) return self.client.EvKey(self.game).receive(self)
def getPrintableKey(self): def getPrintableKey(self):
while True: while True:
@ -199,9 +199,14 @@ class Terminal(Token):
class LeaveJoinToken(Token): class LeaveJoinToken(Token):
def run(self): def run(self):
evLeave = self.game.evSuspend if self.FOnActivate() else self.game.evLeave
evJoin = self.game.evActivate if self.FOnActivate() else self.game.evJoin
while True: while True:
Event.selectDg(self, (self.game.evLeave, self.OnLeave), (self.game.evJoin, self.OnJoin)) Event.selectDg(self, (evLeave, self.OnLeave), (evJoin, self.OnJoin))
def FOnActivate(self):
return False
class AutoJoiner(LeaveJoinToken): class AutoJoiner(LeaveJoinToken):
def InitPersistent(self, owner, *rgclsTok): def InitPersistent(self, owner, *rgclsTok):
Token.InitPersistent(self, owner) Token.InitPersistent(self, owner)
@ -234,6 +239,9 @@ class AliveWithPlayers(LeaveJoinToken):
Token.InitTransient(self) Token.InitTransient(self)
self.rgclient = [] self.rgclient = []
def FOnActivate(self):
return True
def OnLeave(self, client): def OnLeave(self, client):
self.rgclient.remove(client) self.rgclient.remove(client)
if len(self.rgclient) == 0: if len(self.rgclient) == 0:
@ -705,9 +713,18 @@ class MiMenuHoriz(MiMenuI):
mi.DrawWithParent(selor, ascr, x, y, wT, miSelected == mi) mi.DrawWithParent(selor, ascr, x, y, wT, miSelected == mi)
x = x + wT x = x + wT
def DgProjectMiButton(dgExec): class ProjectedValue(TPrs):
def InitPersistent(self, value, dgStName):
self.value = value
self.dgStName = dgStName
def StName(self):
return self.dgStName(self.value)
def Value(self):
return self.value
def DgProjectMiButton(dgExec, dgStName = None):
def DgProject(value): def DgProject(value):
return MiButton(None, lambda: dgExec(value), value) return MiButton(None, lambda: dgExec(value), ProjectedValue(value, dgStName) if dgStName else value)
return DgProject return DgProject
class RgmiProjected(TPrs): class RgmiProjected(TPrs):
@ -781,13 +798,15 @@ class OvStatic(TokenClient):
def Value(self, stName = None): def Value(self, stName = None):
return self.miMenu.Value(stName) return self.miMenu.Value(stName)
class OvMenu(OvStatic): class OvMenu(OvStatic):
def run(self): def run(self):
while True: while True:
self.miMenu.HandleKey(self.client.evKey.receive(self)) self.miMenu.HandleKey(self.client.EvKey(self.game).receive(self))
def draw(self, ascr, client): def draw(self, ascr, client):
self.drawI(ascr, client, True) self.drawI(ascr, client, True)
class OvPopup(OvStatic): class OvPopup(OvStatic):
def InitPersistent(self, owner, client, miMenu): def InitPersistent(self, owner, client, miMenu):
OvStatic.InitPersistent(self, owner, client, miMenu, self.MIDDLE, "drawable", "menu") OvStatic.InitPersistent(self, owner, client, miMenu, self.MIDDLE, "drawable", "menu")
@ -800,7 +819,7 @@ class OvPopup(OvStatic):
self.evShow.receive(self) self.evShow.receive(self)
self.fAwake = True self.fAwake = True
while self.fAwake: while self.fAwake:
key = self.client.evKey.receive(self) key = self.client.EvKey(self.game).receive(self)
if not self.miMenu.HandleKey(key): if not self.miMenu.HandleKey(key):
if key == ansi.K_TAB or key == ansi.K_DEL or key == ' ' or ansi.FEnter(key): if key == ansi.K_TAB or key == ansi.K_DEL or key == ' ' or ansi.FEnter(key):
self.fAwake = False self.fAwake = False

112
engine.py
View file

@ -184,7 +184,7 @@ class Ownable(TPrs):
tok.die() tok.die()
self.DontPersist() self.DontPersist()
@Version(2) @Version(3)
class Game(Ownable): class Game(Ownable):
def InitPersistent(self): def InitPersistent(self):
Ownable.InitPersistent(self) Ownable.InitPersistent(self)
@ -194,12 +194,16 @@ class Game(Ownable):
self.rgdgdie = [] self.rgdgdie = []
self.evJoin = Event(self) self.evJoin = Event(self)
self.evLeave = Event(self) self.evLeave = Event(self)
self.evActivate = Event(self)
self.evSuspend = Event(self)
self.mpuser_cldg = {} self.mpuser_cldg = {}
self.CreateRgtok(self.GetRgclsTokPers(), True) self.CreateRgtok(self.GetRgclsTokPers(), True)
def InitTransient(self): def InitTransient(self):
self.MakeTransient("fScheduled", False) self.MakeTransient("fScheduled", False)
self.rgclient = [] self.rgclientActive = []
self.rgclientSuspended = []
self.rgclientLeaving = []
self.rgproc = [PTimer(), PEvent(), PDraw(*self.GetRgtagDraw())] self.rgproc = [PTimer(), PEvent(), PDraw(*self.GetRgtagDraw())]
for proc in self.rgproc: for proc in self.rgproc:
proc.setGame(self) proc.setGame(self)
@ -240,6 +244,9 @@ class Game(Ownable):
if not fPersist: if not fPersist:
tok.DontPersist() tok.DontPersist()
def RgclientConnected(self):
return self.rgclientActive + self.rgclientSuspended
def tagToken(self, token, tag): def tagToken(self, token, tag):
if not (tag in self.mptagTokenset): if not (tag in self.mptagTokenset):
self.mptagTokenset[tag] = set([token]) self.mptagTokenset[tag] = set([token])
@ -268,10 +275,12 @@ class Game(Ownable):
return rgtoken return rgtoken
def quit(self): def quit(self):
assert len(self.rgclientSuspended) == 0
self.running = False self.running = False
self.fQuit = True self.fQuit = True
def finish(self, rc = None): def finish(self, rc = None):
assert len(self.rgclientSuspended) == 0
self.running = False self.running = False
self.rc = rc self.rc = rc
class QuitException(Exception): class QuitException(Exception):
@ -291,22 +300,44 @@ class Game(Ownable):
proc.process() proc.process()
if not self.running: if not self.running:
self.die() self.die()
for client in self.rgclient: for client in self.rgclientActive:
client.leaveGame(self.rc) if client not in self.rgclientLeaving:
for client in self.rgclient: client.leaveGame(self.rc)
deadClients = list(self.rgclientLeaving)
del self.rgclientLeaving[:]
for client in deadClients:
if client in self.rgclientActive:
self.rgclientActive.remove(client)
if client in self.rgclientSuspended:
self.rgclientSuspended.remove(client)
client.postRunStep() client.postRunStep()
finally: finally:
self.fScheduled = False self.fScheduled = False
return self.running return self.running
def joinGame(self,client): def joinGame(self,client):
self.rgclient.append(client) self.rgclientActive.append(client)
self.evJoin.fire(client) self.evJoin.fire(client)
self.evActivate.fire(client)
def leaveGame(self, client): def leaveGame(self, client):
self.evLeave.fire(client) if self.running:
if client in self.rgclientActive:
self.evSuspend.fire(client)
self.evLeave.fire(client)
self.rgclientLeaving.append(client)
def reactivateClient(self, client):
assert client in self.rgclientSuspended
self.rgclientSuspended.remove(client)
self.rgclientActive.append(client)
self.evActivate.fire(client)
def suspendClient(self, client):
assert client in self.rgclientActive
self.rgclientActive.remove(client)
self.rgclientSuspended.append(client)
self.evSuspend.fire(client)
def leaveGameI(self, client):
self.rgclient.remove(client)
def ensureRun(self): def ensureRun(self):
self.running = True self.running = True
if not self.fScheduled: if not self.fScheduled:
@ -315,6 +346,9 @@ class Game(Ownable):
def UpgradeFrom(self, versionOld): def UpgradeFrom(self, versionOld):
if versionOld < 2: if versionOld < 2:
self.mpuser_cldg = {} self.mpuser_cldg = {}
if versionOld < 3:
self.evActivate = Event(self)
self.evSuspend = Event(self)
# Client data # Client data
@Version(2) @Version(2)
@ -336,9 +370,9 @@ class Client(TPrs):
def InitTransient(self): def InitTransient(self):
self.chRun = stackless.channel() self.chRun = stackless.channel()
self.gameCurr = None self.gameCurr = None
self.mpgame_evKey = {}
self.rggameSuspended = []
self.chCont = None self.chCont = None
self.fRunning = False
self.dgJoinGame = None
self.rgdgQuit = [] self.rgdgQuit = []
self.fQuit = False self.fQuit = False
self.cld = None self.cld = None
@ -353,19 +387,33 @@ class Client(TPrs):
if self._runTskRun(): if self._runTskRun():
self.gameCurr.ensureRun() self.gameCurr.ensureRun()
def joinGame(self, game): def joinGame(self, game):
assert not self.fRunning, "already running" if not self.gameCurr:
chCont = stackless.channel() chCont = stackless.channel()
self.chRun.send((game, chCont)) self.chRun.send((game, chCont))
return chCont.receive() return chCont.receive()
else:
self.gameCurr.suspendClient(self)
self.rggameSuspended.append(self.gameCurr)
self.gameCurr = game
self.gameCurr.joinGame(self)
def leaveGame(self, rc = None): def leaveGame(self, rc = None):
self.fRunning = False
self.gameCurr.leaveGame(self) self.gameCurr.leaveGame(self)
self.rc = rc if self.gameCurr in self.mpgame_evKey:
del self.mpgame_evKey[self.gameCurr]
if len(self.rggameSuspended) == 0:
self.gameCurr = None
self.rc = rc
else:
self.gameCurr = self.rggameSuspended.pop()
self.gameCurr.reactivateClient(self)
def quit(self): def quit(self):
self.leaveGame(None) while self.gameCurr:
self.leaveGame(None)
self.fQuit = True self.fQuit = True
def _runTskRun(self): def _runTskRun(self):
"run self.tskRun after it has been scheduled, and await results" "run self.tskRun after it has been scheduled, and await results"
stackless.run() stackless.run()
@ -373,20 +421,17 @@ class Client(TPrs):
return False return False
self.gameCurr, self.chCont = self.chRun.receive() self.gameCurr, self.chCont = self.chRun.receive()
self.gameCurr.joinGame(self) self.gameCurr.joinGame(self)
self.fRunning = True
if self.dgJoinGame:
self.dgJoinGame(self, self.gameCurr)
return True return True
def postRunStep(self): def postRunStep(self):
if not self.fRunning: if not self.gameCurr:
self.gameCurr.leaveGameI(self)
if not self.fQuit: if not self.fQuit:
self.chCont.send(self.rc) self.chCont.send(self.rc)
self.fQuit = not self._runTskRun() self.fQuit = not self._runTskRun()
if self.fQuit: if self.fQuit:
for dgQuit in self.rgdgQuit: for dgQuit in self.rgdgQuit:
dgQuit() dgQuit()
del self.rgdgQuit[:]
return self.fQuit return self.fQuit
def beep(self): def beep(self):
pass pass
@ -401,9 +446,18 @@ class Client(TPrs):
def removeDgQuit(self, dgquit): def removeDgQuit(self, dgquit):
self.rgdgQuit.remove(dgquit) self.rgdgQuit.remove(dgquit)
def Cldg(self, game = None):
if game == None: def EvKey(self, game):
game = self.gameCurr if game:
if game not in self.mpgame_evKey:
self.mpgame_evKey[game] = Event(game)
return self.mpgame_evKey[game]
def KeyPressed(self, ch):
if self.gameCurr:
self.EvKey(self.gameCurr).fire(ch)
def Cldg(self, game):
return game.GetCldg(self.cld.user) return game.GetCldg(self.cld.user)
class Processor(TPrs): class Processor(TPrs):
@ -463,7 +517,7 @@ class PDraw(Processor):
def process(self): def process(self):
if self.game.ms >= self.msDrawAgain: if self.game.ms >= self.msDrawAgain:
for client in self.game.rgclient: for client in self.game.rgclientActive:
ascr = ansi.Ascr() ascr = ansi.Ascr()
for tok in self.game.rgtoken("drawable", self.rgprio): for tok in self.game.rgtoken("drawable", self.rgprio):
try: try:
@ -552,8 +606,8 @@ class Token(Taggable):
def runI(self): def runI(self):
try: try:
self.run() self.run()
#except greenlet.GreenletExit: except greenlet.GreenletExit:
# raise raise
except Exception: except Exception:
print "token script crashed:" print "token script crashed:"
traceback.print_exc() traceback.print_exc()

7
inspector.py Normal file
View file

@ -0,0 +1,7 @@
# to use:
# python -i inspector.py
# >>> i.Rgclass() ...
from whiteboard import *
o = tpers.Odb.Load('whiteboard.marm')
i = tpers.StaticIndex().Populate(o)

View file

@ -28,9 +28,10 @@ def RegStmt(clsStmt):
class Botdef(TPrs): class Botdef(TPrs):
def InitPersistent(self, stName, ach, x, y): def InitPersistent(self, stName, ach, x, y):
self.stName = stName self.stName = stName
self.ast = ast self.ach = ach
self.x = x self.x = x
self.y = y self.y = y
self.fPlayer = False
self.syntOnTouch = SyntBlock() self.syntOnTouch = SyntBlock()
class Flagdef(TPrs): class Flagdef(TPrs):
@ -62,6 +63,9 @@ class Vm(TPrs):
def FFlagSet(self, flagdef): def FFlagSet(self, flagdef):
return self.mpflagdef[flagdef] return self.mpflagdef[flagdef]
def Fail(self, stMessage, synt):
self.Log(Fail(stMessage, synt))
def Log(self, fail): def Log(self, fail):
print(fail.stMessage, fail.synt) print(fail.stMessage, fail.synt)
@ -155,7 +159,7 @@ class Synt(Typeable):
pass pass
def Eval(self, vm): def Eval(self, vm):
"Execute yourself, in the context of vm." "Execute yourself, in the context of vm."
return Fail("Missing information", self) return vm.Fail("Missing information", self)
def StypeForChild(self, syntChild): def StypeForChild(self, syntChild):
"Return the stype that should be applied to the given child." "Return the stype that should be applied to the given child."
@ -361,7 +365,7 @@ class SyntFlagRef(Synt):
try: try:
return vm.FFlagSet(self.flagdef) return vm.FFlagSet(self.flagdef)
except: except:
return Fail("No such flag", self) return vm.Fail("No such flag", self)
class SyntIsFlagSet(SyntDesc): class SyntIsFlagSet(SyntDesc):
desc = [["set"]] desc = [["set"]]
@ -422,10 +426,9 @@ class Fail(TPrs):
edit a script while the game is running and see the effect of their changes immediately; this suggests that failures edit a script while the game is running and see the effect of their changes immediately; this suggests that failures
should be logged and the script should continue to run. should be logged and the script should continue to run.
""" """
def InitPersistent(self, vm, stFailure, synt): def InitPersistent(self, stFailure, synt):
self.stFailure = stFailure self.stFailure = stFailure
self.synt = synt self.synt = synt
vm.Log(self)
# projection cursor # projection cursor
class Pcur(object): class Pcur(object):
@ -880,7 +883,7 @@ class Pov(TokenClient):
def run(self): def run(self):
while True: while True:
key = self.client.evKey.receive(self) key = self.client.EvKey(self.game).receive(self)
if key == ansi.K_PGDN: if key == ansi.K_PGDN:
self.block.Eval(Vm(self.defs)) self.block.Eval(Vm(self.defs))
psel = self.pselstate.PselByClient(self.client) psel = self.pselstate.PselByClient(self.client)

View file

@ -41,8 +41,6 @@ class AnsiTelnet(protocol.Protocol):
print "created" print "created"
self.ast = None self.ast = None
self.client = client self.client = client
self.evKey = None
self.evArrow = None
self.Refresh() self.Refresh()
def connectionMade(self): def connectionMade(self):
@ -122,27 +120,21 @@ class AnsiTelnet(protocol.Protocol):
print "negotiated!" print "negotiated!"
self.transport.write(ansi.esc + "2J") #cls self.transport.write(ansi.esc + "2J") #cls
self.transport.write(ansi.esc + "=25l") #hide the cursor self.transport.write(ansi.esc + "=25l") #hide the cursor
self.client.dgJoinGame = self.CreateEvents
self.client.addDgQuit(self.transport.loseConnection) self.client.addDgQuit(self.transport.loseConnection)
self.client.go(self.factory.fnRun, self) self.client.go(self.factory.fnRun, self)
def RcvText(self, text): def RcvText(self, text):
if self.client.evKey: for ch in text:
for ch in text: self.client.KeyPressed(ch)
self.client.evKey.fire(ch)
mpcode_key = {"A": ansi.K_UP, "B": ansi.K_DOWN, "C": ansi.K_RIGHT, "D": ansi.K_LEFT, mpcode_key = {"A": ansi.K_UP, "B": ansi.K_DOWN, "C": ansi.K_RIGHT, "D": ansi.K_LEFT,
"K": ansi.K_END, "H": ansi.K_HOME, "V": ansi.K_PGUP, "U": ansi.K_PGDN } "K": ansi.K_END, "H": ansi.K_HOME, "V": ansi.K_PGUP, "U": ansi.K_PGDN }
def RcvAnsi(self, code, rgarg): def RcvAnsi(self, code, rgarg):
if self.client.evKey: if code in self.mpcode_key:
if code in self.mpcode_key: self.client.KeyPressed(self.mpcode_key[code])
self.client.evKey.fire(self.mpcode_key[code]) else:
else: print "unrecognized code", code, rgarg
print "unrecognized code", code, rgarg
def CreateEvents(self, client, game):
client.evKey = engine.Event(game)
def Draw(self, ascr): def Draw(self, ascr):
self.transport.write(self.ascr.AstDiff(ascr)) self.transport.write(self.ascr.AstDiff(ascr))

View file

@ -319,3 +319,61 @@ def _unpickle_method(func_name, obj, cls):
import copy_reg import copy_reg
import types import types
copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method) copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)
def hashable(v):
try:
hash(v)
return True
except:
return False
class StaticIndex(TPrs):
def InitPersistent(self, fIncludeTemp = False):
self.mpv_a_rge = {}
self.rgv = set()
self.fIncludeTemp = fIncludeTemp
def FPersist(self):
return False
def AddEAV(self, e, a, v):
if v not in self.mpv_a_rge:
self.mpv_a_rge[v] = {}
a_rge = self.mpv_a_rge[v]
if a not in a_rge:
a_rge[a] = []
a_rge[a].append(e)
def Populate(self, v, a = (), e = None):
if hashable(v):
if e:
self.AddEAV(e, a, v)
if v in self.rgv:
return self
self.rgv.add(v)
if isinstance(v, TPrs):
attrs = set(v._persistent.keys())
if self.fIncludeTemp:
attrs = attrs.union(set(v.__dict__.keys()))
for attr in attrs:
child = getattr(v, attr)
self.Populate(child, (attr,), v)
elif isinstance(v, clsList) or isinstance(v, clsTuple):
for ichild, child in enumerate(v):
self.Populate(child, a + (ichild,), e)
elif isinstance(v, clsSet):
for child in v:
self.Populate(child, a, e)
elif isinstance(v, clsDict):
for k, child in v.items():
self.Populate(child, a + (k,), e)
return self
def Rgclass(self):
return set([v.__class__ for v in self.rgv])
def RgvWithClass(self, cls):
return [v for v in self.rgv if isinstance(v, cls)]
def Rgref(self, v):
return self.mpv_a_rge.get(v)

View file

@ -20,6 +20,7 @@ from basetoken import *
from tpers import * from tpers import *
from datetime import * from datetime import *
from util import * from util import *
from scripting import Botdef, Flagdef, Defs, PselState, Pov
import telnet import telnet
import time import time
import login import login
@ -129,8 +130,13 @@ class Drawing(Ownable):
def SetTimestamp(self): def SetTimestamp(self):
self.dtLastModified = datetime.now() self.dtLastModified = datetime.now()
@Version(3) @Version(4)
class Board(Drawing): 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): def StType(self):
return "Board" return "Board"
def W(self): return config.W def W(self): return config.W
@ -139,6 +145,10 @@ class Board(Drawing):
def draw(self, ascr): def draw(self, ascr):
for layer in reversed(self.rglayer): for layer in reversed(self.rglayer):
ascr.PutAscr(layer.ascr) 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): def Save(self):
fn = config.DIR_ANSI + "/" + "_".join([str(x) for x in time.localtime()]) fn = config.DIR_ANSI + "/" + "_".join([str(x) for x in time.localtime()])
@ -158,6 +168,9 @@ class Board(Drawing):
self.user = gameWB.user self.user = gameWB.user
if versionOld < 3: if versionOld < 3:
self.chat = Chat() self.chat = Chat()
if versionOld < 4:
self.defs = Defs()
self.layerObj = self.rglayer[0]
class Sprite(Drawing): class Sprite(Drawing):
def InitPersistent(self, owner, stName, user, w, h): def InitPersistent(self, owner, stName, user, w, h):
@ -225,19 +238,22 @@ class Chat(TPrs):
def InitTransient(self): def InitTransient(self):
TPrs.InitTransient(self) TPrs.InitTransient(self)
self.cchl = 100 self.cchl = 100
self.DetachFromGame() self.MakeTransient("game", None)
self.MakeTransient("evChat", None)
def InitPersistent(self): def InitPersistent(self):
TPrs.InitPersistent(self) TPrs.InitPersistent(self)
self.rgchl = [] self.rgchl = []
def AttachToGame(self, game): def AttachToGame(self, game):
self.game = game with self.SetTransiently():
self.evChat = Event(game) self.game = game
self.evChat = Event(game)
def DetachFromGame(self): def DetachFromGame(self):
self.game = None with self.SetTransiently():
self.evChat = None self.game = None
self.evChat = None
def Add(self, chl): def Add(self, chl):
if len(chl.st) > 0: if len(chl.st) > 0:
@ -248,8 +264,10 @@ class Chat(TPrs):
def CChl(self): def CChl(self):
return len(self.rgchl) return len(self.rgchl)
def CUsers(self): def CUsers(self):
return len(self.game.rgclient) return len(self.game.RgclientConnected())
def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0): def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0):
yDraw = y + h - 1 yDraw = y + h - 1
for chl in self.rgchl[ichl:]: for chl in self.rgchl[ichl:]:
@ -274,8 +292,8 @@ class Chat(TPrs):
def DrawUsers(self, ascr, x, y, w, h, colBg=ansi.BLACK, iuser=0): def DrawUsers(self, ascr, x, y, w, h, colBg=ansi.BLACK, iuser=0):
yDraw = y yDraw = y
for client in sorted(self.game.rgclient, lambda c1, c2: cmp(c1.Cldg().iclient, c2.Cldg().iclient))[iuser:]: 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().iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg) ascr.PutSt("{0:{1}}".format(str(client.Cldg(self.game).iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw + 1 yDraw = yDraw + 1
if yDraw == y + h: if yDraw == y + h:
break break
@ -320,19 +338,38 @@ class Cursor(TokenClient):
miQuit = MiButton("Back to Menu", self.client.leaveGame) miQuit = MiButton("Back to Menu", self.client.leaveGame)
self.cf = self.MenuDrawing(miFg, miBg, miChars, miQuit) 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.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.rgmiLayer = RgmiProjected(self.game.drawing.rglayer, DgProjectMiButton(self.SelectLayer, self.StLayerName))
self.miLayers = self.MenuLayers(miQuit) self.miLayers = self.MenuLayers(miQuit)
self.ovPopup = OvPopup(self, self.client, MiTab.MiTabbed(["Drawing", self.cf], [self.StLayerTab(), self.miLayers], ["Chat", self.miChat])) self.ovPopup = OvPopup(self, self.client, MiTab.MiTabbed(["Drawing", self.cf], [self.StLayerTab(), self.miLayers], ["Chat", self.miChat]))
miStatus = MiMenuHoriz([MiStatic("Hit <Tab> for menu"), MiValue(miChars)]) miStatus = MiMenuHoriz([MiDgText(self.StStatus), MiValue(miChars)])
miStatus.ListenForNotice(self.miChat) miStatus.ListenForNotice(self.miChat)
self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM) 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.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y()) != 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): def MenuDrawing(self, miFg, miBg, miChars, miQuit):
return MiMenu([None, miFg, miBg, None, miChars, None, MiButton("Save", self.Save), None, miQuit, None]) return MiMenu([None, miFg, miBg, None, miChars, None, MiButton("Save", self.Save), None, miQuit, None])
def MenuLayers(self, miQuit): 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), 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])) 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 StLayerTab(self): return "Layers"
def Save(self): def Save(self):
self.game.drawing.Save() self.game.drawing.Save()
@ -345,7 +382,8 @@ class Cursor(TokenClient):
return True return True
def GetLayer(self): def GetLayer(self):
return self.ovPopup.GetSelection("layer").Value() selLayer = self.ovPopup.GetSelection("layer")
return selLayer.Value().Value() if selLayer else None
def NewLayer(self): def NewLayer(self):
ilayer = 1 ilayer = 1
@ -373,6 +411,9 @@ class Cursor(TokenClient):
self.GetLayer().SetName(st) self.GetLayer().SetName(st)
return True return True
def SetBotLayer(self):
self.game.drawing.layerObj = self.GetLayer()
def blinkCursor(self, fBlink): def blinkCursor(self, fBlink):
self.fBlink = fBlink self.fBlink = fBlink
if not fBlink and self.cDontBlink > 0: if not fBlink and self.cDontBlink > 0:
@ -391,7 +432,7 @@ class Cursor(TokenClient):
def run(self): def run(self):
with self.game.drawing.chat.evChat.oob(self, self.OnChat): with self.game.drawing.chat.evChat.oob(self, self.OnChat):
while True: while True:
key = self.client.evKey.receive(self) key = self.client.EvKey(self.game).receive(self)
fDontBlink = True fDontBlink = True
if key == ansi.K_LEFT: if key == ansi.K_LEFT:
self.pos.Left() self.pos.Left()
@ -419,6 +460,8 @@ class Cursor(TokenClient):
self.Skip(self.pos.Up) self.Skip(self.pos.Up)
elif key == ansi.K_PGDN: elif key == ansi.K_PGDN:
self.Skip(self.pos.Down) self.Skip(self.pos.Down)
elif key == ansi.K_NEWLINE or key == ansi.K_RETURN:
self.EditBot()
elif ansi.FKeyPrintable(key): elif ansi.FKeyPrintable(key):
if key.isdigit(): if key.isdigit():
self.PutWb(chr(self.cf.Value("Characters")[IchFromCh(key)])) self.PutWb(chr(self.cf.Value("Characters")[IchFromCh(key)]))
@ -466,6 +509,41 @@ class Cursor(TokenClient):
def FDrawCursor(self): def FDrawCursor(self):
return self.fBlink or self.cDontBlink > 0 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): def draw(self, ascr, client):
if self.FDrawCursor(): if self.FDrawCursor():
if client == self.client: if client == self.client:
@ -475,7 +553,7 @@ class Cursor(TokenClient):
chCursor = chr(177) chCursor = chr(177)
self.Put(chCursor, ascr, *self.XzYzScreenPic()) self.Put(chCursor, ascr, *self.XzYzScreenPic())
elif self.fBlink: elif self.fBlink:
self.Put(str(self.client.Cldg().iclient)[0], ascr, *self.XzYzScreenPic()) self.Put(str(self.client.Cldg(self.game).iclient)[0], ascr, *self.XzYzScreenPic())
class CursorSprite(Cursor): class CursorSprite(Cursor):
def InitPersistent(self, game, client): def InitPersistent(self, game, client):
@ -492,7 +570,9 @@ class CursorSprite(Cursor):
return MiMenu(Rgseq([None], self.rgmiLayer, [None, MiButton("New Frame", self.NewLayer), MiButton("Move up", self.LayerUp), MiButton("Move down", self.LayerDown), 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])) 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 StLayerTab(self): return "Frames"
def NewLayer(self): def NewLayer(self):
stLayer = "Frame " + str(len(self.game.drawing.rglayer) + 1) stLayer = "Frame " + str(len(self.game.drawing.rglayer) + 1)
layer = self.GetLayer() layer = self.GetLayer()
@ -521,10 +601,14 @@ class WhiteboardDraw(TokenClient):
def draw(self, ascr, client): def draw(self, ascr, client):
if client == self.client: if client == self.client:
fDrawObjects = True
if self.owner.miLayers.Value("Show One"): if self.owner.miLayers.Value("Show One"):
ascr.PutAscr(self.owner.GetLayer().ascr) ascr.PutAscr(self.owner.GetLayer().ascr)
fDrawObjects = self.owner.GetLayer() == self.owner.layerObj
else: else:
self.game.drawing.draw(ascr) self.game.drawing.draw(ascr)
if fDrawObjects:
self.game.drawing.DrawObjectDefs(ascr)
class SpriteDraw(TokenClient): class SpriteDraw(TokenClient):
def InitPersistent(self, owner, client): def InitPersistent(self, owner, client):
@ -671,10 +755,10 @@ class Joiner(LeaveJoinToken):
self.board = self.game.drawing self.board = self.game.drawing
def OnJoin(self, client): def OnJoin(self, client):
with client.Cldg().SetTransiently() as cldg: with client.Cldg(self.game).SetTransiently() as cldg:
cldg.iclient = -1 cldg.iclient = -1
iclient = 1 iclient = 1
rgiclient = [clientT.Cldg().iclient for clientT in self.game.rgclient] rgiclient = [clientT.Cldg(self.game).iclient for clientT in self.game.RgclientConnected()]
while iclient in rgiclient: while iclient in rgiclient:
iclient = iclient + 1 iclient = iclient + 1
cldg.iclient = iclient cldg.iclient = iclient
@ -813,7 +897,29 @@ class GameLogin(Game):
def GetRgclsTokTrans(self): def GetRgclsTokTrans(self):
return [[AutoJoiner, login.LoginTerm]] return [[AutoJoiner, login.LoginTerm]]
@Version(6) 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): class GameLobby(Game):
def InitPersistent(self): def InitPersistent(self):
self.rgproject = [] self.rgproject = []
@ -821,30 +927,37 @@ class GameLobby(Game):
def InitTransient(self): def InitTransient(self):
Game.InitTransient(self) Game.InitTransient(self)
self.mpdrawing_game = {} self.mpdrawing_game = Mpsubgame(self, self.MakeGameFromDrawing)
self.mpdrawingsynt_game = Mpsubgame(self, self.MakeGameSyntEdit)
def GetRgclsTokTrans(self): def GetRgclsTokTrans(self):
return [[AutoJoiner, Lobby]] return [[AutoJoiner, Lobby]]
def GameFromDrawing(self, drawing): def GameFromDrawing(self, drawing):
if not (drawing in self.mpdrawing_game): return self.mpdrawing_game.SubgameFromKey(drawing)
if drawing.StType() == "Board":
game = GameWB(self, drawing)
elif drawing.StType() == "Sprite":
game = GameSprEdit(self, drawing)
else:
assert False, "Lobby not prepared to deal with drawing of type " + drawing.StType()
game.rgdgdie.append(Partial(self.KillBoard, drawing))
self.mpdrawing_game[drawing] = game
return game
return self.mpdrawing_game[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): def KillBoard(self, drawing):
del self.mpdrawing_game[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): def CclientDrawing(self, drawing):
if drawing in self.mpdrawing_game: if drawing in self.mpdrawing_game:
return len(self.mpdrawing_game[drawing].rgclient) return len(self.mpdrawing_game[drawing].RgclientConnected())
return 0 return 0
def StNameProject(self, project): def StNameProject(self, project):
@ -883,7 +996,12 @@ class GameLobby(Game):
project.rgdrawing.append(board) project.rgdrawing.append(board)
del self.rgboard del self.rgboard
self.rgproject = [project] self.rgproject = [project]
self.mpuser_cldg = {} # ugh, upgrading parent classes is broken in our model :(
# 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): class GameWB(Game):
def InitPersistent(self, gameLobby, board): def InitPersistent(self, gameLobby, board):
@ -905,6 +1023,18 @@ class GameWB(Game):
def GetRgtagDraw(self): def GetRgtagDraw(self):
return ["background", "solid", "cursor", "menu", "msgscroller"] 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): class GameSprEdit(GameWB):
def ClsCursor(self): def ClsCursor(self):