In-game chat, add quit options to script editor and game world

This commit is contained in:
Jeremy Penner 2020-08-01 22:18:36 -04:00
parent 3dd87784f3
commit 7cfe7184dc
3 changed files with 231 additions and 197 deletions

View file

@ -444,7 +444,7 @@ class SyntSet(SyntDesc):
@RegStmt
class SyntPrint(SyntDesc):
desc = [["Show Message"], " \"", SyntText, "\""]
desc = [["Broadcast"], " message to all players: \"", SyntText, "\""]
expr = SyntDesc.DefProp(0)
def Eval(self, env):
env.GlobalMsg(self.expr.Eval(env))
@ -463,9 +463,11 @@ class Fail(TPrs):
# projection cursor
class Pcur(object):
@staticmethod
def PwProjected(defs, synt):
def PwProjected(defs, synt, dgDone):
pcur = Pcur(defs, synt)
synt.Project(pcur)
PwStatic(pcur.pwVert, ' ')
PwButton(pcur.pwVert, 'Save and go back to editor', defs, dgDone)
return pcur.pwSplit
def __init__(self, defs, synt):
@ -909,8 +911,11 @@ class Pov(TokenClient):
self.pselstate = self.game.rgtoken("pselstate")[0]
def PwProjected(self):
return Pcur.PwProjected(self.defs, self.block)
return Pcur.PwProjected(self.defs, self.block, self.Back)
def Back(self, val, pw):
self.client.leaveGame()
def run(self):
while True:
key = self.EvKey().receive(self)
@ -942,7 +947,7 @@ class PselState(LeaveJoinToken):
del self.mpclient_psel[client]
def OnJoin(self, client):
print("client joined,", client)
self.mpclient_psel[client] = Psel(Pcur.PwProjected(self.defs, self.block).PwFirstSelectable(), Ksel.NAV)
self.mpclient_psel[client] = Psel(Pcur.PwProjected(self.defs, self.block, None).PwFirstSelectable(), Ksel.NAV)
class Mpksel(object):
def __init__(self, pwRoot, client, mpclient_psel):
self.mppw_ksel = {}

View file

