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 @RegStmt
class SyntPrint(SyntDesc): class SyntPrint(SyntDesc):
desc = [["Show Message"], " \"", SyntText, "\""] desc = [["Broadcast"], " message to all players: \"", SyntText, "\""]
expr = SyntDesc.DefProp(0) expr = SyntDesc.DefProp(0)
def Eval(self, env): def Eval(self, env):
env.GlobalMsg(self.expr.Eval(env)) env.GlobalMsg(self.expr.Eval(env))
@ -463,9 +463,11 @@ class Fail(TPrs):
# projection cursor # projection cursor
class Pcur(object): class Pcur(object):
@staticmethod @staticmethod
def PwProjected(defs, synt): def PwProjected(defs, synt, dgDone):
pcur = Pcur(defs, synt) pcur = Pcur(defs, synt)
synt.Project(pcur) synt.Project(pcur)
PwStatic(pcur.pwVert, ' ')
PwButton(pcur.pwVert, 'Save and go back to editor', defs, dgDone)
return pcur.pwSplit return pcur.pwSplit
def __init__(self, defs, synt): def __init__(self, defs, synt):
@ -909,7 +911,10 @@ class Pov(TokenClient):
self.pselstate = self.game.rgtoken("pselstate")[0] self.pselstate = self.game.rgtoken("pselstate")[0]
def PwProjected(self): 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): def run(self):
while True: while True:
@ -942,7 +947,7 @@ class PselState(LeaveJoinToken):
del self.mpclient_psel[client] del self.mpclient_psel[client]
def OnJoin(self, client): def OnJoin(self, client):
print("client joined,", 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): class Mpksel(object):
def __init__(self, pwRoot, client, mpclient_psel): def __init__(self, pwRoot, client, mpclient_psel):
self.mppw_ksel = {} self.mppw_ksel = {}

View file

@ -21,7 +21,7 @@ from tpers import *
from datetime import * from datetime import *
from util import * from util import *
from scripting import Botdef, Flagdef, Defs, PselState, Pov, Vm 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 telnet
import time import time
import login import login
@ -231,96 +231,6 @@ class Whiteboard(Token):
if versionOld < 5: if versionOld < 5:
self.board = Board(self.game.gameLobby, self.game.stName, self.game.user, rglayer, dt) 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): class Cursor(TokenClient):
def InitPersistent(self, game, client): def InitPersistent(self, game, client):
TokenClient.InitPersistent(self, game, client, "drawable", "cursor") TokenClient.InitPersistent(self, game, client, "drawable", "cursor")
@ -357,7 +267,8 @@ 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]) 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.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]))
@ -384,7 +295,7 @@ class Cursor(TokenClient):
return botdef 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 to ANSI Gallery", 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),
@ -439,12 +350,6 @@ class Cursor(TokenClient):
if not fBlink and self.cDontBlink > 0: if not fBlink and self.cDontBlink > 0:
self.cDontBlink = self.cDontBlink - 1 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): def OnChat(self, chl):
if type(chl) != ChlSys: if type(chl) != ChlSys:
self.miChat.FlagNotice() self.miChat.FlagNotice()
@ -704,97 +609,6 @@ def IchFromCh(ch):
return 9 return 9
return int(ch) - 1 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): class Lobby(TokenClient):
def InitPersistent(self, owner, client): def InitPersistent(self, owner, client):
TokenClient.InitPersistent(self, owner, client) TokenClient.InitPersistent(self, owner, client)
@ -1098,6 +912,9 @@ class GameWB(Game):
self.drawing.chat.AttachToGame(self) self.drawing.chat.AttachToGame(self)
self.rgdgdie.append(self.drawing.chat.DetachFromGame) self.rgdgdie.append(self.drawing.chat.DetachFromGame)
def Chat(self):
return self.drawing.chat
def ClsCursor(self): def ClsCursor(self):
return Cursor return Cursor

218
world.py
View file

@ -14,17 +14,216 @@
# Copyright 2020 Jeremy Penner # Copyright 2020 Jeremy Penner
import ansi_cython as ansi import ansi_cython as ansi
from tpers import TPrs, Partial
from scripting import Vm from scripting import Vm
from basetoken import AutoJoiner, AutoKiller, MsgScroller from basetoken import AutoJoiner, AutoKiller, MsgScroller, Mi, MiButton, MiTypein, MiMenu, OvPopup
from engine import Game, TokenClient from basetoken import LeaveJoinToken, Selor
from util import RgstWrapped
from engine import Game, Token, TokenClient, Event
from datetime import datetime from datetime import datetime
import traceback 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): class Player(TokenClient):
def InitPersistent(self, owner, client): def InitPersistent(self, owner, client):
TokenClient.InitPersistent(self, owner, client, "drawable", "player") TokenClient.InitPersistent(self, owner, client, "drawable", "player")
self.rbot = self.game.vm.AddPlayer() self.rbot = self.game.vm.AddPlayer()
self.vm = self.game.vm 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): def die(self):
self.vm.RemovePlayer(self.rbot) self.vm.RemovePlayer(self.rbot)
@ -42,6 +241,9 @@ class Player(TokenClient):
posNew.Left() posNew.Left()
elif key == ansi.K_RIGHT: elif key == ansi.K_RIGHT:
posNew.Right() posNew.Right()
elif key == ansi.K_TAB:
self.ovPopup.evShow.fire()
self.ovPopup.evDone.receive(self)
if not posNew.Equals(self.rbot.Pos()): if not posNew.Equals(self.rbot.Pos()):
# is there an rbot here? # is there an rbot here?
rgrbotCollide = [rbot for rbot in self.vm.Rgrbot() if rbot != self.rbot and rbot.Pos().Equals(posNew)] 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.board = board
self.vm = Vm(board.defs) self.vm = Vm(board.defs)
self.dtStart = datetime.now() 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): def StName(self):
return self.dtStart.strftime("%b %d, %Y at %I:%M%p") + " [" + str(len(self.RgclientConnected())) + "]" return self.dtStart.strftime("%b %d, %Y at %I:%M%p") + " [" + str(len(self.RgclientConnected())) + "]"
def GetRgclsTokTrans(self): def GetRgclsTokTrans(self):
return [[AutoJoiner, Player], MsgScroller, AutoKiller] return [[AutoJoiner, Player], MsgScroller, Joiner, AutoKiller]
def GetRgtagDraw(self): def GetRgtagDraw(self):
return ["player", "overlay"] return ["player", "overlay"]