From 7cfe7184dc347971fd234748a6efcca7ae9d4445 Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Sat, 1 Aug 2020 22:18:36 -0400 Subject: [PATCH] In-game chat, add quit options to script editor and game world --- scripting.py | 13 ++- whiteboard.py | 197 ++------------------------------------------- world.py | 218 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 231 insertions(+), 197 deletions(-) diff --git a/scripting.py b/scripting.py index 2d81aff..b75fed1 100644 --- a/scripting.py +++ b/scripting.py @@ -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 = {} diff --git a/whiteboard.py b/whiteboard.py index cd12729..b4c69a8 100755 --- a/whiteboard.py +++ b/whiteboard.py @@ -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 diff --git a/world.py b/world.py index c9748d4..0824fd8 100644 --- a/world.py +++ b/world.py @@ -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"]