@ -21,7 +21,7 @@ from tpers import *
from datetime import *
from util import *
from scripting import Botdef, Flagdef, Defs, PselState, Pov, Vm
from world import GameWorld
from world import GameWorld, Chl, ChlMsg, ChlAct, ChlSys, Chat, MiChat, Joiner
import telnet
import time
import login
@ -230,96 +230,6 @@ class Whiteboard(Token):
del self.rglayer
if versionOld < 5:
self.board = Board(self.game.gameLobby, self.game.stName, self.game.user, rglayer, dt)
class Chl(object):
def __init__(self, client, st):
self.user = client.cld.user
self.st = st.rstrip()
class ChlMsg(Chl):
def StUser(self):
return "<" + self.user + ">"
def Col(self):
return ansi.WHITE
class ChlAct(Chl):
def StUser(self):
return "* " + self.user
def Col(self):
return ansi.MAGENTA
class ChlSys(Chl):
def StUser(self):
return self.user
def Col(self):
return ansi.BLUE
class Chat(TPrs):
def InitTransient(self):
TPrs.InitTransient(self)
self.cchl = 100
self.MakeTransient("game", None)
self.MakeTransient("evChat", None)
def InitPersistent(self):
TPrs.InitPersistent(self)
self.rgchl = []
def AttachToGame(self, game):
with self.SetTransiently():
self.game = game
self.evChat = Event(game)
def DetachFromGame(self):
with self.SetTransiently():
self.game = None
self.evChat = None
def Add(self, chl):
if len(chl.st) > 0:
self.rgchl.insert(0, chl)
if len(self.rgchl) > self.cchl:
self.rgchl = self.rgchl[:self.cchl]
self.evChat.fire(chl)
def CChl(self):
return len(self.rgchl)
def CUsers(self):
return len(self.game.RgclientConnected())
def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0):
yDraw = y + h - 1
for chl in self.rgchl[ichl:]:
stUser = chl.StUser()
colFg = chl.Col()
wMsg = w - len(stUser) - 1
rgstMsg = RgstWrapped(chl.st, wMsg)
rgstMsg.reverse()
for istMsg, stMsg in enumerate(rgstMsg):
if istMsg == len(rgstMsg) - 1:
st = "{0} {1:{2}}".format(stUser, stMsg, wMsg)
else:
st = "{0:{1}} {2:{3}}".format("", len(stUser), stMsg, wMsg)
ascr.PutSt(st, x, yDraw, colFg, colBg)
yDraw = yDraw - 1
if yDraw == y - 1:
return
while yDraw >= y:
ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw - 1
def DrawUsers(self, ascr, x, y, w, h, colBg=ansi.BLACK, iuser=0):
yDraw = y
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(self.game).iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw + 1
if yDraw == y + h:
break
while yDraw < y + h:
ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw + 1
class Cursor(TokenClient):
def InitPersistent(self, game, client):
@ -357,7 +267,8 @@ class Cursor(TokenClient):
miQuit = MiButton("Back to Menu", self.client.leaveGame)
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])
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]))
@ -384,7 +295,7 @@ class Cursor(TokenClient):
return botdef
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 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),
@ -439,12 +350,6 @@ class Cursor(TokenClient):
if not fBlink and self.cDontBlink > 0:
self.cDontBlink = self.cDontBlink - 1
def SendMsg(self, st):
if st.lower().startswith("/me ") and len(st.rstrip()) > 3:
self.game.drawing.chat.Add(ChlAct(self.client, st[4:]))
else:
self.game.drawing.chat.Add(ChlMsg(self.client, st))
def OnChat(self, chl):
if type(chl) != ChlSys:
self.miChat.FlagNotice()
@ -703,97 +608,6 @@ def IchFromCh(ch):
if ch == '0':
return 9
return int(ch) - 1
class MiChat(Mi):
KWNDMSG = 0
KWNDUSERS = 1
def InitPersistent(self, client, height, chat):
Mi.InitPersistent(self)
self.client = client
self.chat = chat
self.height = height
assert height > 2
self.kwnd = self.KWNDMSG
self.imsg = 0
self.iuser = 0
def StName(self):
return "Chat"
def HandleKey(self, key):
if self.kwnd == self.KWNDUSERS:
if key == ansi.K_LEFT:
self.kwnd = self.KWNDMSG
elif (key == 'a' or key == 'A') and self.iuser > 0:
self.iuser = self.iuser - 1
elif (key == 'z' or key == 'Z') and self.iuser < self.chat.CUsers() - 1:
self.iuser = self.iuser + 1
else:
return False
else: #KWNDMSG
if key == ansi.K_RIGHT:
self.kwnd = self.KWNDUSERS
elif (key == 'a' or key == 'A') and self.imsg < self.chat.CChl() - 1:
self.imsg = self.imsg + 1
elif (key == 'z' or key == 'Z') and self.imsg > 0:
self.imsg = self.imsg - 1
else:
return False
return True
def Height(self, w):
return self.height
def ColBgDefault(self):
return ansi.BLACK
def ColBgSelected(self, fPri):
return ansi.BLACK
def ColFg(self):
return ansi.WHITE
def FIgnoreParent(self):
return True
def Draw(self, ascr, x, y, w, selor):
wChat = (w / 4) * 3
wUser = (w / 4) - 1
self.chat.DrawMsg(ascr, x, y, wChat, self.height - 1, self.ColBg(selor), self.imsg)
for yT in range(y, y + self.height - 1):
ascr.PutAch(ansi.MkAch(chr(179)), x + wChat, yT)
ascr.PutSt("{0:{1}<{2}}{3}{0:{1}<{4}}".format("", chr(196), wChat, chr(193), wUser), x, y + self.height - 1)
self.chat.DrawUsers(ascr, x + wChat + 1, y, wUser, self.height - 1, self.ColBg(selor), self.iuser)
if selor.KSel() == Selor.PRI:
if self.kwnd == self.KWNDMSG:
xBar = x + wChat - 1
pct = 1 - (self.imsg / float(self.chat.CChl()))
else: # KWNDUSERS
xBar = x + w - 1
pct = self.iuser / float(self.chat.CUsers())
yBar = int(y + (pct * (self.height - 4)))
ascr.PutAch(ansi.MkAch("A", ansi.RED, ansi.WHITE), xBar, yBar)
ascr.PutAch(ansi.MkAch(chr(186), ansi.RED, ansi.WHITE), xBar, yBar + 1)
ascr.PutAch(ansi.MkAch("Z", ansi.RED, ansi.WHITE), xBar, yBar + 2)
class Joiner(LeaveJoinToken):
def InitPersistent(self, owner):
Token.InitPersistent(self, owner)
self.msgScroller = self.game.rgtoken("msgscroller")[0]
self.board = self.game.drawing
def OnJoin(self, client):
with client.Cldg(self.game).SetTransiently() as cldg:
cldg.iclient = -1
iclient = 1
rgiclient = [clientT.Cldg(self.game).iclient for clientT in self.game.RgclientConnected()]
while iclient in rgiclient:
iclient = iclient + 1
cldg.iclient = iclient
self.msgScroller.evPost.fire(client.cld.user + "(#" + str(cldg.iclient) + ") has joined! :)")
self.board.chat.Add(ChlSys(client, "has joined"))
def OnLeave(self, client):
self.msgScroller.evPost.fire(client.cld.user + " has left! :(")
self.board.chat.Add(ChlSys(client, "has left"))
class Lobby(TokenClient):
def InitPersistent(self, owner, client):
@ -1098,6 +912,9 @@ class GameWB(Game):
self.drawing.chat.AttachToGame(self)
self.rgdgdie.append(self.drawing.chat.DetachFromGame)
def Chat(self):
return self.drawing.chat
def ClsCursor(self):
return Cursor

