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 *
|
|
|
|
import config
|
|
|
|
from zope.interface import implements, Interface
|
|
|
|
import weakref
|
|
|
|
from util import *
|
|
|
|
|
|
|
|
class Blinker(Token):
|
|
|
|
def InitPersistent(self, owner, setter, rgVal = [True, False], interval = 500):
|
|
|
|
Token.InitPersistent(self, owner)
|
|
|
|
self.setter = setter
|
|
|
|
self.rgVal = rgVal
|
|
|
|
self.interval = interval
|
|
|
|
def run(self):
|
|
|
|
while True:
|
|
|
|
for val in self.rgVal:
|
|
|
|
self.setter(val)
|
|
|
|
self.wait(self.interval)
|
|
|
|
|
|
|
|
class StaticGraphic(Token):
|
|
|
|
def InitPersistent(self, owner, ascr, pos, *rgtag):
|
|
|
|
Token.InitPersistent(self, owner)
|
|
|
|
self.pos = pos
|
|
|
|
self.tag("drawable", *rgtag)
|
|
|
|
self.ascr = ascr
|
|
|
|
def draw(self, ascr, client):
|
|
|
|
ascr.PutAscr(self.ascr, self.pos[0], self.pos[1])
|
|
|
|
|
|
|
|
|
|
|
|
class AnimatedGraphic(StaticGraphic):
|
|
|
|
def InitPersistent(self, owner, rgascr, pos, interval, *rgtag):
|
|
|
|
StaticGraphic.InitPersistent(self, owner, rgascr[0], pos, *rgtag)
|
|
|
|
Blinker(self, self.setAscr, rgascr, interval)
|
|
|
|
def setAscr(self, ascr):
|
|
|
|
self.ascr = ascr
|
|
|
|
|
|
|
|
class Terminal(Token):
|
|
|
|
def InitPersistent(self, owner, client, w = config.W, h = config.H):
|
|
|
|
Token.InitPersistent(self, owner, "drawable", "background")
|
|
|
|
self.client = client
|
|
|
|
self.x = 0
|
|
|
|
self.y = 0
|
|
|
|
self.w = w
|
|
|
|
self.h = h
|
|
|
|
self.fWrapHoriz = False
|
|
|
|
self.fWrapVert = True
|
|
|
|
self.stLine = None
|
|
|
|
self.xLine = None
|
|
|
|
self.yLine = None
|
|
|
|
self.screen = []
|
|
|
|
self.fBlink = False
|
|
|
|
self.colBg = ansi.BLUE
|
|
|
|
self.chEcho = None
|
|
|
|
for y in range(h):
|
|
|
|
self.screen.append("")
|
|
|
|
Blinker(self, self.setBlink)
|
|
|
|
|
|
|
|
def setBlink(self, fBlink):
|
|
|
|
self.fBlink = fBlink
|
|
|
|
|
|
|
|
def getKey(self):
|
2020-07-20 01:31:54 +00:00
|
|
|
return self.client.EvKey(self.game).receive(self)
|
2011-03-19 00:10:02 +00:00
|
|
|
|
|
|
|
def getPrintableKey(self):
|
|
|
|
while True:
|
|
|
|
key = self.getKey()
|
|
|
|
if ansi.FKeyPrintable(key):
|
|
|
|
return key
|
|
|
|
|
|
|
|
def deleteCh(self):
|
|
|
|
stOld = self.screen[self.y]
|
|
|
|
self.screen[self.y] = stOld[0:self.x] + stOld[self.x + 1:]
|
|
|
|
|
|
|
|
def printSt(self, st):
|
|
|
|
stOld = self.screen[self.y]
|
|
|
|
stNew = stOld[0:self.x] + st + stOld[self.x+len(st):]
|
|
|
|
self.screen[self.y] = stNew
|
|
|
|
self.moveTo(self.x + len(st), self.y)
|
|
|
|
|
|
|
|
def moveTo(self, x, y):
|
|
|
|
self.x = x
|
|
|
|
self.y = y
|
|
|
|
while (self.x >= self.w):
|
|
|
|
if (self.fWrapHoriz):
|
|
|
|
self.y = self.y + 1
|
|
|
|
self.x = self.x - self.w
|
|
|
|
else:
|
|
|
|
self.x = self.w - 1
|
|
|
|
while (self.x < 0):
|
|
|
|
self.y = self.y - 1
|
|
|
|
self.x = self.x + self.w
|
|
|
|
if (self.y < 0):
|
|
|
|
self.y = 0
|
|
|
|
while (self.fWrapVert and self.y >= self.h):
|
|
|
|
self.screen = self.screen[1:]
|
|
|
|
self.screen.append("")
|
|
|
|
self.y = self.y - 1
|
|
|
|
self.screen[self.y] = self.screen[self.y].ljust(self.x)
|
|
|
|
|
|
|
|
def type(self, st):
|
|
|
|
self.client.beep()
|
|
|
|
for iSt in range(len(st)):
|
|
|
|
self.printSt(st[iSt:iSt + 1])
|
|
|
|
self.wait(35)
|
|
|
|
|
|
|
|
def typeLn(self, st):
|
|
|
|
self.type(st)
|
|
|
|
self.newLine()
|
|
|
|
|
|
|
|
def centerLn(self, st):
|
|
|
|
self.typeLn(st.rjust((self.w / 2) + (len(st) / 2)))
|
|
|
|
|
|
|
|
def getLine(self):
|
|
|
|
self.xLine = self.x
|
|
|
|
self.yLine = self.y
|
|
|
|
self.stLine = ""
|
|
|
|
|
|
|
|
fWrapHorizStart = self.fWrapHoriz
|
|
|
|
self.fWrapHoriz = False
|
|
|
|
while True:
|
|
|
|
key = self.getKey()
|
|
|
|
fBeep = True
|
|
|
|
if (key == ansi.K_BACKSPACE and self.x > self.xLine):
|
|
|
|
self.stLine = self.stLine[:self.x - self.xLine - 1] + self.stLine[self.x - self.xLine:]
|
|
|
|
self.moveTo(self.x - 1, self.y)
|
|
|
|
elif (key == ansi.K_LEFT and self.x > self.xLine):
|
|
|
|
self.moveTo(self.x - 1, self.y)
|
|
|
|
elif (key == ansi.K_RIGHT):
|
|
|
|
self.moveTo(self.x + 1, self.y)
|
|
|
|
if (self.x - self.xLine) > len(self.stLine):
|
|
|
|
self.stLine = self.stLine + " "
|
|
|
|
elif (ansi.FKeyPrintable(key)):
|
|
|
|
self.stLine = self.stLine[:self.x - self.xLine] + key + self.stLine[(self.x + 1) - self.xLine:]
|
|
|
|
self.moveTo(self.x + 1, self.y)
|
2020-06-27 19:39:10 +00:00
|
|
|
elif key == ansi.K_RETURN or key == ansi.K_NEWLINE:
|
2011-03-19 00:10:02 +00:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
print "weird key", ansi.StrKey(key)
|
|
|
|
fBeep = False
|
|
|
|
if fBeep:
|
|
|
|
self.client.beep()
|
|
|
|
self.fWrapHoriz = fWrapHorizStart
|
|
|
|
|
|
|
|
x,y = self.x, self.y
|
|
|
|
self.moveTo(self.xLine, self.yLine)
|
|
|
|
self.printSt(self.StLineDisp())
|
|
|
|
self.moveTo(x,y)
|
|
|
|
|
|
|
|
stLine = self.stLine.strip()
|
|
|
|
self.stLine = None
|
|
|
|
self.xLine = None
|
|
|
|
self.yLine = None
|
|
|
|
return stLine
|
|
|
|
|
|
|
|
def StLineDisp(self):
|
|
|
|
return self.stLine if not self.chEcho else self.chEcho * len(self.stLine)
|
|
|
|
def draw(self, ascr, client):
|
|
|
|
if client == self.client:
|
|
|
|
y = 1
|
|
|
|
for line in self.screen:
|
|
|
|
ascr.PutSt("{0:{1}}".format(line, self.w), 1, y, ansi.WHITE | ansi.FBRIGHT, self.colBg)
|
|
|
|
y = y + 1
|
|
|
|
if self.stLine:
|
|
|
|
ascr.PutSt(self.StLineDisp(), self.xLine + 1, self.yLine + 1, ansi.WHITE | ansi.FBRIGHT, self.colBg)
|
|
|
|
if (self.fBlink):
|
|
|
|
ascr.PutAch(ansi.MkAch(" ", ansi.WHITE | ansi.FBRIGHT, ansi.WHITE), self.x + 1, self.y + 1)
|
|
|
|
def spin(self, stSpin):
|
|
|
|
self.moveTo(self.x - 1, self.y)
|
|
|
|
self.printSt(stSpin)
|
|
|
|
|
|
|
|
def newSpinner(self):
|
|
|
|
return Blinker(self.game, self.spin, ["/","-","\\", "|"], 100)
|
|
|
|
|
|
|
|
def newLine(self, count = 1, wait = 0):
|
|
|
|
for i in range(count):
|
|
|
|
self.moveTo(0, self.y + 1)
|
|
|
|
self.beep()
|
|
|
|
self.wait(wait)
|
|
|
|
|
|
|
|
def beep(self):
|
|
|
|
self.client.beep()
|
|
|
|
|
|
|
|
def setColBg(self, colBg):
|
|
|
|
self.colBg = colBg
|
2011-09-21 21:41:57 +00:00
|
|
|
|
|
|
|
class LeaveJoinToken(Token):
|
|
|
|
def run(self):
|
2020-07-20 01:31:54 +00:00
|
|
|
evLeave = self.game.evSuspend if self.FOnActivate() else self.game.evLeave
|
|
|
|
evJoin = self.game.evActivate if self.FOnActivate() else self.game.evJoin
|
2011-09-21 21:41:57 +00:00
|
|
|
while True:
|
2020-07-20 01:31:54 +00:00
|
|
|
Event.selectDg(self, (evLeave, self.OnLeave), (evJoin, self.OnJoin))
|
|
|
|
|
|
|
|
def FOnActivate(self):
|
|
|
|
return False
|
|
|
|
|
2011-09-21 21:41:57 +00:00
|
|
|
class AutoJoiner(LeaveJoinToken):
|
2011-03-19 00:10:02 +00:00
|
|
|
def InitPersistent(self, owner, *rgclsTok):
|
|
|
|
Token.InitPersistent(self, owner)
|
|
|
|
self.rgclsTok = rgclsTok
|
|
|
|
|
|
|
|
def InitTransient(self):
|
|
|
|
Token.InitTransient(self)
|
|
|
|
self.mpclient_rgtok = {}
|
2011-09-21 21:41:57 +00:00
|
|
|
|
|
|
|
def OnLeave(self, client):
|
|
|
|
for tok in self.mpclient_rgtok[client]:
|
|
|
|
print "killing ", tok
|
|
|
|
tok.die()
|
|
|
|
def OnJoin(self, client):
|
|
|
|
rgtok = []
|
|
|
|
for clsTok in self.rgclsTok:
|
|
|
|
if FIter(clsTok):
|
|
|
|
tok = clsTok[0](self.game, client, *clsTok[1:])
|
|
|
|
else:
|
|
|
|
tok = clsTok(self.game, client)
|
|
|
|
rgtok.append(tok)
|
|
|
|
self.mpclient_rgtok[client] = rgtok
|
|
|
|
|
|
|
|
class AliveWithPlayers(LeaveJoinToken):
|
2011-03-19 00:10:02 +00:00
|
|
|
def InitPersistent(self, owner, *rgclsTok):
|
|
|
|
Token.InitPersistent(self, owner)
|
|
|
|
self.rgclsTok = rgclsTok
|
|
|
|
|
|
|
|
def InitTransient(self):
|
|
|
|
Token.InitTransient(self)
|
|
|
|
self.rgclient = []
|
|
|
|
|
2020-07-20 01:31:54 +00:00
|
|
|
def FOnActivate(self):
|
|
|
|
return True
|
|
|
|
|
2011-03-19 00:10:02 +00:00
|
|
|
def OnLeave(self, client):
|
|
|
|
self.rgclient.remove(client)
|
|
|
|
if len(self.rgclient) == 0:
|
|
|
|
for tok in [x for x in self.rgtokOwn]:
|
|
|
|
tok.die()
|
2011-09-21 21:41:57 +00:00
|
|
|
def OnJoin(self, client):
|
|
|
|
self.rgclient.append(client)
|
|
|
|
if len(self.rgclient) == 1:
|
|
|
|
for clsTok in self.rgclsTok:
|
|
|
|
if FIter(clsTok):
|
|
|
|
tok = clsTok[0](self, clsTok[1:])
|
|
|
|
else:
|
|
|
|
tok = clsTok(self)
|
|
|
|
|
|
|
|
class AutoKiller(LeaveJoinToken):
|
2011-03-19 00:10:02 +00:00
|
|
|
def InitTransient(self):
|
|
|
|
Token.InitTransient(self)
|
|
|
|
self.rgclient = []
|
|
|
|
def OnLeave(self, client):
|
|
|
|
self.rgclient.remove(client)
|
|
|
|
if len(self.rgclient) == 0:
|
|
|
|
self.game.finish()
|
2011-09-21 21:41:57 +00:00
|
|
|
def OnJoin(self, client):
|
|
|
|
self.rgclient.append(client)
|
2011-03-19 00:10:02 +00:00
|
|
|
|
|
|
|
#selection oracle
|
|
|
|
class Selor(TPrs):
|
|
|
|
NONE = 0
|
|
|
|
PRI = 1
|
|
|
|
SEC = 2
|
|
|
|
|
|
|
|
def __init__(self, mi, selorParent, rgmiSec, fPri):
|
|
|
|
self.selorParent = selorParent
|
|
|
|
self.mi = mi
|
|
|
|
self.rgmiSec = rgmiSec
|
|
|
|
self.fPri = fPri
|
|
|
|
|
|
|
|
def MiParent(self):
|
|
|
|
if self.selorParent != None:
|
|
|
|
return self.selorParent.mi
|
|
|
|
return None
|
|
|
|
|
|
|
|
def KSel(self):
|
|
|
|
if self.fPri:
|
|
|
|
return self.PRI
|
|
|
|
elif self.mi in self.rgmiSec:
|
|
|
|
return self.SEC
|
|
|
|
else:
|
|
|
|
return self.NONE
|
|
|
|
|
|
|
|
def SelorChild(self, miChild, fPri):
|
|
|
|
return Selor(miChild, self, self.rgmiSec, self.fPri and fPri)
|
|
|
|
|
|
|
|
def SelorDeselect(self):
|
|
|
|
return Selor(self.mi, self.selorParent, self.rgmiSec, False)
|
|
|
|
class Mi(TPrs):
|
|
|
|
def InitPersistent(self):
|
|
|
|
self.miParent = None
|
|
|
|
self.fNotice = False
|
|
|
|
self.rgmiNotice = [self]
|
|
|
|
def Height(self, w):
|
|
|
|
return 1
|
|
|
|
|
|
|
|
def StName(self):
|
|
|
|
return "Unnamed"
|
|
|
|
|
|
|
|
def StKey(self):
|
|
|
|
return self.StName()
|
|
|
|
def HandleKey(self, key):
|
|
|
|
return False
|
|
|
|
def FIgnoreParent(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
def DrawWithParent(self, selorParent, ascr, x, y, w, fPri):
|
|
|
|
self.Draw(ascr, x, y, w, selorParent.SelorChild(self, fPri))
|
|
|
|
|
|
|
|
def ColBgDefault(self):
|
|
|
|
return ansi.BLUE
|
|
|
|
def ColBgSelected(self, fPri):
|
|
|
|
return ansi.YELLOW if fPri else ansi.CYAN
|
|
|
|
def ColBgNotice(self):
|
|
|
|
return ansi.CYAN
|
|
|
|
|
|
|
|
def ColBg(self, selor):
|
|
|
|
if selor != None and selor.KSel() != Selor.NONE:
|
|
|
|
if selor.KSel() == Selor.PRI:
|
|
|
|
for mi in self.rgmiNotice:
|
|
|
|
if mi != self and mi.fNotice:
|
|
|
|
print "Notice cleared for ", mi.StName(), "by ", self.StName()
|
|
|
|
mi.fNotice = False
|
|
|
|
return self.ColBgSelected(True)
|
|
|
|
else:
|
|
|
|
return self.ColBgSelected(False)
|
|
|
|
elif self.FNoticeFlagged():
|
|
|
|
return self.ColBgNotice()
|
|
|
|
elif not self.FIgnoreParent() and selor != None and selor.MiParent() != None:
|
|
|
|
return selor.MiParent().ColBg(selor.selorParent.SelorDeselect())
|
|
|
|
else:
|
|
|
|
return self.ColBgDefault()
|
|
|
|
|
|
|
|
def ColFg(self):
|
|
|
|
return ansi.WHITE | ansi.FBRIGHT
|
|
|
|
def FlagNotice(self):
|
|
|
|
self.fNotice = True
|
|
|
|
|
|
|
|
def ListenForNotice(self, mi):
|
|
|
|
self.rgmiNotice.append(mi)
|
|
|
|
|
|
|
|
def FNoticeFlagged(self):
|
|
|
|
for mi in self.rgmiNotice:
|
|
|
|
if mi.fNotice:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
def FSelectable(self):
|
|
|
|
return True
|
|
|
|
class MiInvisible(Mi):
|
|
|
|
def Height(self, w):
|
|
|
|
return 0
|
|
|
|
def Draw(self, *rgarg):
|
|
|
|
pass
|
|
|
|
def FSelectable(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
miInvisible = MiInvisible()
|
|
|
|
class MiStatic(Mi):
|
|
|
|
def InitPersistent(self, stText):
|
|
|
|
Mi.InitPersistent(self)
|
|
|
|
self.stText = stText
|
|
|
|
def StName(self):
|
|
|
|
return self.stText
|
|
|
|
def FSelectable(self):
|
|
|
|
return False
|
|
|
|
def Height(self, w):
|
|
|
|
return len(RgstWrapped(self.StName(), w, 0))
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
|
|
for st in RgstWrapped(self.StName(), w, 0):
|
|
|
|
ascr.PutSt("{0:^{1}}".format(st, w), x, y, self.ColFg(), self.ColBg(selor))
|
|
|
|
y = y + 1
|
|
|
|
|
|
|
|
class MiDgText(MiStatic):
|
|
|
|
def InitPersistent(self, dgText):
|
|
|
|
MiStatic.InitPersistent(self, None)
|
|
|
|
self.dgText = dgText
|
|
|
|
def StName(self):
|
|
|
|
return self.dgText()
|
|
|
|
class MiButton(MiStatic):
|
|
|
|
def InitPersistent(self, stName, dgCmd, value = None):
|
|
|
|
MiStatic.InitPersistent(self, stName)
|
|
|
|
self.dgCmd = dgCmd
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
def StName(self):
|
|
|
|
if self.stText == None and self.value != None:
|
|
|
|
return self.value.StName()
|
|
|
|
return self.stText
|
|
|
|
|
|
|
|
def Exec(self):
|
|
|
|
return self.dgCmd()
|
|
|
|
|
|
|
|
def FSelectable(self):
|
|
|
|
return True
|
|
|
|
def HandleKey(self, key):
|
|
|
|
if ansi.FEnter(key) or key == ' ':
|
|
|
|
return self.Exec()
|
|
|
|
return False
|
|
|
|
|
|
|
|
def Value(self):
|
|
|
|
return self.value
|
|
|
|
|
2020-07-31 03:01:02 +00:00
|
|
|
class MiButtonConfirm(MiButton):
|
|
|
|
def InitPersistent(self, stName, stConfirm, dgCmd):
|
|
|
|
super(MiButtonConfirm, self).InitPersistent(stName, dgCmd)
|
|
|
|
self.stConfirm = stConfirm
|
|
|
|
self.fConfirming = False
|
|
|
|
|
|
|
|
def StName(self):
|
|
|
|
if self.fConfirming:
|
|
|
|
return self.stConfirm
|
|
|
|
return super(MiButtonConfirm, self).StName()
|
|
|
|
|
|
|
|
def HandleKey(self, key):
|
|
|
|
if ansi.FEnter(key) or key == ' ':
|
|
|
|
if self.fConfirming:
|
|
|
|
self.Exec()
|
|
|
|
else:
|
|
|
|
self.fConfirming = True
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
self.fConfirming = False
|
|
|
|
return False
|
|
|
|
|
|
|
|
def ColBg(self, selor):
|
|
|
|
if self.fConfirming:
|
|
|
|
return ansi.RED
|
|
|
|
return super(MiButtonConfirm, self).ColBg(selor)
|
|
|
|
|
2011-03-19 00:10:02 +00:00
|
|
|
class MiToggle(MiButton):
|
|
|
|
def InitPersistent(self, stName, fInitial = False):
|
|
|
|
MiButton.InitPersistent(self, stName, None, fInitial)
|
|
|
|
|
|
|
|
def Exec(self):
|
|
|
|
self.value = not self.value
|
|
|
|
return True
|
|
|
|
|
|
|
|
def StName(self):
|
|
|
|
stPre = "[X] " if self.value else "[ ] "
|
|
|
|
return stPre + self.stText
|
|
|
|
|
|
|
|
def StKey(self):
|
|
|
|
return self.stText
|
|
|
|
class MiList(Mi):
|
|
|
|
def InitPersistent(self, rgval, ival = 0):
|
|
|
|
Mi.InitPersistent(self)
|
|
|
|
self.rgval = rgval
|
|
|
|
self.ival = ival
|
|
|
|
|
|
|
|
def Value(self):
|
|
|
|
return self.rgval[self.ival]
|
|
|
|
|
|
|
|
def Inc(self):
|
|
|
|
if self.ival < len(self.rgval) - 1:
|
|
|
|
self.ival = self.ival + 1
|
|
|
|
|
|
|
|
def Dec(self):
|
|
|
|
if self.ival > 0:
|
|
|
|
self.ival = self.ival - 1
|
|
|
|
|
|
|
|
def HandleKey(self, key):
|
|
|
|
if key == ansi.K_LEFT:
|
|
|
|
self.Dec()
|
|
|
|
elif key == ansi.K_RIGHT:
|
|
|
|
self.Inc()
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
|
|
xText = w / 4
|
|
|
|
xVal = w / 2
|
|
|
|
ascr.PutSt(self.StName() + ":", xText, y, self.ColFg(), self.ColBg(selor))
|
|
|
|
self.DrawValue(ascr, xVal, y, selor.SelorDeselect())
|
|
|
|
class MiTypein(Mi):
|
|
|
|
def InitPersistent(self, dgEnter, stName = "Typein", pctW = 100):
|
|
|
|
Mi.InitPersistent(self)
|
|
|
|
self.stedit = Stedit(dgEnter)
|
|
|
|
self.stName = stName
|
|
|
|
self.pctW = pctW
|
|
|
|
assert pctW > 0 and pctW <= 100
|
|
|
|
|
|
|
|
def StName(self):
|
|
|
|
return self.stName
|
|
|
|
|
|
|
|
def Value(self):
|
|
|
|
return self.stedit.GetValue()
|
|
|
|
|
|
|
|
def SetValue(self, st):
|
|
|
|
self.stedit.SetValue(st)
|
|
|
|
def HandleKey(self, key):
|
|
|
|
return self.stedit.HandleKey(key)
|
|
|
|
|
|
|
|
def ColBgDefault(self):
|
|
|
|
return ansi.BLACK
|
|
|
|
def ColBgSelected(self, fPri):
|
|
|
|
return ansi.WHITE if fPri else ansi.BLUE
|
|
|
|
def ColFg(self):
|
|
|
|
return ansi.WHITE
|
|
|
|
|
|
|
|
def FIgnoreParent(self):
|
|
|
|
return True
|
|
|
|
def Height(self, wT):
|
|
|
|
return len(RgstWrapped(self.GetStToWrap(), self.GetW(wT)))
|
|
|
|
def GetW(self, wT):
|
|
|
|
return int(wT * (self.pctW / 100.0))
|
|
|
|
def GetStToWrap(self):
|
|
|
|
return self.stedit.StForSize()
|
|
|
|
def Draw(self, ascr, xT, y, wT, selor):
|
|
|
|
w = self.GetW(wT)
|
|
|
|
x = xT + ((wT - w) / 2)
|
|
|
|
if selor.KSel() != Selor.NONE:
|
|
|
|
stToWrap = self.GetStToWrap()
|
|
|
|
else:
|
|
|
|
stToWrap = self.stedit.GetValue()
|
|
|
|
for snippet in RgSnippetWrapped(stToWrap, w):
|
|
|
|
ascr.PutSt("{0:{1}}".format(snippet.st, w), x, y, self.ColFg(), self.ColBg(selor.SelorDeselect()))
|
|
|
|
if selor.KSel() != Selor.NONE:
|
|
|
|
self.stedit.DrawCursor(ascr, x, y, self.ColFg(), self.ColBg(selor), snippet)
|
|
|
|
y = y + 1
|
|
|
|
class MiTab(MiStatic):
|
|
|
|
def InitPersistent(self, stText, mi):
|
|
|
|
MiStatic.InitPersistent(self, stText)
|
|
|
|
self.mi = mi
|
|
|
|
self.ListenForNotice(mi)
|
|
|
|
def Value(self):
|
|
|
|
return self.mi
|
|
|
|
def FSelectable(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def MiTabbed(*rgstTab_mi):
|
|
|
|
miTabRow = MiMenuHoriz([MiTab(*stTab_mi) for stTab_mi in rgstTab_mi])
|
|
|
|
return MiMenu([miTabRow, MiTabBody(miTabRow)])
|
|
|
|
class MiTabBody(Mi):
|
|
|
|
def InitPersistent(self, miMenu):
|
|
|
|
Mi.InitPersistent(self)
|
|
|
|
self.miMenu = miMenu
|
|
|
|
def Mi(self):
|
|
|
|
return self.miMenu.Value()
|
|
|
|
def HandleKey(self, key):
|
|
|
|
return self.Mi().HandleKey(key)
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
|
|
self.Mi().Draw(ascr, x, y, w, selor)
|
|
|
|
def Height(self,w):
|
|
|
|
return self.Mi().Height(w)
|
|
|
|
class MiValue(Mi):
|
|
|
|
def InitPersistent(self, mi):
|
|
|
|
Mi.InitPersistent(self)
|
|
|
|
self.mi = mi
|
|
|
|
def StName(self):
|
|
|
|
return self.mi.StName()
|
|
|
|
def Value(self):
|
|
|
|
return self.mi.Value()
|
|
|
|
def Height(self,w):
|
|
|
|
return self.mi.Height(w)
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
|
|
self.mi.DrawValue(ascr, x, y, selor.SelorChild(self.mi, False))
|
|
|
|
|
|
|
|
class Selection(TPrs):
|
|
|
|
def InitPersistent(self, rgo, o = None, io = 0):
|
|
|
|
self.rgo = rgo
|
|
|
|
self.o = o
|
|
|
|
self.io = -1 if o == None else io
|
|
|
|
self.Validate()
|
|
|
|
|
|
|
|
def Invalidate(self):
|
|
|
|
self.o = None
|
|
|
|
self.io = -1
|
|
|
|
|
|
|
|
def Validate(self):
|
|
|
|
# aw hell, selection only handles inserts, not deletes :(
|
|
|
|
if self.o == None or self.io < 0:
|
|
|
|
self.Invalidate()
|
|
|
|
elif len(self.rgo) <= self.io or self.rgo[self.io] != self.o:
|
|
|
|
try:
|
|
|
|
self.io = self.rgo.index(self.o)
|
|
|
|
except Exception:
|
|
|
|
self.Invalidate()
|
|
|
|
if self.o == None and not self.FEmpty():
|
|
|
|
self.io = IoInc(self.rgo, self.io, self.FSelectableI)
|
|
|
|
self.o = self.rgo[self.io]
|
|
|
|
|
|
|
|
def GetSelected(self):
|
|
|
|
self.Validate()
|
|
|
|
return self.o
|
|
|
|
|
|
|
|
def SetSelected(self, o):
|
|
|
|
self.o = o
|
|
|
|
self.Validate()
|
|
|
|
def ITopScroll(self, h, dgHeight):
|
|
|
|
# requirements:
|
|
|
|
# selection must be visible
|
|
|
|
# if possible, selection should be centered
|
|
|
|
if self.io < 0:
|
|
|
|
return 0
|
|
|
|
iotop = self.io
|
|
|
|
iobot = self.io
|
|
|
|
hCurr = dgHeight(self.o)
|
|
|
|
yobot = hCurr
|
|
|
|
fGrowUp = False
|
|
|
|
while hCurr < h:
|
|
|
|
fDoneUp = iotop == 0
|
|
|
|
fDoneDown = iobot == len(self.rgo) - 1
|
|
|
|
if (fGrowUp or fDoneDown) and not fDoneUp:
|
|
|
|
ho = dgHeight(self.rgo[iotop - 1])
|
|
|
|
if (yobot + ho >= h): # handle case where thing above selected thing is huge and pushes the selection off the screen
|
|
|
|
break
|
|
|
|
iotop = iotop - 1
|
|
|
|
yobot = yobot + ho
|
|
|
|
elif not fDoneDown:
|
|
|
|
ho = dgHeight(self.rgo[iobot + 1])
|
|
|
|
iobot = iobot + 1
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
hCurr = hCurr + ho
|
|
|
|
fGrowUp = not fGrowUp
|
|
|
|
return iotop
|
|
|
|
|
|
|
|
def Inc(self):
|
|
|
|
return self.ChangeI(IoInc)
|
|
|
|
|
|
|
|
def Dec(self):
|
|
|
|
return self.ChangeI(IoDec)
|
|
|
|
|
|
|
|
def FEmpty(self):
|
|
|
|
for o in self.rgo:
|
|
|
|
if self.FSelectableI(o):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def ChangeI(self, fnChange):
|
|
|
|
self.Validate()
|
|
|
|
if self.o != None:
|
|
|
|
ioT = self.io
|
|
|
|
self.io = fnChange(self.rgo, self.io, self.FSelectableI)
|
|
|
|
self.o = self.rgo[self.io]
|
|
|
|
return ioT != self.io
|
|
|
|
|
|
|
|
def FSelectableI(self, o):
|
|
|
|
return o != None and o.FSelectable()
|
|
|
|
|
|
|
|
class MiMenuI(Mi):
|
|
|
|
def InitPersistent(self, rgmi):
|
|
|
|
Mi.InitPersistent(self)
|
|
|
|
self.selMi = Selection(rgmi)
|
|
|
|
self.mpstName_mi = {}
|
|
|
|
for mi in rgmi:
|
|
|
|
if mi != None:
|
|
|
|
self.mpstName_mi[mi.StKey().lower()] = mi
|
|
|
|
def Value(self, stKey = None):
|
|
|
|
if stKey == None:
|
|
|
|
return self.selMi.GetSelected().Value()
|
|
|
|
return self.mpstName_mi[stKey.lower()].Value()
|
|
|
|
def MiByName(self, stKey):
|
|
|
|
return self.mpstName_mi[stKey.lower()]
|
|
|
|
def HeightMi(self, mi, w):
|
|
|
|
if mi == None:
|
|
|
|
return 1
|
|
|
|
return mi.Height(w)
|
|
|
|
def ColBgSelected(self, fPri):
|
|
|
|
return self.ColBgDefault()
|
|
|
|
|
|
|
|
class MiMenu(MiMenuI):
|
|
|
|
def InitPersistent(self, rgmi, hMax = -1):
|
|
|
|
MiMenuI.InitPersistent(self, rgmi)
|
|
|
|
self.hMax = hMax
|
|
|
|
def HandleKey(self, key):
|
|
|
|
mi = self.selMi.GetSelected()
|
|
|
|
if mi == None or not mi.HandleKey(key):
|
|
|
|
if key == ansi.K_DOWN:
|
|
|
|
return self.selMi.Inc()
|
|
|
|
elif key == ansi.K_UP:
|
|
|
|
return self.selMi.Dec()
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def HMax(self, ascr, y):
|
|
|
|
if self.hMax < 0:
|
|
|
|
return (ascr.H() - y) + 1
|
|
|
|
return self.hMax
|
|
|
|
|
|
|
|
def Height(self, w):
|
|
|
|
return sum([self.HeightMi(mi,w) for mi in self.selMi.rgo])
|
|
|
|
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
|
|
ascr.Fill(ansi.MkAch(' ', self.ColFg(), self.ColBg(selor)), w, self.Height(w), x, y)
|
|
|
|
miSelected = self.selMi.GetSelected()
|
|
|
|
h = 0
|
|
|
|
hMax = self.HMax(ascr, y)
|
|
|
|
itop = self.selMi.ITopScroll(hMax, lambda mi: self.HeightMi(mi, w))
|
|
|
|
for imi, mi in enumerate(self.selMi.rgo):
|
|
|
|
if imi < itop:
|
|
|
|
continue
|
|
|
|
if mi != None:
|
|
|
|
mi.DrawWithParent(selor, ascr, x, y + h, w, miSelected == mi)
|
|
|
|
h = h + self.HeightMi(mi, w)
|
|
|
|
if h >= hMax:
|
|
|
|
break
|
|
|
|
class MiMenuHoriz(MiMenuI):
|
|
|
|
def InitPersistent(self, rgmi, rgsize = None):
|
|
|
|
MiMenuI.InitPersistent(self, rgmi)
|
|
|
|
if rgsize == None:
|
|
|
|
rgsize = [1 for mi in rgmi]
|
|
|
|
self.rgsize = rgsize
|
|
|
|
self.sizeMax = float(sum(rgsize))
|
|
|
|
|
|
|
|
def Width(self, imi, w):
|
|
|
|
return int((self.rgsize[imi] / self.sizeMax) * w)
|
|
|
|
|
|
|
|
def HandleKey(self, key):
|
|
|
|
mi = self.selMi.GetSelected()
|
|
|
|
if mi == None or not mi.HandleKey(key):
|
|
|
|
if key == ansi.K_RIGHT:
|
|
|
|
return self.selMi.Inc()
|
|
|
|
elif key == ansi.K_LEFT:
|
|
|
|
return self.selMi.Dec()
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def Height(self, w):
|
|
|
|
return max([self.HeightMi(mi, w) for mi in self.selMi.rgo])
|
|
|
|
def Draw(self, ascr, x, y, w, selor):
|
|
|
|
ascr.Fill(ansi.MkAch(' ', self.ColFg(), self.ColBg(selor)), w, self.Height(w), x, y)
|
|
|
|
miSelected = self.selMi.GetSelected()
|
|
|
|
for imi, mi in enumerate(self.selMi.rgo):
|
|
|
|
wT = self.Width(imi, w)
|
|
|
|
if mi != None:
|
|
|
|
mi.DrawWithParent(selor, ascr, x, y, wT, miSelected == mi)
|
|
|
|
x = x + wT
|
|
|
|
|
2020-07-20 01:31:54 +00:00
|
|
|
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):
|
2011-03-19 00:10:02 +00:00
|
|
|
def DgProject(value):
|
2020-07-20 01:31:54 +00:00
|
|
|
return MiButton(None, lambda: dgExec(value), ProjectedValue(value, dgStName) if dgStName else value)
|
2011-03-19 00:10:02 +00:00
|
|
|
return DgProject
|
|
|
|
|
|
|
|
class RgmiProjected(TPrs):
|
|
|
|
def InitPersistent(self, rgvalue, dgProject):
|
|
|
|
self.rgvalue = rgvalue
|
|
|
|
self.dgProject = dgProject
|
|
|
|
|
|
|
|
def InitTransient(self):
|
|
|
|
self.mpvalue_wrmi = weakref.WeakKeyDictionary()
|
|
|
|
|
|
|
|
def __getitem__(self, i):
|
|
|
|
return self.MiFromValue(self.rgvalue[i])
|
|
|
|
|
|
|
|
def MiFromValue(self, value):
|
|
|
|
if value in self.mpvalue_wrmi:
|
|
|
|
mi = self.mpvalue_wrmi[value]()
|
|
|
|
if mi != None:
|
|
|
|
return mi
|
|
|
|
mi = self.dgProject(value)
|
|
|
|
mi._valueProjected = value
|
|
|
|
self.mpvalue_wrmi[value] = weakref.ref(mi) # this probably gc's much more than is necessary ... :/
|
|
|
|
return mi
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self.rgvalue)
|
|
|
|
|
|
|
|
def index(self, mi):
|
|
|
|
return self.rgvalue.index(mi._valueProjected)
|
|
|
|
|
|
|
|
|
|
|
|
class OvStatic(TokenClient):
|
|
|
|
TOP = 1
|
|
|
|
MIDDLE = 2
|
|
|
|
BOTTOM = 3
|
|
|
|
FILL = 4
|
|
|
|
|
|
|
|
def InitPersistent(self, owner, client, miMenu, kplace, *rgtag):
|
|
|
|
TokenClient.InitPersistent(self, owner, client, "drawable", "menu", *rgtag)
|
|
|
|
self.miMenu = miMenu
|
|
|
|
self.kplace = kplace
|
|
|
|
self.mpst_miSel = {}
|
|
|
|
def SetSelection(self, stSel, mi):
|
|
|
|
self.mpst_miSel[stSel] = mi
|
|
|
|
|
|
|
|
def GetSelection(self, stSel):
|
|
|
|
try:
|
|
|
|
return self.mpst_miSel[stSel]
|
|
|
|
except KeyError:
|
|
|
|
return None
|
|
|
|
def draw(self, ascr, client):
|
|
|
|
self.drawI(ascr, client, False)
|
|
|
|
|
|
|
|
def drawI(self, ascr, client, fSelected):
|
|
|
|
if client == self.client:
|
|
|
|
selor = Selor(self.miMenu, None, self.mpst_miSel.values(), fSelected)
|
|
|
|
if self.kplace & self.FILL:
|
|
|
|
h = ascr.H()
|
|
|
|
y = 1
|
|
|
|
dh = h - self.miMenu.Height(ascr.W())
|
|
|
|
if dh > 0:
|
|
|
|
ascr.Fill(ansi.MkAch(' ', self.miMenu.ColFg(), self.miMenu.ColBg(selor)), ascr.W(), dh, 1, ascr.H() - dh + 1)
|
|
|
|
else:
|
|
|
|
h = self.miMenu.Height(ascr.W())
|
|
|
|
if self.kplace == self.TOP:
|
|
|
|
y = 1
|
|
|
|
elif self.kplace == self.MIDDLE:
|
|
|
|
y = (ascr.H() / 2) - (h / 2)
|
|
|
|
else: # BOTTOM
|
|
|
|
y = ascr.H() - h + 1
|
|
|
|
self.miMenu.Draw(ascr, 1, y, ascr.W(), selor)
|
|
|
|
|
|
|
|
def Value(self, stName = None):
|
|
|
|
return self.miMenu.Value(stName)
|
2020-07-20 01:31:54 +00:00
|
|
|
|
2011-03-19 00:10:02 +00:00
|
|
|
class OvMenu(OvStatic):
|
|
|
|
def run(self):
|
|
|
|
while True:
|
2020-07-31 03:01:02 +00:00
|
|
|
self.miMenu.HandleKey(self.EvKey().receive(self))
|
2011-03-19 00:10:02 +00:00
|
|
|
|
|
|
|
def draw(self, ascr, client):
|
|
|
|
self.drawI(ascr, client, True)
|
2020-07-20 01:31:54 +00:00
|
|
|
|
2011-03-19 00:10:02 +00:00
|
|
|
class OvPopup(OvStatic):
|
|
|
|
def InitPersistent(self, owner, client, miMenu):
|
|
|
|
OvStatic.InitPersistent(self, owner, client, miMenu, self.MIDDLE, "drawable", "menu")
|
|
|
|
self.evShow = Event(self.game)
|
|
|
|
self.evDone = Event(self.game)
|
|
|
|
self.fAwake = False
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
while True:
|
|
|
|
self.evShow.receive(self)
|
|
|
|
self.fAwake = True
|
|
|
|
while self.fAwake:
|
2020-07-31 03:01:02 +00:00
|
|
|
key = self.EvKey().receive(self)
|
2011-03-19 00:10:02 +00:00
|
|
|
if not self.miMenu.HandleKey(key):
|
|
|
|
if key == ansi.K_TAB or key == ansi.K_DEL or key == ' ' or ansi.FEnter(key):
|
|
|
|
self.fAwake = False
|
|
|
|
self.evDone.fire()
|
|
|
|
|
|
|
|
def draw(self, ascr, client):
|
|
|
|
if self.fAwake:
|
|
|
|
self.drawI(ascr, client, True)
|
|
|
|
class MsgScroller(Token):
|
|
|
|
def InitPersistent(self, owner, colFg=ansi.BLUE, colBg=ansi.BLACK, y=config.H):
|
|
|
|
Token.InitPersistent(self, owner, "drawable", "msgscroller", "overlay")
|
|
|
|
self.colFg = colFg
|
|
|
|
self.colBg = colBg
|
|
|
|
self.rgmsg = []
|
|
|
|
self.msg = None
|
|
|
|
self.evPost = Event(self.game)
|
|
|
|
self.y = y
|
|
|
|
|
|
|
|
def postI(self, msg):
|
|
|
|
self.rgmsg.append(msg)
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
while True:
|
|
|
|
self.postI(self.evPost.receive(self))
|
|
|
|
with self.evPost.oob(self, self.postI):
|
|
|
|
while len(self.rgmsg) > 0:
|
|
|
|
self.msg = self.rgmsg[0]
|
|
|
|
self.rgmsg = self.rgmsg[1:]
|
|
|
|
self.x = config.W
|
|
|
|
while self.x > -len(self.msg):
|
|
|
|
self.x = self.x - 1
|
|
|
|
self.wait(30)
|
|
|
|
self.msg = None
|
|
|
|
|
|
|
|
def draw(self, ascr, client):
|
|
|
|
if self.msg:
|
|
|
|
ascr.PutSt(self.msg, self.x, self.y, self.colFg, self.colBg)
|