711 lines
26 KiB
Python
711 lines
26 KiB
Python
|
from engine import *
|
||
|
from basetoken import *
|
||
|
from tpers import *
|
||
|
from util import *
|
||
|
|
||
|
# scope -- a block? a scril? (scril lets you specifically avoid vars that are defined after a certain point) + game?
|
||
|
# method: rgvar(stype) -- returns a list of variables in scope that match the current stype
|
||
|
# var -- a variable
|
||
|
# contains: stype, sargInitializer
|
||
|
# a var IS A SCRIL
|
||
|
# the scmd creates a new vari in the current ectx, and adds the scril to a map of things that refer to it
|
||
|
# sproj -- an object that can project a sarg into an pw
|
||
|
# stypes should be sprojectors and express a default projection as a class method (why?)
|
||
|
# MiSarg -- an editor for sargs
|
||
|
# MiSargNum, MiSargDrop
|
||
|
# MiScriptLine -- projects a scriptline
|
||
|
# sobj -- generic hungarian for any object that is usable in scripts
|
||
|
# fail -- a FAILURE OBJECT; if it is used as a sarg when evaluating an scmd, a failure is logged and the scmd returns fail.
|
||
|
|
||
|
# execution:
|
||
|
# ectx -- execution context
|
||
|
# contains: mpvar_sobj
|
||
|
# block
|
||
|
# iscril (selection?)
|
||
|
# ectxReturn
|
||
|
# methods: call(block)
|
||
|
|
||
|
class Block(TPrs):
|
||
|
def InitPersistent(self, scrilParent):
|
||
|
self.rgscril = []
|
||
|
self.scrilParent = scrilParent
|
||
|
def InsertScrilAfter(self, scrilAfter, scmd = None):
|
||
|
scrilNew = Scril(self, scmd)
|
||
|
if scrilAfter == None:
|
||
|
self.rgscril.insert(0, scrilNew)
|
||
|
else:
|
||
|
self.rgscril.insert(self.rgscril.index(scrilAfter) + 1, scrilNew)
|
||
|
return scrilNew
|
||
|
def Project(self, pwBlock, dxIndent = 0):
|
||
|
if pwBlock == None:
|
||
|
pwBlock = PwBlock(None, self, 0)
|
||
|
PwButtonHidden(pwBlock, "[insert new line]", None, self.InsertScrilAfter, dxIndent)
|
||
|
for scril in self.rgscril:
|
||
|
scril.Project(pwBlock, dxIndent)
|
||
|
return pwBlock
|
||
|
|
||
|
class Typeable(TPrs):
|
||
|
def InitTransient(self):
|
||
|
self.steditTypein = Stedit()
|
||
|
def GetStTypein(self):
|
||
|
st = self.steditTypein.GetValue()
|
||
|
return None if st == "" else st
|
||
|
def SetStTypein(self, stTypein):
|
||
|
self.steditTypein.SetValue(stTypein or "")
|
||
|
def HandleTypeinKey(self, key):
|
||
|
return self.steditTypein.HandleKey(key)
|
||
|
|
||
|
# Sarg -- pointer to scripting object / argument to a command
|
||
|
# this object type appears to exist solely so that MiSarg can edit something without digging through a list
|
||
|
class Sarg(Typeable):
|
||
|
def InitPersistent(self, stype, scril):
|
||
|
self.stype = stype
|
||
|
self.sobj = stype.SobjDefault(scril)
|
||
|
|
||
|
def FExpr(self):
|
||
|
"Returns true if the sobj must be evaluated to get a result."
|
||
|
return self.sobj.__class__ == Scril
|
||
|
|
||
|
def Eval(self, ectx):
|
||
|
if self.FExpr():
|
||
|
return self.sobj.Exec(ectx)
|
||
|
else:
|
||
|
return self.sobj
|
||
|
|
||
|
class Fail(TPrs):
|
||
|
def InitPersistent(self, stFailure):
|
||
|
self.stFailure == stFailure
|
||
|
|
||
|
# stype -- a description of a sobj (scripting object)
|
||
|
# method: FMember(sobj) -- returns true if an object is in the set of things that this stype represents
|
||
|
# method: FOverlaps(stype) -- returns true if there is some overlap between the two stypes
|
||
|
# -- there is no subtype/supertype relation because stypes are used to help the user,
|
||
|
# and I suspect being specific in this manner is too much of a burden to place on them.
|
||
|
# methods: rgsarg(scope) -- returns a list of all legal objects that exist in the current scope
|
||
|
# this includes functions which return things of this stype, and variables
|
||
|
# literals are not included
|
||
|
# project(cls, game, sarg) -- returns an MiSarg which displays and edits (default sproj for a sarg?)
|
||
|
class Stype(object):
|
||
|
"""A definition of a scripting object type. All methods are class methods; stypes are never instantiated."""
|
||
|
def __init__(self):
|
||
|
raise "do not instantiate stypes"
|
||
|
@classmethod
|
||
|
def FMember(cls, sobj):
|
||
|
return isinstance(sobj, cls.pythontype)
|
||
|
@classmethod
|
||
|
def FOverlaps(cls, stype):
|
||
|
return stype == self
|
||
|
@classmethod
|
||
|
def Rgsobj(cls, scope):
|
||
|
return []
|
||
|
@classmethod
|
||
|
def Rgsobj_st(cls, scope):
|
||
|
return [(sobj, str(sobj)) for sobj in cls.Rgsobj(scope)]
|
||
|
@classmethod
|
||
|
def OnSelect(cls, sarg, sobj):
|
||
|
sarg.sobj = sobj
|
||
|
@classmethod
|
||
|
def Sproj(cls, sarg, pwBlock, pwScril):
|
||
|
ProjectTypein(pwScril, sarg, str(sarg.sobj), cls.Rgsobj_st, cls.OnSelect)
|
||
|
return False
|
||
|
|
||
|
def ProjectTypein(pwParent, typeable, st, dgRgsobj_st, dgOnSelect):
|
||
|
def OnSelect(sobj):
|
||
|
dgOnSelect(typeable, sobj)
|
||
|
typeable.SetStTypein(None)
|
||
|
if typeable.GetStTypein() != None:
|
||
|
pwParent = PwDropdown(pwParent)
|
||
|
PwTypein(pwParent, st, typeable)
|
||
|
if typeable.GetStTypein() != None:
|
||
|
for sobj, st in dgRgsobj_st(typeable):
|
||
|
PwButton(pwParent, st, sobj, OnSelect)
|
||
|
|
||
|
class StypeBool(Stype):
|
||
|
pythontype = bool
|
||
|
@classmethod
|
||
|
def SobjDefault(cls, scril):
|
||
|
return False
|
||
|
@classmethod
|
||
|
def Rgsobj(cls, scope):
|
||
|
return [True, False]
|
||
|
|
||
|
class StypeBlock(Stype):
|
||
|
pythontype = Block
|
||
|
@classmethod
|
||
|
def SobjDefault(cls, scril):
|
||
|
return Block(scril)
|
||
|
@classmethod
|
||
|
def Sproj(cls, sarg, pwBlock, pwScril):
|
||
|
sarg.sobj.Project(pwBlock, pwScril.DxIndent() + 2)
|
||
|
return True
|
||
|
|
||
|
# scmd -- a definition of a scripting command
|
||
|
class Scmd(object):
|
||
|
"""A definition of a scripting command. All methods are class methods; scmds are never instantiated."""
|
||
|
def __init__(self):
|
||
|
raise "do not instantiate scmds"
|
||
|
|
||
|
# used by RegScmd to build a list of all valid scmds for editing
|
||
|
rgscmd = []
|
||
|
|
||
|
@classmethod
|
||
|
def Rgscmd_st(cls, scril):
|
||
|
return [(scmd, scmd.StName()) for scmd in cls.rgscmd]
|
||
|
@classmethod
|
||
|
def Desc(cls, *rgsarg):
|
||
|
"""
|
||
|
Returns a list of desces. A desce can be a constant string, representing nonselectable helper text,
|
||
|
an sproj, a list containing an sproj and a reference to a passed-in sarg, which explicitly binds that
|
||
|
sproj to that sarg, or a list containing a string, which stands for the selectable text which represents
|
||
|
the scril itself.
|
||
|
ie, [["Move"], " ", [SprojMovable, rgsarg[1]], " to ", [SprojPosition, rgsarg[0]]]
|
||
|
If a bare sproj is given, bind it to the first unbound sarg (after processing all explicitly bound sargs).
|
||
|
|
||
|
This method must accept invalid sargs; it is the sproj's job to relay the invalidness to the user.
|
||
|
"""
|
||
|
return cls.desc
|
||
|
|
||
|
stName = None
|
||
|
|
||
|
@classmethod
|
||
|
def StName(cls):
|
||
|
"Returns the name that the user types in to select this scmd."
|
||
|
if cls.stName != None:
|
||
|
return cls.stName
|
||
|
for desce in cls.Desc(Scril(None, cls).rgsarg):
|
||
|
if type(desce) == list and type(desce[0]) == str:
|
||
|
return desce[0]
|
||
|
assert False, "no selectable self in desc"
|
||
|
|
||
|
@classmethod
|
||
|
def Csarg(cls):
|
||
|
"Returns the number of arguments that this command takes."
|
||
|
return len(cls.rgstypeArg)
|
||
|
@classmethod
|
||
|
def StypeResult(cls):
|
||
|
return cls.stypeResult
|
||
|
@classmethod
|
||
|
def RgstypeArg(cls):
|
||
|
return cls.rgstypeArg
|
||
|
|
||
|
def RegScmd(stypeResult, *rgstypeArg):
|
||
|
def RegI(cls):
|
||
|
cls.stypeResult = stypeResult
|
||
|
cls.rgstypeArg = rgstypeArg
|
||
|
Scmd.rgscmd.append(cls)
|
||
|
return cls
|
||
|
return RegI
|
||
|
|
||
|
@RegScmd(None)
|
||
|
class ScmdBlank(Scmd):
|
||
|
stName = ""
|
||
|
desc = [[" "]]
|
||
|
@classmethod
|
||
|
def Exec(cls, ectx):
|
||
|
pass
|
||
|
|
||
|
@RegScmd(None)
|
||
|
class ScmdHelloWorld(Scmd):
|
||
|
"Hello, world!"
|
||
|
desc = [["Hello World"], "!"]
|
||
|
@classmethod
|
||
|
def Exec(cls, ectx):
|
||
|
print "Hello, world!"
|
||
|
|
||
|
@RegScmd(None, StypeBool, StypeBlock, StypeBlock)
|
||
|
class ScmdIf(Scmd):
|
||
|
desc = [["If"], " ", StypeBool.Sproj, ", then:", StypeBlock.Sproj, "or else:", StypeBlock.Sproj]
|
||
|
@classmethod
|
||
|
def Exec(cls, ectx, sargExpr, sargIfTrue, sargIfFalse):
|
||
|
if sargExpr.Eval(ectx):
|
||
|
print "true!"
|
||
|
|
||
|
# needs work
|
||
|
#===========================================================================
|
||
|
# @RegScmd(None, StypeSt, StypeObj)
|
||
|
# class ScmdVar(Scmd):
|
||
|
# desc = ["Define a new ", ["variable"], " called \"", StypeSt, "\", set to ", StypeObj, "."]
|
||
|
# @classmethod
|
||
|
# def Exec(cls, ectx, sargStName, sargInit):
|
||
|
# ectx.mpvar_sobj[ectx.ScrilCurr()] = sargInit.Eval()
|
||
|
#===========================================================================
|
||
|
|
||
|
class Scril(Typeable):
|
||
|
"A line in a script."
|
||
|
def InitPersistent(self, blockParent, scmd = None):
|
||
|
self.scmd = scmd or ScmdBlank
|
||
|
self.rgsarg = [Sarg(stypeArg, self) for stypeArg in self.scmd.RgstypeArg()]
|
||
|
self.blockParent = blockParent
|
||
|
|
||
|
def SetScmd(self, scmd):
|
||
|
if (scmd.Csarg() > self.scmd.Csarg()):
|
||
|
self.rgsarg.extend([Sarg(stypeArg, self) for stypeArg in scmd.RgstypeArg()[self.scmd.Csarg():]])
|
||
|
elif (scmd.Csarg() < self.scmd.Csarg()):
|
||
|
self.rgsarg = self.rgsarg[:scmd.Csarg()]
|
||
|
self.scmd = scmd
|
||
|
|
||
|
def Exec(self, ectx):
|
||
|
return self.scmd.Exec(ectx, *self.rgsarg)
|
||
|
|
||
|
def HandleKey(self, pwKey, pov, psel, key):
|
||
|
if ansi.FEnter(key):
|
||
|
self.blockParent.InsertScrilAfter(self, ScmdBlank)
|
||
|
return True
|
||
|
return False
|
||
|
def Project(self, pwBlock, dxindent):
|
||
|
"adds horizontal pws to a vertical pw"
|
||
|
pwScril = None
|
||
|
|
||
|
desc = self.scmd.Desc(*self.rgsarg)
|
||
|
rgsargUnbound = list(self.rgsarg)
|
||
|
#filter bound sargs
|
||
|
for desce in desc:
|
||
|
if isinstance(desce, list) and len(desce) > 1:
|
||
|
rgsargUnbound.remove(desce[1])
|
||
|
|
||
|
fNewScril = True
|
||
|
for desce in desc:
|
||
|
if fNewScril:
|
||
|
pwKey = PwKeyHandler(pwBlock, self.HandleKey)
|
||
|
pwScril = PwList(pwKey, self, dxindent)
|
||
|
fNewScril = False
|
||
|
if isinstance(desce, str):
|
||
|
PwStatic(pwScril, desce)
|
||
|
elif isinstance(desce, list):
|
||
|
if isinstance(desce[0], str):
|
||
|
# selection for the scmd / scril
|
||
|
ProjectTypein(pwScril, self, desce[0], Scmd.Rgscmd_st, Scril.SetScmd)
|
||
|
else:
|
||
|
# mapping to sarg
|
||
|
fNewScril = self.ProjectSarg(pwBlock, pwScril, desce[0], desce[1])
|
||
|
else:
|
||
|
# sproj
|
||
|
fNewScril = self.ProjectSarg(pwBlock, pwScril, desce, rgsargUnbound.pop(0))
|
||
|
|
||
|
def ProjectSarg(self, pwBlock, pwScril, sproj, sarg):
|
||
|
return sproj(sarg, pwBlock, pwScril)
|
||
|
|
||
|
# pw - projected widget
|
||
|
# Nothing ever depends on the object identity or permanence of a pw!
|
||
|
# They are generated on demand, whenever we need to draw or process a
|
||
|
# keypress.
|
||
|
|
||
|
class Pw(object):
|
||
|
def __init__(self, pwParent):
|
||
|
if pwParent != None:
|
||
|
pwParent.AddChild(self)
|
||
|
self.pwParent = pwParent
|
||
|
def PwParent(self):
|
||
|
return self.pwParent
|
||
|
def Value(self):
|
||
|
return None
|
||
|
def FSelectable(self):
|
||
|
return False
|
||
|
def FContainer(self):
|
||
|
return False
|
||
|
def RgpwChild(self):
|
||
|
return []
|
||
|
def HandleKey(self, pov, psel, key):
|
||
|
return False
|
||
|
def PwFirstSelectable(self):
|
||
|
if self.FSelectable():
|
||
|
return self
|
||
|
for pwChild in self.RgpwChild():
|
||
|
pw = pwChild.PwFirstSelectable()
|
||
|
if pw != None:
|
||
|
return pw
|
||
|
return None
|
||
|
|
||
|
class PwContainer(Pw):
|
||
|
def __init__(self, pwParent):
|
||
|
Pw.__init__(self, pwParent)
|
||
|
self.rgpwContents = []
|
||
|
def AddChild(self, pw):
|
||
|
self.rgpwContents.append(pw)
|
||
|
def RgpwChild(self):
|
||
|
return self.rgpwContents
|
||
|
def FContainer(self):
|
||
|
return True
|
||
|
|
||
|
class PwStatic(Pw):
|
||
|
def __init__(self, pwParent, stText):
|
||
|
Pw.__init__(self, pwParent)
|
||
|
self.stText = stText
|
||
|
def DxDyNew(self, w, dxStart, mpksel):
|
||
|
rgsnippet = self.Rgsnippet(w, dxStart, mpksel)
|
||
|
if len(rgsnippet) == 1:
|
||
|
return (dxStart + len(rgsnippet[0].st), 0)
|
||
|
else:
|
||
|
return (len(rgsnippet[-1].st), len(rgsnippet) - 1)
|
||
|
def StTextDisplay(self, ksel):
|
||
|
return self.stText
|
||
|
def Rgsnippet(self, w, dxStart, mpksel):
|
||
|
ksel = mpksel.Get(self)
|
||
|
return list(RgSnippetWrapped(self.StTextDisplay(ksel), w, ksel == Ksel.NAV, dxStart))
|
||
|
def ColBg(self, ksel):
|
||
|
return ansi.BLACK
|
||
|
def ColFg(self):
|
||
|
return ansi.WHITE | ansi.FBRIGHT
|
||
|
def RgSnippetXY(self, x, y, w, dxStart, mpksel):
|
||
|
fFirst = True
|
||
|
for snippet in self.Rgsnippet(w, dxStart, mpksel):
|
||
|
xT = x + dxStart if fFirst else x
|
||
|
yield (snippet, xT, y)
|
||
|
y = y + 1
|
||
|
fFirst = False
|
||
|
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||
|
if not fOverlay:
|
||
|
ksel = mpksel.Get(self)
|
||
|
for (snippet, x, y) in self.RgSnippetXY(x, y, w, dxStart, mpksel):
|
||
|
ascr.PutSt(snippet.st, x, y, self.ColFg(), self.ColBg(ksel))
|
||
|
|
||
|
class PwTypein(PwStatic):
|
||
|
def __init__(self, pwParent, stSobj, typeable):
|
||
|
if typeable.GetStTypein() == None:
|
||
|
stDisplay = stSobj
|
||
|
else:
|
||
|
stDisplay = None
|
||
|
PwStatic.__init__(self, pwParent, stDisplay)
|
||
|
self.typeable = typeable
|
||
|
def Value(self):
|
||
|
return self.typeable
|
||
|
def StTextDisplay(self, ksel):
|
||
|
if self.stText == None:
|
||
|
return self.typeable.steditTypein.StForSize(ksel == Ksel.NAV)
|
||
|
return self.stText
|
||
|
def FSelectable(self):
|
||
|
return True
|
||
|
def ColBg(self, ksel):
|
||
|
if ksel == Ksel.NAV:
|
||
|
return ansi.YELLOW
|
||
|
elif ksel == Ksel.OTHERNAV:
|
||
|
return ansi.MAGENTA
|
||
|
return ansi.BLUE
|
||
|
def HandleKey(self, pov, psel, key):
|
||
|
return self.typeable.HandleTypeinKey(key)
|
||
|
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||
|
if not fOverlay:
|
||
|
ksel = mpksel.Get(self)
|
||
|
fTextSelect = ksel == Ksel.NAV and self.typeable.GetStTypein() != None
|
||
|
for (snippet, x, y) in self.RgSnippetXY(x, y, w, dxStart, mpksel):
|
||
|
ascr.PutSt(snippet.st, x, y, self.ColFg(), self.ColBg(Ksel.NONE if fTextSelect else ksel))
|
||
|
if fTextSelect:
|
||
|
self.typeable.steditTypein.DrawCursor(ascr, x, y, self.ColFg(), self.ColBg(ksel), snippet)
|
||
|
else:
|
||
|
for pwChild in self.RgpwChild():
|
||
|
pwChild.Draw(ascr, x, y, dxStart, mpksel, fOverlay)
|
||
|
|
||
|
class PwButton(PwStatic):
|
||
|
def __init__(self, pwParent, stText, value, dgEnter):
|
||
|
PwStatic.__init__(self, pwParent, stText)
|
||
|
self.value = value
|
||
|
self.dgEnter = dgEnter
|
||
|
def Value(self):
|
||
|
return self.value
|
||
|
def FSelectable(self):
|
||
|
return True
|
||
|
def ColBg(self, ksel):
|
||
|
if ksel == Ksel.NAV:
|
||
|
return ansi.YELLOW
|
||
|
elif ksel == Ksel.OTHERNAV:
|
||
|
return ansi.MAGENTA
|
||
|
return ansi.BLUE
|
||
|
def ColFg(self):
|
||
|
return ansi.BLUE | ansi.FBRIGHT
|
||
|
def HandleKey(self, pov, psel, key):
|
||
|
if key == ansi.K_RETURN:
|
||
|
self.dgEnter(self.Value())
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
class PwButtonHidden(PwButton):
|
||
|
def __init__(self, pwParent, stText, value, dgEnter, dxIndent):
|
||
|
PwButton.__init__(self, pwParent, stText, value, dgEnter)
|
||
|
self.dxIndent = dxIndent
|
||
|
def DxDyNew(self, w, dxStart, mpksel):
|
||
|
if mpksel.Get(self) == Ksel.NAV:
|
||
|
return PwButton.DxDyNew(self, w - self.dxIndent, dxStart + self.dxIndent, mpksel)
|
||
|
return (0, 0)
|
||
|
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||
|
if mpksel.Get(self) == Ksel.NAV:
|
||
|
PwButton.Draw(self, ascr, x + self.dxIndent, y, w - self.dxIndent, dxStart, mpksel, fOverlay)
|
||
|
|
||
|
class PwKeyHandler(PwContainer):
|
||
|
def __init__(self, pwParent, dgHandleKey):
|
||
|
PwContainer.__init__(self, pwParent)
|
||
|
self.dgHandleKey = dgHandleKey
|
||
|
def DxDyNew(self, w, dxStart, mpksel):
|
||
|
return self.RgpwChild()[0].DxDyNew(w, dxStart, mpksel)
|
||
|
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||
|
self.RgpwChild()[0].Draw(ascr, x, y, w, dxStart, mpksel, fOverlay)
|
||
|
def HandleKey(self, pov, psel, key):
|
||
|
self.dgHandleKey(self, pov, psel, key)
|
||
|
|
||
|
class PwBlock(PwContainer):
|
||
|
def __init__(self, pwParent, block, dxIndent = 2):
|
||
|
PwContainer.__init__(self, pwParent)
|
||
|
self.block = block
|
||
|
self.dxIndent = dxIndent
|
||
|
def Value(self):
|
||
|
return self.block
|
||
|
def DxIndent(self):
|
||
|
return self.dxIndent
|
||
|
def DxDyNew(self, w, dxStart, mpksel):
|
||
|
return (0, 1 + sum([pw.DxDyNew(w - self.DxIndent(), 0, mpksel)[1] + 1 for pw in self.rgpwContents]))
|
||
|
def RgpwChildToDraw(self):
|
||
|
return self.RgpwChild()
|
||
|
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||
|
for pw in self.RgpwChildToDraw():
|
||
|
pw.Draw(ascr, x + self.DxIndent(), y, w - self.DxIndent(), 0, mpksel, fOverlay)
|
||
|
(dxNew, dyNew) = pw.DxDyNew(w - self.DxIndent(), 0, mpksel)
|
||
|
if dxNew > 0 or dyNew > 0:
|
||
|
y = y + dyNew + 1
|
||
|
def HandleKey(self, pov, psel, key):
|
||
|
if key == ansi.K_UP:
|
||
|
return psel.Dec(self)
|
||
|
elif key == ansi.K_DOWN:
|
||
|
return psel.Inc(self)
|
||
|
elif key == ansi.K_DEL:
|
||
|
pass
|
||
|
return False
|
||
|
|
||
|
class PwDropdown(PwBlock):
|
||
|
def __init__(self, pwParent):
|
||
|
PwBlock.__init__(self, pwParent, None, 0)
|
||
|
def WidthMax(self, mpksel):
|
||
|
w = -1
|
||
|
for pw in self.RgpwChild():
|
||
|
wPw = pw.DxDyNew(1000000, 0, mpksel)[0]
|
||
|
if wPw > w:
|
||
|
w = wPw
|
||
|
return w
|
||
|
def RgpwChildToDraw(self):
|
||
|
return PwBlock.RgpwChild(self)[1:]
|
||
|
def PwChildFirst(self):
|
||
|
return PwBlock.RgpwChild(self)[0]
|
||
|
def DxDyNew(self, w, dxStart, mpksel):
|
||
|
return self.PwChildFirst().DxDyNew(w, dxStart, mpksel)
|
||
|
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
|
||
|
if not fOverlay:
|
||
|
self.PwChildFirst().Draw(ascr, x, y, w, dxStart, mpksel, False)
|
||
|
else:
|
||
|
wMax = self.WidthMax(mpksel)
|
||
|
w = w - dxStart
|
||
|
x = x + dxStart
|
||
|
if x + wMax > w and wMax < w:
|
||
|
x = w - self.WidthMax(mpksel)
|
||
|
PwBlock.Draw(self, ascr, x, y + 1, w, 0, mpksel, False)
|
||
|
|
||
|
class PwList(PwContainer):
|
||
|
def __init__(self, pwParent, scril, dxIndent):
|
||
|
PwContainer.__init__(self, pwParent)
|
||
|
self.scril = scril
|
||
|
self.dxIndent = dxIndent
|
||
|
def Value(self):
|
||
|
return self.scril
|
||
|
def DxDyNew(self, w, dxStart, mpksel):
|
||
|
dx = dxStart
|
||
|
dy = 0
|
||
|
for pw in self.rgpwContents:
|
||
|
(dx, dyPw) = pw.DxDyNew(w, dx, mpksel)
|
||
|
dy = dy + dyPw
|
||
|
return (dx, dy)
|
||
|
def DxIndent(self):
|
||
|
return self.dxIndent
|
||
|
def Draw(self, ascr, x, y, w, dx, mpksel, fOverlay):
|
||
|
for pw in self.rgpwContents:
|
||
|
pw.Draw(ascr, x + self.DxIndent(), y, w - self.DxIndent(), dx, mpksel, fOverlay)
|
||
|
(dx, dy) = pw.DxDyNew(w, dx, mpksel)
|
||
|
y = y + dy
|
||
|
def HandleKey(self, pov, psel, key):
|
||
|
if key == ansi.K_LEFT:
|
||
|
return psel.Dec(self)
|
||
|
elif key == ansi.K_RIGHT:
|
||
|
return psel.Inc(self)
|
||
|
return False
|
||
|
|
||
|
class Ksel(object):
|
||
|
FOTHER = 0x2000
|
||
|
NONE = FOTHER | 0x1000 # OR together all ksel flags and add 0x1000; largest possible ksel
|
||
|
NAV = 0
|
||
|
OTHERNAV = NAV | FOTHER
|
||
|
|
||
|
@staticmethod
|
||
|
def KselBest(ksel1, ksel2):
|
||
|
if ksel1 < ksel2:
|
||
|
return ksel1
|
||
|
return ksel2
|
||
|
|
||
|
class Psel(TPrs):
|
||
|
def InitPersistent(self, pw, ksel):
|
||
|
assert len(pw.RgpwChild()) == 0 #leaf
|
||
|
self.rgo_ipw = self.Rgo_ipwFromPw(pw)
|
||
|
self.ksel = ksel
|
||
|
|
||
|
def Rgo_ipwFromPw(self, pw):
|
||
|
rgo_ipw = []
|
||
|
if pw != None:
|
||
|
while pw.PwParent() != None:
|
||
|
rgo_ipw.insert(0, (pw.Value(), pw.PwParent().RgpwChild().index(pw)))
|
||
|
pw = pw.PwParent()
|
||
|
return rgo_ipw
|
||
|
|
||
|
def Validate(self, pwRoot):
|
||
|
assert pwRoot.PwParent() == None #root
|
||
|
io_ipw = 0
|
||
|
pwSelected = pwRoot
|
||
|
while len(pwSelected.RgpwChild()) > 0 and io_ipw < len(self.rgo_ipw):
|
||
|
(o, ipw) = self.rgo_ipw[io_ipw]
|
||
|
ipwNew = 0
|
||
|
for pwChild in pwSelected.RgpwChild():
|
||
|
if o != None and pwChild.Value() == o:
|
||
|
break
|
||
|
ipwNew = ipwNew + 1
|
||
|
if ipwNew == len(pwSelected.RgpwChild()):
|
||
|
ipwLim = ipw + 1 if ipw < len(pwSelected.RgpwChild()) else len(pwSelected.RgpwChild())
|
||
|
ipwNew = IoDec(pwSelected.RgpwChild(), ipwLim, self.FPwValid)
|
||
|
if ipwNew < 0:
|
||
|
break
|
||
|
pwSelected = pwSelected.RgpwChild()[ipwNew]
|
||
|
io_ipw = io_ipw + 1
|
||
|
|
||
|
# we've made our best guess as to what the closest selectable thing is -- now make sure we point at SOMEthing selectable.
|
||
|
if not pwSelected.FSelectable():
|
||
|
pwT = pwSelected
|
||
|
while pwT != None:
|
||
|
pwSelected = pwT.PwFirstSelectable()
|
||
|
if pwSelected != None:
|
||
|
break
|
||
|
pwT = pwT.PwParent()
|
||
|
|
||
|
self.rgo_ipw = self.Rgo_ipwFromPw(pwSelected)
|
||
|
|
||
|
def FPwValid(self, pw):
|
||
|
if pw.FSelectable():
|
||
|
return True
|
||
|
for pwChild in pw.RgpwChild():
|
||
|
if self.FPwValid(pwChild):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def Value(self, pwRoot):
|
||
|
self.Validate(pwRoot)
|
||
|
if len(self.rgo_ipw) > 0:
|
||
|
return self.rgo_ipw[-1][0]
|
||
|
return None
|
||
|
|
||
|
def PwSelected(self, pwRoot, clevel = None):
|
||
|
self.Validate(pwRoot)
|
||
|
return self.PwSelectedI(pwRoot, self.rgo_ipw[:clevel])
|
||
|
|
||
|
def PwSelectedI(self, pw, rgo_ipw):
|
||
|
for (_, ipw) in rgo_ipw:
|
||
|
pw = pw.RgpwChild()[ipw]
|
||
|
return pw
|
||
|
# contract: pwContainer is actually selected
|
||
|
def Inc(self, pwContainer):
|
||
|
return self.ChangeI(pwContainer, IoInc)
|
||
|
def Dec(self, pwContainer):
|
||
|
return self.ChangeI(pwContainer, IoDec)
|
||
|
|
||
|
def ChangeI(self, pwContainer, fnChange):
|
||
|
clevelChild = 1
|
||
|
pwRoot = pwContainer
|
||
|
while pwRoot.PwParent() != None:
|
||
|
clevelChild = clevelChild + 1
|
||
|
pwRoot = pwRoot.PwParent()
|
||
|
self.Validate(pwRoot)
|
||
|
assert self.PwSelected(pwRoot, clevelChild).PwParent() == pwContainer
|
||
|
|
||
|
ipw = self.rgo_ipw[clevelChild - 1][1]
|
||
|
rgpw = pwContainer.RgpwChild()
|
||
|
ipwNew = fnChange(rgpw, ipw, self.FPwValid)
|
||
|
self.rgo_ipw[clevelChild - 1] = (rgpw[ipwNew].Value(), ipwNew)
|
||
|
self.Validate(pwRoot)
|
||
|
return ipwNew != ipw
|
||
|
|
||
|
# projector overlay -- handles navigation, drawing the projection, etc
|
||
|
class Pov(TokenClient):
|
||
|
def InitPersistent(self, owner, client, block):
|
||
|
TokenClient.InitPersistent(self, owner, client, "drawable", "overlay")
|
||
|
self.block = block
|
||
|
self.pselstate = self.game.rgtoken("pselstate")[0]
|
||
|
|
||
|
def PwProjected(self):
|
||
|
return self.block.Project(None)
|
||
|
|
||
|
def run(self):
|
||
|
while True:
|
||
|
key = self.client.evKey.receive(self)
|
||
|
psel = self.pselstate.PselByClient(self.client)
|
||
|
pwSel = psel.PwSelected(self.PwProjected())
|
||
|
while pwSel != None:
|
||
|
if pwSel.HandleKey(self, psel, key):
|
||
|
break
|
||
|
pwSel = pwSel.PwParent()
|
||
|
|
||
|
def draw(self, ascr, client):
|
||
|
if client == self.client:
|
||
|
pw = self.PwProjected()
|
||
|
mpksel = self.pselstate.GetMpksel(pw, client)
|
||
|
pw.Draw(ascr, 1, 1, ascr.W(), 0, mpksel, False)
|
||
|
pw.Draw(ascr, 1, 1, ascr.W(), 0, mpksel, True)
|
||
|
|
||
|
class PselState(Token):
|
||
|
def InitPersistent(self, owner, block):
|
||
|
Token.InitPersistent(self, owner, "pselstate")
|
||
|
self.block = block
|
||
|
def InitTransient(self):
|
||
|
Token.InitTransient(self)
|
||
|
self.mpclient_psel = {}
|
||
|
def OnLeave(self, client):
|
||
|
del self.mpclient_psel[client]
|
||
|
def OnJoin(self, client):
|
||
|
print "client joined,", client
|
||
|
self.mpclient_psel[client] = Psel(self.block.Project(None).PwFirstSelectable(), Ksel.NAV)
|
||
|
class Mpksel(object):
|
||
|
def __init__(self, pwRoot, client, mpclient_psel):
|
||
|
self.mppw_ksel = {}
|
||
|
for clientT, psel in mpclient_psel.items():
|
||
|
kselNew = Ksel.NAV
|
||
|
if clientT != client:
|
||
|
kselNew = kselNew | Ksel.FOTHER
|
||
|
pw = psel.PwSelected(pwRoot)
|
||
|
self.mppw_ksel[pw] = Ksel.KselBest(kselNew, self.Get(pw))
|
||
|
|
||
|
def Get(self, pw):
|
||
|
return self.mppw_ksel.get(pw, Ksel.NONE)
|
||
|
|
||
|
def GetMpksel(self, pwRoot, client):
|
||
|
return self.Mpksel(pwRoot, client, self.mpclient_psel)
|
||
|
def PselByClient(self, client):
|
||
|
return self.mpclient_psel[client]
|
||
|
def run(self):
|
||
|
print "pselstate running"
|
||
|
with self.game.evLeave.oob(self, self.OnLeave):
|
||
|
while True:
|
||
|
self.OnJoin(self.game.evJoin.receive(self))
|
||
|
|
||
|
class GameScriptTest(Game):
|
||
|
def InitPersistent(self):
|
||
|
Game.InitPersistent(self)
|
||
|
self.block = Block(None)
|
||
|
self.block.InsertScrilAfter(None, ScmdHelloWorld)
|
||
|
self.block.InsertScrilAfter(None, ScmdBlank)
|
||
|
self.block.InsertScrilAfter(None, ScmdHelloWorld)
|
||
|
self.block.InsertScrilAfter(None, ScmdIf)
|
||
|
self.block.InsertScrilAfter(None, ScmdHelloWorld)
|
||
|
|
||
|
def GetRgclsTokTrans(self):
|
||
|
return [[PselState, self.block], [AutoJoiner, [Pov, self.block]]]
|
||
|
|
||
|
class RunnerScriptTest(Runner):
|
||
|
def InitPersistent(self):
|
||
|
self.game = GameScriptTest()
|
||
|
Runner.InitPersistent(self)
|
||
|
|
||
|
def RunGame(self, client):
|
||
|
client.joinGame(self.game)
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
Run(RunnerScriptTest, "script_test.marm")
|