218
world.py
View file

@ -14,17 +14,216 @@
# Copyright 2020 Jeremy Penner
import ansi_cython as ansi
from tpers import TPrs, Partial
from scripting import Vm
from basetoken import AutoJoiner, AutoKiller, MsgScroller
from engine import Game, TokenClient
from basetoken import AutoJoiner, AutoKiller, MsgScroller, Mi, MiButton, MiTypein, MiMenu, OvPopup
from basetoken import LeaveJoinToken, Selor
from util import RgstWrapped
from engine import Game, Token, TokenClient, Event
from datetime import datetime
import traceback
class Chl(object):
def __init__(self, client, st):
self.user = client.cld.user
self.st = st.rstrip()
class ChlMsg(Chl):
def StUser(self):
return "<" + self.user + ">"
def Col(self):
return ansi.WHITE
class ChlAct(Chl):
def StUser(self):
return "* " + self.user
def Col(self):
return ansi.MAGENTA
class ChlSys(Chl):
def StUser(self):
return self.user
def Col(self):
return ansi.BLUE
class Chat(TPrs):
def InitTransient(self):
TPrs.InitTransient(self)
self.cchl = 100
self.MakeTransient("game", None)
self.MakeTransient("evChat", None)
def InitPersistent(self):
TPrs.InitPersistent(self)
self.rgchl = []
def AttachToGame(self, game):
with self.SetTransiently():
self.game = game
self.evChat = Event(game)
def DetachFromGame(self):
with self.SetTransiently():
self.game = None
self.evChat = None
def Add(self, chl):
if len(chl.st) > 0:
self.rgchl.insert(0, chl)
if len(self.rgchl) > self.cchl:
self.rgchl = self.rgchl[:self.cchl]
self.evChat.fire(chl)
def SendMsg(self, client, st):
if st.lower().startswith("/me ") and len(st.rstrip()) > 3:
self.Add(ChlAct(client, st[4:]))
else:
self.Add(ChlMsg(client, st))
def CChl(self):
return len(self.rgchl)
def CUsers(self):
return len(self.game.RgclientConnected())
def DrawMsg(self, ascr, x, y, w, h, colBg=ansi.BLACK, ichl=0):
yDraw = y + h - 1
for chl in self.rgchl[ichl:]:
stUser = chl.StUser()
colFg = chl.Col()
wMsg = w - len(stUser) - 1
rgstMsg = RgstWrapped(chl.st, wMsg)
rgstMsg.reverse()
for istMsg, stMsg in enumerate(rgstMsg):
if istMsg == len(rgstMsg) - 1:
st = "{0} {1:{2}}".format(stUser, stMsg, wMsg)
else:
st = "{0:{1}} {2:{3}}".format("", len(stUser), stMsg, wMsg)
ascr.PutSt(st, x, yDraw, colFg, colBg)
yDraw = yDraw - 1
if yDraw == y - 1:
return
while yDraw >= y:
ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw - 1
def DrawUsers(self, ascr, x, y, w, h, colBg=ansi.BLACK, iuser=0):
yDraw = y
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(self.game).iclient) + ") " + client.cld.user, w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw + 1
if yDraw == y + h:
break
while yDraw < y + h:
ascr.PutSt("{0:{1}}".format("", w), x, yDraw, ansi.WHITE, colBg)
yDraw = yDraw + 1
class MiChat(Mi):
KWNDMSG = 0
KWNDUSERS = 1
def InitPersistent(self, client, height, chat):
Mi.InitPersistent(self)
self.client = client
self.chat = chat
self.height = height
assert height > 2
self.kwnd = self.KWNDMSG
self.imsg = 0
self.iuser = 0
def StName(self):
return "Chat"
def HandleKey(self, key):
if self.kwnd == self.KWNDUSERS:
if key == ansi.K_LEFT:
self.kwnd = self.KWNDMSG
elif (key == 'a' or key == 'A') and self.iuser > 0:
self.iuser = self.iuser - 1
elif (key == 'z' or key == 'Z') and self.iuser < self.chat.CUsers() - 1:
self.iuser = self.iuser + 1
else:
return False
else: #KWNDMSG
if key == ansi.K_RIGHT:
self.kwnd = self.KWNDUSERS
elif (key == 'a' or key == 'A') and self.imsg < self.chat.CChl() - 1:
self.imsg = self.imsg + 1
elif (key == 'z' or key == 'Z') and self.imsg > 0:
self.imsg = self.imsg - 1
else:
return False
return True
def Height(self, w):
return self.height
def ColBgDefault(self):
return ansi.BLACK
def ColBgSelected(self, fPri):
return ansi.BLACK
def ColFg(self):
return ansi.WHITE
def FIgnoreParent(self):
return True
def Draw(self, ascr, x, y, w, selor):
wChat = (w / 4) * 3
wUser = (w / 4) - 1
self.chat.DrawMsg(ascr, x, y, wChat, self.height - 1, self.ColBg(selor), self.imsg)
for yT in range(y, y + self.height - 1):
ascr.PutAch(ansi.MkAch(chr(179)), x + wChat, yT)
ascr.PutSt("{0:{1}<{2}}{3}{0:{1}<{4}}".format("", chr(196), wChat, chr(193), wUser), x, y + self.height - 1)
self.chat.DrawUsers(ascr, x + wChat + 1, y, wUser, self.height - 1, self.ColBg(selor), self.iuser)
if selor.KSel() == Selor.PRI:
if self.kwnd == self.KWNDMSG:
xBar = x + wChat - 1
pct = 1 - (self.imsg / float(self.chat.CChl()))
else: # KWNDUSERS
xBar = x + w - 1
pct = self.iuser / float(self.chat.CUsers())
yBar = int(y + (pct * (self.height - 4)))
ascr.PutAch(ansi.MkAch("A", ansi.RED, ansi.WHITE), xBar, yBar)
ascr.PutAch(ansi.MkAch(chr(186), ansi.RED, ansi.WHITE), xBar, yBar + 1)
ascr.PutAch(ansi.MkAch("Z", ansi.RED, ansi.WHITE), xBar, yBar + 2)
class Joiner(LeaveJoinToken):
def InitPersistent(self, owner):
Token.InitPersistent(self, owner)
self.msgScroller = self.game.rgtoken("msgscroller")[0]
def OnJoin(self, client):
with client.Cldg(self.game).SetTransiently() as cldg:
cldg.iclient = -1
iclient = 1
rgiclient = [clientT.Cldg(self.game).iclient for clientT in self.game.RgclientConnected()]
while iclient in rgiclient:
iclient = iclient + 1
cldg.iclient = iclient
self.msgScroller.evPost.fire(client.cld.user + "(#" + str(cldg.iclient) + ") has joined! :)")
self.game.Chat().Add(ChlSys(client, "has joined"))
def OnLeave(self, client):
self.msgScroller.evPost.fire(client.cld.user + " has left! :(")
self.game.Chat().Add(ChlSys(client, "has left"))
## TODO:
## - fix numbering
class Player(TokenClient):
def InitPersistent(self, owner, client):
TokenClient.InitPersistent(self, owner, client, "drawable", "player")
self.rbot = self.game.vm.AddPlayer()
self.vm = self.game.vm
chat = self.game.chat
miQuit = MiButton("Leave Game", self.client.leaveGame)
self.mi = MiMenu([None, MiChat(self.client, 13, chat), MiTypein(Partial(chat.SendMsg, self.client)), None, miQuit, None])
self.ovPopup = OvPopup(self, self.client, self.mi)
def die(self):
self.vm.RemovePlayer(self.rbot)
@ -42,6 +241,9 @@ class Player(TokenClient):
posNew.Left()
elif key == ansi.K_RIGHT:
posNew.Right()
elif key == ansi.K_TAB:
self.ovPopup.evShow.fire()
self.ovPopup.evDone.receive(self)
if not posNew.Equals(self.rbot.Pos()):
# is there an rbot here?
rgrbotCollide = [rbot for rbot in self.vm.Rgrbot() if rbot != self.rbot and rbot.Pos().Equals(posNew)]
@ -70,12 +272,22 @@ class GameWorld(Game):
self.board = board
self.vm = Vm(board.defs)
self.dtStart = datetime.now()
self.chat = Chat()
def InitTransient(self):
Game.InitTransient(self)
if not self.FDead():
self.chat.AttachToGame(self)
self.rgdgdie.append(self.chat.DetachFromGame)
def Chat(self):
return self.chat
def StName(self):
return self.dtStart.strftime("%b %d, %Y at %I:%M%p") + " [" + str(len(self.RgclientConnected())) + "]"
def GetRgclsTokTrans(self):
return [[AutoJoiner, Player], MsgScroller, AutoKiller]
return [[AutoJoiner, Player], MsgScroller, Joiner, AutoKiller]
def GetRgtagDraw(self):
return ["player", "overlay"]