diff --git a/ansi_cython.pyx b/ansi_cython.pyx index 1946475..37e4756 100644 --- a/ansi_cython.pyx +++ b/ansi_cython.pyx @@ -238,6 +238,13 @@ class Pos(object): def Right(self): cdef PosI posI = self.i return posI.Right() + def Clone(self): + cdef PosI posI = self.i + return posI.ToPos() + def Equals(self, other): + cdef PosI posIL = self.i + cdef PosI posIR = other.i + return posIL.xz == posIR.xz and posIL.yz == posIR.yz and posIL.w == posIR.w and posIL.h == posIR.h def __setstate__(self, state): self.i = PosI(state["w"], state["h"], state["xz"] + 1, state["yz"] + 1) def __getstate__(self): diff --git a/basetoken.py b/basetoken.py index d2c8682..ca77213 100644 --- a/basetoken.py +++ b/basetoken.py @@ -411,6 +411,33 @@ class MiButton(MiStatic): def Value(self): return self.value +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) + class MiToggle(MiButton): def InitPersistent(self, stName, fInitial = False): MiButton.InitPersistent(self, stName, None, fInitial) @@ -802,7 +829,7 @@ class OvStatic(TokenClient): class OvMenu(OvStatic): def run(self): while True: - self.miMenu.HandleKey(self.client.EvKey(self.game).receive(self)) + self.miMenu.HandleKey(self.EvKey().receive(self)) def draw(self, ascr, client): self.drawI(ascr, client, True) @@ -819,7 +846,7 @@ class OvPopup(OvStatic): self.evShow.receive(self) self.fAwake = True while self.fAwake: - key = self.client.EvKey(self.game).receive(self) + key = self.EvKey().receive(self) 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 diff --git a/engine.py b/engine.py index c1a871c..729ce55 100644 --- a/engine.py +++ b/engine.py @@ -625,3 +625,9 @@ class TokenClient(Token): def FPersist(self): return False + + def Cldg(self): + return self.game.GetCldg(self.client) + + def EvKey(self): + return self.client.EvKey(self.game) diff --git a/scripting.py b/scripting.py index 869c860..5e4bc8b 100644 --- a/scripting.py +++ b/scripting.py @@ -18,6 +18,8 @@ from basetoken import * from tpers import * from util import * from contextlib import contextmanager +import itertools +import random # sobj -- generic hungarian for any object that is usable in scripts # stmt -- statement; a synt that can be used as a line in a SyntBlock @@ -42,9 +44,17 @@ class Flagdef(TPrs): class Rbot(TPrs): def InitPersistent(self, botdef): self.botdef = botdef - self.x = botdef.x - self.y = botdef.y + self.pos = ansi.Pos(x=botdef.x, y=botdef.y) + + def Ach(self): + return self.botdef.ach + + def Pos(self): + return self.pos + def Move(self, pos): + self.pos = pos + class Defs(TPrs): def InitPersistent(self, rgflagdef = None, rgbotdef = None): self.rgflagdef = rgflagdef or [] @@ -57,12 +67,28 @@ class Vm(TPrs): def InitPersistent(self, defs): self.defs = defs self.mpflagdef = { flagdef: flagdef.value for flagdef in defs.rgflagdef } - self.mpbotdef = { botdef: Rbot(botdef) for botdef in defs.rgbotdef } + self.mpbotdef = { botdef: Rbot(botdef) for botdef in defs.rgbotdef if not botdef.fPlayer } + self.rgrbotPlayer = [] + def SetFlag(self, flagdef, fValue): self.mpflagdef[flagdef] = fValue + def FFlagSet(self, flagdef): return self.mpflagdef[flagdef] + def AddPlayer(self): + rgbotdefPlayer = [botdef for botdef in self.defs.rgbotdef if botdef.fPlayer] + botdef = random.choice(rgbotdefPlayer) + rbotPlayer = Rbot(botdef) + self.rgrbotPlayer.append(rbotPlayer) + return rbotPlayer + + def RemovePlayer(self, rbotPlayer): + self.rgrbotPlayer.remove(rbotPlayer) + + def Rgrbot(self): + return itertools.chain(self.mpbotdef.values(), self.rgrbotPlayer) + def Fail(self, stMessage, synt): self.Log(Fail(stMessage, synt)) @@ -883,7 +909,7 @@ class Pov(TokenClient): def run(self): while True: - key = self.client.EvKey(self.game).receive(self) + key = self.EvKey().receive(self) if key == ansi.K_PGDN: self.block.Eval(Vm(self.defs)) psel = self.pselstate.PselByClient(self.client) diff --git a/whiteboard.py b/whiteboard.py index d1b0cc2..598fb4f 100755 --- a/whiteboard.py +++ b/whiteboard.py @@ -20,7 +20,7 @@ from basetoken import * from tpers import * from datetime import * from util import * -from scripting import Botdef, Flagdef, Defs, PselState, Pov +from scripting import Botdef, Flagdef, Defs, PselState, Pov, Vm import telnet import time import login @@ -129,7 +129,10 @@ class Drawing(Ownable): def SetTimestamp(self): self.dtLastModified = datetime.now() - + + def FPlayable(self): + return False + @Version(4) class Board(Drawing): def InitPersistent(self, owner, stName, user, rglayer=None, dtLastModified=None): @@ -142,13 +145,21 @@ class Board(Drawing): def W(self): return config.W def H(self): return config.H + def FPlayable(self): + return len([botdef for botdef in self.defs.rgbotdef if botdef.fPlayer]) > 0 + def draw(self, ascr): for layer in reversed(self.rglayer): ascr.PutAscr(layer.ascr) - def DrawObjectDefs(self, ascr): + def DrawObjectDefs(self, ascr, bgColor = None): for botdef in self.defs.rgbotdef: - ascr.PutAch(botdef.ach, botdef.x, botdef.y) + ach = botdef.ach + if bgColor != None: + oach = ansi.Ach(ach) + oach.bgCol = bgColor + ach = oach.ToAch() + ascr.PutAch(ach, botdef.x, botdef.y) def Save(self): fn = config.DIR_ANSI + "/" + "_".join([str(x) for x in time.localtime()]) @@ -172,6 +183,14 @@ class Board(Drawing): self.defs = Defs() self.layerObj = self.rglayer[0] + def LayerObj(self): + return self.layerObj + + def AchAtPos(self, pos, layer = None): + if layer == None: + layer = self.LayerObj() + return layer.ascr.GetAch(pos.X(), pos.Y()) + class Sprite(Drawing): def InitPersistent(self, owner, stName, user, w, h): self.w = w @@ -346,15 +365,15 @@ class Cursor(TokenClient): self.ovStatus = OvStatic(self, self.client, miStatus, OvStatic.BOTTOM) def StLayerName(self, layer): - return ('[Bot] ' if layer == self.game.drawing.layerObj else '') + layer.StName() + return ('[Bot] ' if layer == self.game.drawing.LayerObj() else '') + layer.StName() def StStatus(self): stStatus = ": Menu" - if self.GetLayer() == self.game.drawing.layerObj: + if self.GetLayer() == self.game.drawing.LayerObj(): botdef = self.BotdefAtPos(self.pos) if botdef: stStatus += " | : Edit bot" - elif self.game.drawing.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y()) != ansi.achBlank: + elif self.game.drawing.AchAtPos(self.pos) != ansi.achBlank: stStatus += " | : Make bot" return stStatus @@ -432,7 +451,7 @@ class Cursor(TokenClient): def run(self): with self.game.drawing.chat.evChat.oob(self, self.OnChat): while True: - key = self.client.EvKey(self.game).receive(self) + key = self.EvKey().receive(self) fDontBlink = True if key == ansi.K_LEFT: self.pos.Left() @@ -510,26 +529,26 @@ class Cursor(TokenClient): return self.fBlink or self.cDontBlink > 0 def EditBot(self): - if self.GetLayer() == self.game.drawing.layerObj: + if self.GetLayer() == self.game.drawing.LayerObj(): botdef = self.BotdefAtPos(self.pos) - ach = self.game.drawing.layerObj.ascr.GetAch(self.pos.X(), self.pos.Y()) + ach = self.game.drawing.AchAtPos(self.pos) fNewBot = ach != ansi.achBlank if not botdef else False if not fNewBot and not botdef: return if fNewBot: botdef = Botdef('Bot ' + str(len(self.game.drawing.defs.rgbotdef) + 1) + ' (' + ansi.Ach(ach).ch + ')', ach, self.pos.X(), self.pos.Y()) + self.game.drawing.defs.rgbotdef.append(botdef) evDone = Event(self.game) - def DgDone(val): - return lambda: evDone.fire(val) - + miBotName = MiTypein(None, "BotName", 80) miBotName.SetValue(botdef.stName) miMenu = MiMenu([None, - MiStatic("Bot Name"), miBotName, None, - MiToggle("Is Player", botdef.fPlayer), None, - MiButton("Save", DgDone(True)), MiButton("Cancel", DgDone(False)), None, - MiButton("Edit Code (live)", lambda: self.client.joinGame(self.game.GameSyntEdit(botdef.syntOnTouch))), None]) + MiStatic("Bot Name"), miBotName, + MiToggle("Is Player", botdef.fPlayer), + MiButton("Edit Code (live)", lambda: self.client.joinGame(self.game.GameSyntEdit(botdef.syntOnTouch))), None, + MiButton("Save", lambda: evDone.fire(True)), None, None, + MiButtonConfirm("Delete", "Hit RETURN again to confirm!", lambda: evDone.fire(False)), None]) ov = OvMenu(self, self.client, miMenu, OvMenu.MIDDLE) try: @@ -539,10 +558,8 @@ class Cursor(TokenClient): if result: botdef.stName = miBotName.Value() botdef.fPlayer = miMenu.MiByName("Is Player").Value() - if fNewBot: - self.game.drawing.defs.rgbotdef.append(botdef) - - + else: + self.game.drawing.defs.rgbotdef.remove(botdef) def draw(self, ascr, client): if self.FDrawCursor(): @@ -553,7 +570,7 @@ class Cursor(TokenClient): chCursor = chr(177) self.Put(chCursor, ascr, *self.XzYzScreenPic()) elif self.fBlink: - self.Put(str(self.client.Cldg(self.game).iclient)[0], ascr, *self.XzYzScreenPic()) + self.Put(str(self.Cldg().iclient)[0], ascr, *self.XzYzScreenPic()) class CursorSprite(Cursor): def InitPersistent(self, game, client): @@ -595,20 +612,28 @@ class CursorSprite(Cursor): if self.blinkerAnimate: return False return Cursor.FDrawCursor(self) + class WhiteboardDraw(TokenClient): def InitPersistent(self, owner, client): - TokenClient.InitPersistent(self, owner, client, "drawable", "solid") + TokenClient.InitPersistent(self, owner, client, "drawable", "solid", "cursor") + self.color = 0 + self.fBlink = False def draw(self, ascr, client): if client == self.client: fDrawObjects = True if self.owner.miLayers.Value("Show One"): ascr.PutAscr(self.owner.GetLayer().ascr) - fDrawObjects = self.owner.GetLayer() == self.owner.layerObj + fDrawObjects = self.owner.GetLayer() == self.owner.LayerObj() else: self.game.drawing.draw(ascr) if fDrawObjects: - self.game.drawing.DrawObjectDefs(ascr) + self.game.drawing.DrawObjectDefs(ascr, self.color if not self.fBlink else None) + + def blinkCursor(self, fBlink): + self.fBlink = fBlink + if fBlink: + self.color = (self.color + 1) % 8 class SpriteDraw(TokenClient): def InitPersistent(self, owner, client): @@ -843,10 +868,16 @@ class LobbyBoard(LobbyI): def RgObj(self): return self.project.rgdrawing + def OnObjSelected(self, board): - self.client.leaveGame(board) + if board.FPlayable(): + self.owner.SwitchToLobby(LobbyWorld(self.owner, self.client, self.project, board)) + else: + self.client.leaveGame((board, self.game.GameFromDrawing(board))) + def NewBoard(self, stName): self.OnObjSelected(self.project.NewBoard(stName, self.client.cld.user)) + def NewSprite(self, stName): try: w = int(self.miDimensions.Value("w")) @@ -855,6 +886,7 @@ class LobbyBoard(LobbyI): self.OnObjSelected(self.project.NewSprite(stName, self.client.cld.user, w, h)) except: pass + def RgstMetadata(self, board): if board != None: return board.RgstMetadata() @@ -868,17 +900,47 @@ class LobbyProject(LobbyI): miTypein, miBtnNew = self.MiTypein_MiButton("New Project", "ProjName", self.New) return MiMenu([None, None, miTypein, miBtnNew, None, None, None, None, None, None, None, None, MiDgText(self.StMetadata)]) - def StNewObj(self): - return "New Project" def RgObj(self): return self.game.rgproject + def OnObjSelected(self, project): self.owner.SwitchToLobby(LobbyBoard(self.owner, self.client, project)) + def New(self, stName): project = self.game.NewProject(stName, self.client.cld.user) self.OnObjSelected(project) + def RgstMetadata(self, project): return project.RgstMetadata() + +class LobbyWorld(LobbyI): + def InitPersistent(self, owner, client, project, board): + self.project = project + self.board = board + LobbyI.InitPersistent(self, owner, client, None, MiButton("<-- Back to drawings", self.Back), None) + + def CreateMiCmds(self): + miBtnNew = MiButton("New Game", self.NewGame) + miBtnEdit = MiButton("Edit board", self.Edit) + return MiMenu([None, None, miBtnNew, None, miBtnEdit, None, None, None, None, MiDgText(self.StMetadata)]) + + def RgObj(self): + return self.game.RggameWorld(self.board) + + def OnObjSelected(self, game): + self.client.leaveGame((self.board, game)) + + def NewGame(self): + self.OnObjSelected(self.game.StartNewWorld(self.board)) + + def Edit(self): + self.client.leaveGame((self.board, self.game.GameFromDrawing(self.board))) + + def Back(self): + self.owner.SwitchToLobby(LobbyBoard(self.owner, self.client, self.project, self.board)) + + def RgstMetadata(self, game): + return [client.cld.user for client in game.RgclientConnected()] class RunnerWB(Runner): def InitPersistent(self): @@ -889,9 +951,9 @@ class RunnerWB(Runner): if (client.joinGame(GameLogin())): client.Cldg(self.gameLobby).drawingSelected = None while True: - drawing = client.joinGame(self.gameLobby) + drawing, game = client.joinGame(self.gameLobby) client.Cldg(self.gameLobby).drawingSelected = drawing - client.joinGame(self.gameLobby.GameFromDrawing(drawing)) + client.joinGame(game) class GameLogin(Game): def GetRgclsTokTrans(self): @@ -929,6 +991,7 @@ class GameLobby(Game): Game.InitTransient(self) self.mpdrawing_game = Mpsubgame(self, self.MakeGameFromDrawing) self.mpdrawingsynt_game = Mpsubgame(self, self.MakeGameSyntEdit) + self.mpboard_rggame = {} def GetRgclsTokTrans(self): return [[AutoJoiner, Lobby]] @@ -955,6 +1018,25 @@ class GameLobby(Game): drawing, synt = drawing_synt return GameSyntEdit(drawing, synt) + def StartNewWorld(self, board): + if board not in self.mpboard_rggame: + self.mpboard_rggame[board] = [] + game = GameWorld(board) + game.rgdgdie.append(Partial(self.OnWorldDie, game)) + self.mpboard_rggame[board].append(game) + return game + + def OnWorldDie(self, game): + rggame = self.mpboard_rggame[game.board] + rggame.remove(game) + if len(rggame) == 0: + del self.mpboard_rggame[game.board] + + def RggameWorld(self, board): + if board in self.mpboard_rggame: + return self.mpboard_rggame[board] + return [] + def CclientDrawing(self, drawing): if drawing in self.mpdrawing_game: return len(self.mpdrawing_game[drawing].RgclientConnected()) @@ -1039,7 +1121,57 @@ class GameSyntEdit(Game): class GameSprEdit(GameWB): def ClsCursor(self): return CursorSprite - + +class Player(TokenClient): + def InitPersistent(self, owner, client): + TokenClient.InitPersistent(self, owner, client, "drawable", "player") + self.rbot = self.game.vm.AddPlayer() + + def die(self): + self.game.vm.RemovePlayer(self.rbot) + TokenClient.die(self) + + def run(self): + while True: + key = self.EvKey().receive(self) + posNew = self.rbot.Pos().Clone() + if key == ansi.K_UP: + posNew.Up() + elif key == ansi.K_DOWN: + posNew.Down() + elif key == ansi.K_LEFT: + posNew.Left() + elif key == ansi.K_RIGHT: + posNew.Right() + if not posNew.Equals(self.rbot.Pos()): + # is there an rbot here? + rgrbotCollide = [rbot for rbot in self.game.vm.Rgrbot() if rbot != self.rbot and rbot.Pos().Equals(posNew)] + if len(rgrbotCollide) > 0: + rgrbotCollide[0].botdef.syntOnTouch.Eval(self.game.vm) + elif self.game.board.AchAtPos(posNew) == ansi.achBlank: + self.rbot.Move(posNew) + + def draw(self, ascr, client): + if client == self.client: + self.game.board.draw(ascr) + for rbot in self.game.vm.Rgrbot(): + ascr.PutAch(rbot.Ach(), rbot.Pos().X(), rbot.Pos().Y()) + +# idea: synchronous event pair to implement "show message" w/ blocking + +class GameWorld(Game): + def InitPersistent(self, board): + Game.InitPersistent(self) + self.board = board + self.vm = Vm(board.defs) + self.dtStart = datetime.now() + + def StName(self): + return self.dtStart.strftime("%b %d, %Y at %I:%M%p") + " [" + str(len(self.RgclientConnected())) + "]" + + def GetRgclsTokTrans(self): + return [[AutoJoiner, Player], AutoKiller] + if __name__ == "__main__": if len(sys.argv) > 1: config.override(sys.argv[1])