polish robot creation, add shared game launching!!!

This commit is contained in:
Jeremy Penner 2020-07-30 23:01:02 -04:00
parent 10edbacf86
commit 6f5e58e777
5 changed files with 235 additions and 37 deletions

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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,8 +44,16 @@ 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):
@ -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)

View file

@ -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
@ -130,6 +130,9 @@ 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 = "<Tab>: Menu"
if self.GetLayer() == self.game.drawing.layerObj:
if self.GetLayer() == self.game.drawing.LayerObj():
botdef = self.BotdefAtPos(self.pos)
if botdef:
stStatus += " | <Enter>: 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 += " | <Enter>: 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,18 +900,48 @@ 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):
self.gameLobby = GameLobby()
@ -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())
@ -1040,6 +1122,56 @@ 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])