summaryrefslogtreecommitdiff
path: root/asciifarm
diff options
context:
space:
mode:
Diffstat (limited to 'asciifarm')
-rw-r--r--asciifarm/charmaps/__init__.py0
-rw-r--r--asciifarm/charmaps/emoji.json10
-rw-r--r--asciifarm/charmaps/fullwidth.json52
-rw-r--r--asciifarm/charmaps/halfwidth.json64
-rw-r--r--asciifarm/client/__init__.py0
-rw-r--r--asciifarm/client/__main__.py16
-rw-r--r--asciifarm/client/commandhandler.py150
-rw-r--r--asciifarm/client/connection.py30
-rw-r--r--asciifarm/client/display.py174
-rw-r--r--asciifarm/client/gameclient.py173
-rw-r--r--asciifarm/client/inputhandler.py98
-rw-r--r--asciifarm/client/layout.xml44
-rw-r--r--asciifarm/client/listselector.py49
-rw-r--r--asciifarm/client/loaders.py81
-rw-r--r--asciifarm/client/main.py65
-rw-r--r--asciifarm/client/parseargs.py66
-rw-r--r--asciifarm/client/paths.py7
-rw-r--r--asciifarm/common/__init__.py0
-rw-r--r--asciifarm/common/messages.py157
-rw-r--r--asciifarm/common/tcommunicate.py32
-rw-r--r--asciifarm/common/utils.py47
-rw-r--r--asciifarm/keybindings/azerty.json14
-rw-r--r--asciifarm/keybindings/default.json42
23 files changed, 0 insertions, 1371 deletions
diff --git a/asciifarm/charmaps/__init__.py b/asciifarm/charmaps/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/asciifarm/charmaps/__init__.py
+++ /dev/null
diff --git a/asciifarm/charmaps/emoji.json b/asciifarm/charmaps/emoji.json
deleted file mode 100644
index 3049816..0000000
--- a/asciifarm/charmaps/emoji.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "templates": ["fullwidth"],
- "mapping":{
- "tree": ["🌳",0,2],
- "stairdown": ["↘",7],
- "stairup": ["↖",7],
- "player": ["🙂",15],
- "sword": ["🗡️", 7]
- }
-}
diff --git a/asciifarm/charmaps/fullwidth.json b/asciifarm/charmaps/fullwidth.json
deleted file mode 100644
index 6d912fe..0000000
--- a/asciifarm/charmaps/fullwidth.json
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "templates": ["halfwidth"],
- "mapping":{
- "tree": ["T",0,2],
- "wall": ["#",7,8],
- "builtwall": ["+",7,8],
- "rock": ["#",8,8],
- "stone": ["o",7],
- "pebble": ["*",7],
- "player": ["@",15],
- "ground": [".",3],
- "grass1": [",",2],
- "grass2": ["'",2],
- "grass3": ["`",10],
- "rabbit": ["b",15],
- "water": ["~",4,4],
- "floor": [".",8],
- "portal": ["$", 5],
- "stairdown": [">",7],
- "stairup": ["<",7],
- "dummy": ["d",3],
- "spikes": ["^",7],
- "goblin": ["g",2],
- "seed": [":",10],
- "plantedseed": [".",10],
- "seedling": [",",10],
- "youngplant": ["v",10],
- "smallplant": ["v",2],
- "plant": ["Y",2],
- "food": ["8",13],
- "troll": ["T",1],
- "sword": ["/", 7],
- "club": ["!", 3],
- "house": ["^", 3, 1],
- "bridge": ["=", 9,3],
- "smallbridge": ["=", 3,4],
- "entry": ["%", 7],
- "fence": ["#", 3],
- "armour": ["[", 7],
- "wound": [" ", 7, 1],
- "rat": ["r",7],
- "closeddoor": ["+",1 ,3],
- "opendoor": [""",3],
- "raindrop": [":",12],
- "snowflake": ["*",15],
- " ": [" ", 7]
- },
- "default": ["?", 7],
- "charwidth": 2,
- "alphabet":
- "!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
-}
diff --git a/asciifarm/charmaps/halfwidth.json b/asciifarm/charmaps/halfwidth.json
deleted file mode 100644
index d3e16f8..0000000
--- a/asciifarm/charmaps/halfwidth.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "mapping":{
- "tree": ["T", 0, 2],
- "wall": ["#", 7, 8],
- "builtwall": ["+", 7, 8],
- "rock": ["#", 8, 8],
- "stone": ["o", 7],
- "pebble": ["*", 7],
- "player": ["@", 15],
- "ground": [".", 3],
- "grass1": [",", 2],
- "grass2": ["'", 2],
- "grass3": ["`", 10],
- "water": ["~", 4, 4],
- "rabbit": ["b", 15],
- "floor": [".", 8],
- "portal": ["$", 5],
- "stairdown": [">",7],
- "stairup": ["<",7],
- "dummy": ["d", 3],
- "spikes": ["^",7],
- "goblin": ["g",2],
- "seed": [":",10],
- "plantedseed": [".",10],
- "seedling": [",", 10],
- "youngplant": ["v", 10],
- "smallplant": ["v", 2],
- "plant": ["Y",2],
- "food": ["8",13],
- "troll": ["T",1],
- "sword": ["/",7],
- "club": ["!", 3],
- "house": ["^", 3, 1],
- "bridge": ["=", 9,3],
- "smallbridge": ["=", 3,4],
- "entry": ["%", 7],
- "fence": ["#", 3],
- "armour": ["[", 7],
- "wound": [" ", 7, 1],
- "rat": ["r", 7],
- "closeddoor": ["+",1, 3],
- "opendoor": ["\"", 3],
- "raindrop": [":", 12],
- "snowflake": ["*", 15],
- " ": [" ", 7]
- },
- "writable": {
- "engravedwall": [15, 8],
- "emptyletter": [7, 0]
- },
- "default": ["?", 7],
- "charwidth": 1,
- "healthfull": ["#", 7, 2],
- "healthempty": ["_", 7, 1],
- "msgcolours": {
- "chat": [15,0],
- "attack": [12,0],
- "damage": [9,0],
- "heal": [10,0],
- "connect": [3,0],
- "server": [11,0]
- },
- "alphabet": "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
-}
diff --git a/asciifarm/client/__init__.py b/asciifarm/client/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/asciifarm/client/__init__.py
+++ /dev/null
diff --git a/asciifarm/client/__main__.py b/asciifarm/client/__main__.py
deleted file mode 100644
index 2841ae9..0000000
--- a/asciifarm/client/__main__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-import sys
-
-if sys.version_info[0] < 3:
- print("This game is written in python 3.\nRun 'python3 "+sys.argv[0]+"' or './"+sys.argv[0]+"'")
- sys.exit(-1)
-
-if __package__ == "asciifarm.client":
- from . import main
-else:
- import os.path
- sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
- from asciifarm.client import main
-
-main.main()
diff --git a/asciifarm/client/commandhandler.py b/asciifarm/client/commandhandler.py
deleted file mode 100644
index ec5baa3..0000000
--- a/asciifarm/client/commandhandler.py
+++ /dev/null
@@ -1,150 +0,0 @@
-
-import json
-
-try:
- import hy
-except ImportError as e:
- hy = None
- hyErr = e
-
-class InvalidCommandException(Exception):
- pass
-
-
-class CommandHandler:
-
- def __init__(self, client):
- self.client = client
-
- self.commands = {
- "input": self.input,
- "move": self.move,
- "say": self.say,
- "pick": self.pick,
- "chat": self.chat,
- "log": self.log,
- "do": self.do,
- "runinput": self.runInput,
- "selectwidget": self.selectWidget,
- "selectitem": self.selectItem,
- "inputwithselected": self.actWithSelected,
- "use": self.useSelected,
- "drop": self.dropSelected,
- "take": self.takeSelected,
- "eval": self.eval,
- "exec": self.exec,
- "scrollchat": self.scrollChat,
- "json": self.json,
- "j": self.json,
- "ijson": self.ijson,
- "ij": self.ijson,
- "hy": self.hy
- }
-
- self.evalArgs = {
- "self": self,
- "client": self.client,
- "connection": self.client.connection,
- "display": self.client.display,
- "print": self.log
- }
-
- def execute(self, action):
- if action is None:
- return
- if isinstance(action[0], str):
- command = action[0]
- if command in self.commands:
- self.commands[command](*action[1:])
- else:
- raise InvalidCommandException("Invalid command '{}'".format(command))
- else:
- raise Exception("Command should be a string")
-
-
- # Commands
-
- def input(self, action):
- self.client.sendInput(action)
-
- def move(self, direction):
- self.input(["move", direction])
-
- def say(self, text):
- self.input(["say", text])
-
- def pick(self, option):
- self.input(["interact", [None, "north", "south", "east", "west"], option])
-
- def chat(self, text):
- self.client.sendChat( text)
-
-
- def log(self, text):
- self.client.log(text)
-
- def do(self, actions):
- for action in actions:
- self.execute(action)
-
- def runInput(self, startText=""):
- self.client.inputHandler.startTyping(startText)
-
- def selectWidget(self, value, relative=False, modular=False):
- self.client.display.selectMenu(value, relative, modular)
-
- def selectItem(self, value, relative=False, modular=False):
- self.client.display.selectItem(None, value, relative, modular)
-
- def actWithSelected(self, action, menu):
- self.input([action, self.client.display.getSelectedItem(menu).getSelected()])
-
- def useSelected(self):
- menu = self.client.display.getSelectedMenu()
- selected = self.client.display.getSelectedItem(menu)
- if menu in ("inventory", "equipment"):
- self.input(["use", menu, selected])
- elif menu == "ground":
- self.input(["interact", selected])
-
- def dropSelected(self):
- menu = self.client.display.getSelectedMenu()
- selected = self.client.display.getSelectedItem(menu)
- if menu == "inventory":
- action = "drop"
- else:
- return
- self.input([action, selected])
-
- def takeSelected(self):
- menu = self.client.display.getSelectedMenu()
- selected = self.client.display.getSelectedItem(menu)
- if menu == "ground":
- action = "take"
- else:
- return
- self.input([action, selected])
-
- def eval(self, text):
- self.log(eval(text, self.evalArgs))
-
- def exec(self, text):
- exec(text, self.evalArgs)
-
- def hy(self, code):
- if hy is None:
- self.log(hyErr)
- return
- expr = hy.read_str(code)
- self.log(hy.eval(expr, self.evalArgs))
-
- def scrollChat(self, lines):
- self.client.display.scrollBack(lines)
-
- def json(self, text):
- self.execute(json.loads(text))
-
- def ijson(self, text):
- self.input(json.loads(text))
-
-
diff --git a/asciifarm/client/connection.py b/asciifarm/client/connection.py
deleted file mode 100644
index 0a188c3..0000000
--- a/asciifarm/client/connection.py
+++ /dev/null
@@ -1,30 +0,0 @@
-
-import socket
-
-from asciifarm.common.tcommunicate import send, receive
-
-class Connection:
-
- def __init__(self, socketType):
- if socketType == "abstract" or socketType == "unix":
- sockType = socket.AF_UNIX
- elif socketType == "inet":
- sockType = socket.AF_INET
- else:
- raise ValueError("Invalid socket type: %r" % (socketType,))
- self.sock = socket.socket(sockType, socket.SOCK_STREAM)
-
- def connect(self, address):
- self.sock.connect(address)
-
- def listen(self, callback, onError):
- while True:
- try:
- data = receive(self.sock)
- except Exception as err:
- onError(err)
- else:
- callback(data)
-
- def send(self, message):
- send(self.sock, message)
diff --git a/asciifarm/client/display.py b/asciifarm/client/display.py
deleted file mode 100644
index edd5acb..0000000
--- a/asciifarm/client/display.py
+++ /dev/null
@@ -1,174 +0,0 @@
-
-
-
-import os
-from ratuil.layout import Layout
-from ratuil.bufferedscreen import BufferedScreen as Screen
-#from ratuil.screen import Screen
-from ratuil.textstyle import TextStyle
-from asciifarm.common.utils import get
-from .listselector import ListSelector
-
-
-SIDEWIDTH = 20
-
-ALPHABET = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
-
-class Display:
-
- def __init__(self, charMap):
-
- self.characters = {}
-
- def parseSprite(sprite):
- if isinstance(sprite, str):
- return (sprite, None, None)
- char = get(sprite, 0, " ")
- fg = get(sprite, 1)
- bg = get(sprite, 2)
- return (char, fg, bg)
- for name, sprite in charMap["mapping"].items():
- vals = parseSprite(sprite)
- if vals:
- self.characters[name] = vals
-
- for name, colours in charMap.get("writable", {}).items():
- fg = get(colours, 0)
- bg = get(colours, 1)
- for i in range(min(len(ALPHABET), len(charMap.get("alphabet", [])))):
- self.characters[name + '-' + ALPHABET[i]] = (charMap["alphabet"][i], fg, bg)
-
- self.defaultChar = parseSprite(charMap.get("default", "?"))
-
- self.messageColours = charMap.get("msgcolours", {})
-
- fname = os.path.join(os.path.dirname(__file__), "layout.xml")
- self.layout = Layout.from_xml_file(fname)
- self.layout.get("field").set_char_size(charMap.get("charwidth", 1))
-
- self.screen = Screen()
- self.screen.clear()
-
- self.layout.set_target(self.screen)
- self.layout.update()
-
-
-
- # temporary, until these have a better place
- self.inventory = ListSelector(self.getWidget("inventory"))
- self.inventory._debug_name = "inventory"
- self.equipment = ListSelector(self.getWidget("equipment"))
- self.equipment._debug_name = "equipment"
- self.ground = ListSelector(self.getWidget("ground"))
- self.ground._debug_name = "ground"
- self.switch = ListSelector(self.getWidget("switchtitles"))
- self.switch._debug_name = "switch"
-
- self.switch.setItems(["inventory", "equipment", "ground"])
- self.menus = {
- "inventory": self.inventory,
- "equipment": self.equipment,
- "ground": self.ground
- }
-
- self.layout.get("switch").select(0)
-
-
- def getWidget(self, name):
- return self.layout.get(name)
-
- def resizeField(self, size):
- self.getWidget("field").set_size(*size)
- self.getWidget("fieldbackground").change()
-
- def drawFieldCells(self, cells):
- field = self.getWidget("field")
- for cell in cells:
- (x, y), spriteNames = cell
- if not len(spriteNames):
- char, fg, bg = self.getChar(' ')
- else:
- char, fg, bg = self.getChar(spriteNames[0])
- for spriteName in spriteNames[1:]:
- if bg is not None:
- break
- _char, _fg, bg = self.getChar(spriteName)
- field.change_cell(x, y, char, TextStyle(fg, bg))
-
-
- def setFieldCenter(self, pos):
- self.getWidget("field").set_center(*pos)
-
- def setHealth(self, health, maxHealth):
- if health is None:
- health = 0
- if maxHealth is None:
- maxHealth = 0
- self.getWidget("health").set_total(maxHealth)
- self.getWidget("health").set_filled(health)
- self.getWidget("healthtitle").format({"filled": health, "total":maxHealth})
-
-
- def showInfo(self, infostring):
- self.getWidget("info").set_text(infostring)
-
- def selectMenu(self, *args, **kwargs):
- self.switch.select(*args, **kwargs)
- self.layout.get("switch").select(self.getSelectedMenu())
-
- def getSelectedMenu(self):
- return self.switch.getSelectedItem()
-
- def getSelectedItem(self, menu=None):
- return self._getMenu(menu).getSelected()
-
- def selectItem(self, menu=None, *args, **kwargs):
- self._getMenu(menu).select(*args, **kwargs)
-
- def _getMenu(self, name=None):
- if name is None:
- name = self.getSelectedMenu()
- name = name.casefold()
- return self.menus[name]
-
- def setInventory(self, items):
- self.inventory.setItems(items)
-
-
- def setEquipment(self, slots):
- self.equipment.setItems([
- slot + ": " + (item if item else "")
- for slot, item in slots
- ])
-
- def setGround(self, items):
- self.ground.setItems(items)
-
-
- def addMessage(self, message, msgtype=None):
- if msgtype is not None:
- style = TextStyle(*self.messageColours.get(msgtype, (7,0)))
- else:
- style = None
- self.getWidget("msg").add_message(message, style)
-
- def log(self, message):
- self.addMessage(str(message))
-
- def scrollBack(self, amount, relative=True):
- self.getWidget("msg").scroll(amount, relative)
-
- def setInputString(self, string, cursor):
- self.getWidget("textinput").set_text(string, cursor)
-
- def update(self):
- self.layout.update()
- self.screen.update()
-
- def getChar(self, sprite):
- """This returns the character belonging to some spritename. This does not read a character"""
- return self.characters.get(sprite, self.defaultChar)
-
- def update_size(self):
- self.screen.reset()
-
diff --git a/asciifarm/client/gameclient.py b/asciifarm/client/gameclient.py
deleted file mode 100644
index c0f702c..0000000
--- a/asciifarm/client/gameclient.py
+++ /dev/null
@@ -1,173 +0,0 @@
-
-
-import os
-import sys
-
-import threading
-import json
-import getpass
-import argparse
-import string
-from queue import Queue
-
-import ratuil.inputs
-
-from .inputhandler import InputHandler
-from asciifarm.common import messages
-
-class Client:
-
- def __init__(self, display, name, connection, keybindings, logFile=None):
-
- self.display = display
- self.name = name
- self.keepalive = True
- self.connection = connection
- self.logFile = logFile
- self.closeMessage = None
-
- self.inputHandler = InputHandler(self, keybindings["actions"])
-
- self.controlsString = keybindings.get("help", "")
-
- self.display.showInfo(self.controlsString)
- self.queue = Queue()
-
-
- def sendMessage(self, message):
- self.connection.send(message.to_json_bytes())
-
- def sendInput(self, inp):
- message = messages.InputMessage(inp)
- self.sendMessage(message)
-
- def sendChat(self, text):
- try:
- self.sendMessage(messages.ChatMessage(text))
- except messages.InvalidMessageError as e:
- self.log(e.description)
-
- def start(self):
- self.sendMessage(messages.NameMessage(self.name))
- threading.Thread(target=self.listen, daemon=True).start()
- threading.Thread(target=self.getInput, daemon=True).start()
-
- self.command_loop()
-
- def listen(self):
- self.connection.listen(self.pushMessage, self.onConnectionError)
-
- def pushMessage(self, databytes):
- self.queue.put(("message", databytes))
-
- def onConnectionError(self, error):
- self.queue.put(("error", error))
-
- def getInput(self):
- while True:
- key = ratuil.inputs.get_key()
- self.queue.put(("input", key))
-
- def close(self, msg=None):
- self.keepalive = False
- self.closeMessage = msg
-
-
- def update(self, databytes):
- if len(databytes) == 0:
- self.close("Connection closed by server")
- return
- datastr = databytes.decode('utf-8')
- msg = json.loads(datastr)
- message = messages.messages[msg[0]].from_json(msg)
- if isinstance(message, messages.ErrorMessage):
- error = message.errType
- if error == "nametaken":
- self.close("error: name is already taken")
- return
- if error == "invalidname":
- self.close("Invalid name error: "+ str(message.description))
- return
- self.log(message.errType + ": " + message.description)
- elif isinstance(message, messages.MessageMessage):
- self.log(message.text, message.type)
- elif isinstance(message, messages.WorldMessage):
- for msg in message.updates:
- self.handleWorldUpdate(msg)
-
- def handleWorldUpdate(self, msg):
- msgType = msg[0]
- if msgType == 'field':
- field = msg[1]
- fieldWidth = field['width']
- fieldHeight = field['height']
- self.display.resizeField((fieldWidth, fieldHeight))
- fieldCells = field['field']
- mapping = field['mapping']
- self.display.drawFieldCells(
- (
- tuple(reversed(divmod(i, fieldWidth))),
- mapping[spr]
- )
- for i, spr in enumerate(fieldCells))
-
- if msgType == 'changecells' and len(msg[1]):
- self.display.drawFieldCells(msg[1])
-
- if msgType == "playerpos":
- self.display.setFieldCenter(msg[1])
-
- if msgType == "health":
- health, maxHealth = msg[1]
- self.display.setHealth(health, maxHealth)
- if maxHealth is None:
- self.log("You have died. Restart the client to respawn")
- if msgType == "inventory":
- self.display.setInventory(msg[1])
- if msgType == "equipment":
- self.display.setEquipment(msg[1])
- if msgType == "ground":
- self.display.setGround(msg[1])
- if msgType == "message":
- type, text = msg[1][:2]
- self.log(text, type)
- if msgType == "options":
- if msg[1] != None:
- description, options = msg[1]
- self.log(description)
- for option in options:
- self.log(option)
-
-
- def log(self, text, type=None):
- if not isinstance(text, str):
- text = str(text)
- self.display.addMessage(text, type)
- if self.logFile:
- with(open(self.logFile, 'a')) as f:
- f.write("[{}] {}\n".format(type or "", text))
-
-
- def command_loop(self):
- while self.keepalive:
- self.display.update()
- action = self.queue.get()
- if action[0] == "message":
- self.update(action[1])
- elif action[0] == "input":
- if action[1] == "^C":
- raise KeyboardInterrupt
- self.inputHandler.onInput(action[1])
- elif action[0] == "error":
- raise action[1]
- elif action[0] == "sigwinch":
- self.display.update_size()
- else:
- raise Exception("invalid action in queue")
-
- def onSigwinch(self, signum, frame):
- self.queue.put(("sigwinch", (signum, frame)))
-
-
-
-
diff --git a/asciifarm/client/inputhandler.py b/asciifarm/client/inputhandler.py
deleted file mode 100644
index 4281c01..0000000
--- a/asciifarm/client/inputhandler.py
+++ /dev/null
@@ -1,98 +0,0 @@
-
-import string
-
-from .commandhandler import CommandHandler, InvalidCommandException
-
-import ratuil.inputs as inp
-
-
-class InputHandler:
-
- def __init__(self, client, keybindings):
- self.client = client
- self.keybindings = keybindings
- self.commandHandler = CommandHandler(self.client)
-
- self.typing = False
- self.string = ""
- self.cursor = 0
-
-
- def onInput(self, key):
- if not self.typing:
- keyName = key
- if keyName in self.keybindings:
- self.commandHandler.execute(self.keybindings[keyName])
- else:
- self.addKey(key)
-
-
- def processString(self, message):
- if message:
- if message[0] == '/':
- if len(message) == 1:
- return
- if message[1] == '/':
- self.commandHandler.chat(message[1:])
- else:
- try:
- command, _sep, arg = message[1:].partition(' ')
- try:
- self.commandHandler.execute([command, arg])
- except Exception as e:
- self.log(e)
- except InvalidCommandException as e:
- self.client.log(", ".join(e.args))
- else:
- self.commandHandler.chat(message)
-
- def startTyping(self, startText=""):
- self.typing = True
- if startText and not self.string:
- self.string = startText
- self.cursor = len(self.string)
-
- self.showString()
-
- def showString(self):
- self.client.display.setInputString(self.string, self.cursor if self.typing else None)
-
- def addKey(self, key):
- if key == inp.BACKSPACE:
- self.string = self.string[:self.cursor-1] + self.string[self.cursor:]
- self.cursor = max(self.cursor - 1, 0)
- elif key == inp.RIGHT:
- self.cursor = min(self.cursor + 1, len(self.string))
- elif key == inp.LEFT:
- self.cursor = max(self.cursor - 1, 0)
- elif key == inp.DELETE:
- self.string = self.string[:self.cursor] + self.string[self.cursor+1:]
- elif key == inp.HOME:
- self.cursor = 0
- elif key == inp.END:
- self.cursor = len(self.string)
-
- elif key == inp.ESCAPE:
- # throw away entered string and go back to game
- self.typing = False
- self.string = ""
- self.cursor = 0
- elif key == inp.ENTER:
- # process entered string and reset it
- message = self.string
- self.string = ""
- self.cursor = 0
- self.typing = False
- self.processString(message)
- elif key == "^I": # tab
- # return to game but keep entered string
- self.typing = False
- elif key.isprintable() and len(key) == 1:
- self.string = self.string[:self.cursor] + key + self.string[self.cursor:]
- self.cursor += len(key)
-
- self.showString()
-
-
-
-
diff --git a/asciifarm/client/layout.xml b/asciifarm/client/layout.xml
deleted file mode 100644
index d50ecba..0000000
--- a/asciifarm/client/layout.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0"?>
-<hbox>
- <vbox width="20" align="right">
- <textbox id="healthtitle" height="1" format="true">Health ({filled}/{total})</textbox>
- <bar id="health" height="1" full-char="#" empty-char="_" full-style="fg:2; bg:2" empty-style="fg:1; bg: 1;"></bar>
- <listing id="switchtitles" height="0"></listing>
- <switchbox id="switch" height="50%">
- <vbox key="inventory">
- <textbox height="1">Inventory:</textbox>
- <listing id="inventory">
- milk
- eggs
- bread
- </listing>
- </vbox>
- <vbox key="equipment">
- <textbox height="1">Equipment:</textbox>
- <listing id="equipment">
- cotton underwear
- cotton shirt
- jeans
- friendship bracelet
- </listing>
- </vbox>
- <vbox key="ground">
- <textbox height="1">Ground:</textbox>
- <listing id="ground">
- concrete
- </listing>
- </vbox>
- </switchbox>
- <textbox id="info" wrap="words"></textbox>
- </vbox>
- <vbox>
- <textinput id="textinput" align="bottom" height="1">hello</textinput>
- <log id="msg" align="bottom" height="20%%">
- Welcome to asciifarm
- </log>
- <overlay>
- <fill id="fieldbackground" char=" "></fill>
- <field id="field"></field>
- </overlay>
- </vbox>
-</hbox>
diff --git a/asciifarm/client/listselector.py b/asciifarm/client/listselector.py
deleted file mode 100644
index b88b967..0000000
--- a/asciifarm/client/listselector.py
+++ /dev/null
@@ -1,49 +0,0 @@
-
-from asciifarm.common import utils
-
-
-class ListSelector:
-
- def __init__(self, widget):
- self.widget = widget
- self.items = []
- self.selector = 0
-
- def getSelected(self):
- return self.selector
-
- def select(self, value, relative=False, modular=False):
- invLen = len(self.items)
- if relative:
- value += self.selector
- if modular and invLen:
- value %= invLen
- if value < 0:
- value = 0
- if value >= invLen:
- value = invLen-1
- if value in range(invLen):
- self.doSelect(value)
-
- def doSelect(self, value):
- self.selector = value
- self.widget.select(value)
-
- def setItems(self, items):
- self.items = items
- self.selector = utils.clamp(self.selector, 0, len(items)-1)
- self.widget.set_items([self.itemName(item) for item in self.items])
- self.widget.select(self.selector)
-
- def getItem(self, num):
- return self.items[num]
-
- def getSelectedItem(self):
- return self.getItem(self.getSelected())
-
- def getNumItems(self):
- return len(self.items)
-
- def itemName(self, item):
- return item
-
diff --git a/asciifarm/client/loaders.py b/asciifarm/client/loaders.py
deleted file mode 100644
index efdd1c0..0000000
--- a/asciifarm/client/loaders.py
+++ /dev/null
@@ -1,81 +0,0 @@
-
-import os
-
-from .paths import keybindingsPath, charmapPath
-import json
-
-
-standardKeyFiles = {
- "default": os.path.join(keybindingsPath, "default.json"),
- "azerty": os.path.join(keybindingsPath, "azerty.json")
-}
-
-def loadKeybindings(name):
- fname = None
- if name in standardKeyFiles:
- fname = standardKeyFiles[name]
- else:
- fname = name
- with open(fname) as f:
- data = json.load(f)
- bindings = {}
- help = ""
- for ftemplate in data.get("templates", []):
- if ftemplate.partition(os.sep)[0] in {".", ".."}:
- ftemplate = os.path.relpath(ftemplate, fname)
- template = loadKeybindings(ftemplate)
- bindings.update(template.get("actions", {}))
- help = template.get("help", help)
- bindings.update(data.get("actions", {}))
- help = data.get("help", help)
- return {"actions": bindings, "help": help}
-
-
-standardCharFiles = {name: os.path.join(charmapPath, file) for name, file in {
- "default": "fullwidth.json",
- "halfwidth": "halfwidth.json",
- "hw": "halfwidth.json",
- "fullwidth": "fullwidth.json",
- "fw": "fullwidth.json",
- "emoji": "emoji.json"
-}.items()}
-
-def loadCharmap(name):
- fname = None
- if name in standardCharFiles:
- fname = standardCharFiles[name]
- else:
- fname = name
- with open(fname) as f:
- data = json.load(f)
-
- templates = []
- for ftemplate in data.get("templates", []):
- if ftemplate.partition(os.sep)[0] in {".", ".."}:
- ftemplate = os.path.relpath(ftemplate, fname)
- templates.append(loadCharmap(ftemplate))
-
- templates.append(data)
-
- mapping = {}
- writable = {}
- default = None
- charwidth = 1
- alphabet = ""
- msgcolours = {}
-
- for template in templates:
- mapping.update(template.get("mapping", {}))
- writable.update(template.get("writable", {}))
- default = template.get("default", default)
- charwidth = template.get("charwidth", charwidth)
- alphabet = template.get("alphabet", alphabet)
- msgcolours.update(template.get("msgcolours", {}))
- return {
- "mapping": mapping,
- "writable": writable,
- "default": default,
- "charwidth": charwidth,
- "alphabet": alphabet,
- "msgcolours": msgcolours
- }
diff --git a/asciifarm/client/main.py b/asciifarm/client/main.py
deleted file mode 100644
index d720477..0000000
--- a/asciifarm/client/main.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#! /usr/bin/python3
-
-import json
-
-import sys
-import termios
-import tty
-import signal
-#import os
-
-from .connection import Connection
-from .gameclient import Client
-from .display import Display
-from .parseargs import parse_args
-from ratuil.screen import Screen
-
-def main(argv=None):
-
- (name, socketType, address, keybindings, characters, colours, logfile) = parse_args(argv)
-
-
- connection = Connection(socketType)
- try:
- connection.connect(address)
- except ConnectionRefusedError:
- print("ERROR: Could not connect to server.\nAre you sure that the server is running and that you're connecting to the right address?", file=sys.stderr)
- return
-
- error = None
- closeMessage = None
-
- #os.environ.setdefault("ESCDELAY", "25")
-
- fd = sys.stdin.fileno()
- oldterm = termios.tcgetattr(fd)
-
- try:
-
- tty.setraw(sys.stdin)
- Screen.default.hide_cursor()
-
- display = Display(characters)
- client = Client(display, name, connection, keybindings, logfile)
- signal.signal(signal.SIGWINCH, client.onSigwinch)
- try:
- client.start()
- except KeyboardInterrupt:
- client.close("^C caught, goodbye")
- except Exception as e:
- # throw the execption outside ncurses
- # so the cleanup can happen first
- error = e
- closeMessage = client.closeMessage
- finally:
- ## Set everything back to normal
- termios.tcsetattr(fd, termios.TCSADRAIN, oldterm)
- Screen.default.finalize()
-
-
- if error is not None:
- raise error
-
- if closeMessage:
- print(closeMessage, file=sys.stderr)
-
diff --git a/asciifarm/client/parseargs.py b/asciifarm/client/parseargs.py
deleted file mode 100644
index a8b393b..0000000
--- a/asciifarm/client/parseargs.py
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-import argparse
-import getpass
-import json
-import os
-import os.path
-
-from . import loaders
-
-
-defaultAdresses = {
- "abstract": "asciifarm",
- "unix": "asciifarm.socket",
- "inet": "localhost:9021",
-}
-
-def parse_args(argv):
-
- parser = argparse.ArgumentParser(description="The client to AsciiFarm. Run this to connect to to the server.", epilog="""
- Gameplay information:
- Walk around and explore the rooms.
- Kill the goblins and plant the seeds.
-
- ~troido""", formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument('-n', '--name', help='Your player name (must be unique!). Defaults to username on inet sockets and tildename on unix socket (including abstract). Apart from the tilde in a tildename all characters must be unicode letters, numbers or connection puctuation. The maximum size of a name is 256 bytes when encoded as utf8', default=None)
- parser.add_argument("-a", "--address", help="The address of the socket. When the socket type is 'abstract' this is just a name. When it is 'unix' this is a filename. When it is 'inet' is should be in the format 'address:port', eg 'localhost:8080'. Defaults depends on the socket type")
- parser.add_argument("-s", "--socket", help="the socket type. 'unix' is unix domain sockets, 'abstract' is abstract unix domain sockets and 'inet' is inet sockets. ", choices=["abstract", "unix", "inet"], default="abstract")
- parser.add_argument('-k', '--keybindings', help='The file with the keybinding configuration. This file is a JSON file.', default="default")
- parser.add_argument('-c', '--characters', help='The file with the character mappings for the graphics. If it is either of these names: {} it will be loaded from the charmaps directory.'.format(list(loaders.standardCharFiles.keys())), default="default")
- parser.add_argument('-o', '--logfile', help='All game messages will be written to this file.', default=None)
-
- colourGroup = parser.add_mutually_exclusive_group()
- colourGroup.add_argument('-l', '--colours', '--colors', help='enable colours! :)', action="store_true")
- colourGroup.add_argument('-b', '--nocolours', '--nocolors', help='disable colours! :)', action="store_true")
-
- args = parser.parse_args(argv)
-
- charmap = loaders.loadCharmap(args.characters)
-
- keybindings = loaders.loadKeybindings(args.keybindings)
-
- address = args.address
- if address is None:
- address = defaultAdresses[args.socket]
- if args.socket == "abstract":
- address = '\0' + address
- elif args.socket == "inet":
- hostname, sep, port = address.partition(':')
- address = (hostname, int(port))
-
- colours = True
- if args.colours:
- colours = True
- elif args.nocolours:
- colours = False
-
- name = args.name
- if name is None:
- username = getpass.getuser()
- if args.socket == "unix" or args.socket == "abstract":
- name = "~"+username
- else:
- name = username
-
- return (name, args.socket, address, keybindings, charmap, colours, args.logfile)
diff --git a/asciifarm/client/paths.py b/asciifarm/client/paths.py
deleted file mode 100644
index 407d54c..0000000
--- a/asciifarm/client/paths.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import os.path
-
-clientPath = os.path.dirname(__file__)
-farmsPath = os.path.normpath(os.path.join(clientPath, ".."))
-charmapPath = os.path.join(farmsPath, "charmaps")
-keybindingsPath = os.path.join(farmsPath, "keybindings")
diff --git a/asciifarm/common/__init__.py b/asciifarm/common/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/asciifarm/common/__init__.py
+++ /dev/null
diff --git a/asciifarm/common/messages.py b/asciifarm/common/messages.py
deleted file mode 100644
index d3aed9a..0000000
--- a/asciifarm/common/messages.py
+++ /dev/null
@@ -1,157 +0,0 @@
-
-import re
-import unicodedata
-import json
-
-class InvalidMessageError(Exception):
- errType = "invalidmessage"
- description = ""
-
- def __init__(self, description="", errType=None):
- self.description = description
- if errType is not None:
- self.errType = errType
-
- def toMessage(self):
- return ErrorMessage(self.errType, self.description)
-
-class InvalidNameError(InvalidMessageError):
- errType = "invalidname"
-
-class Message:
-
- @classmethod
- def msgType(cls):
- return cls.typename
-
- def to_json(self):
- raise NotImplementedError
-
- def to_json_bytes(self):
- return bytes(json.dumps(self.to_json()), "utf-8")
-
- @classmethod
- def from_json(cls, jsonobj):
- raise NotImplementedError
-
-class ClientToServerMessage(Message):
-
- def body(self):
- raise NotImplementedError
-
- def to_json(self):
- return [self.typename, self.body()]
-
- @classmethod
- def from_json(cls, jsonlist):
- assert len(jsonlist) == 2, InvalidMessageError
- typename, body = jsonlist
- assert typename == cls.msgType(), InvalidMessageError
- return cls(body)
-
-
-class NameMessage(ClientToServerMessage):
-
- typename = "name"
- categories = {"Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No", "Pc"}
-
-
- def __init__(self, name):
- assert isinstance(name, str), InvalidNameError("name must be a string")
- assert (len(name) > 0), InvalidNameError("name needs at least one character")
- assert (len(bytes(name, "utf-8")) <= 256), InvalidNameError("name may not be longer than 256 utf8 bytes")
- if name[0] != "~":
- for char in name:
- category = unicodedata.category(char)
- assert category in self.categories, InvalidNameError("all name caracters must be in these unicode categories: " + "|".join(self.categories) + " (except for tildenames)")
- self.name = name
-
- def body(self):
- return self.name
-
-
-class InputMessage(ClientToServerMessage):
-
- typename = "input"
-
- def __init__(self, inp):
- self.inp = inp
-
- def body(self):
- return self.inp
-
-class ChatMessage(ClientToServerMessage):
-
- typename = "chat"
-
- def __init__(self, text):
- assert isinstance(text, str), InvalidMessageError("chat message must be a string")
- assert text.isprintable(), InvalidMessageError("chat messages may only contain printable unicode characters")
- self.text = text
-
- def body(self):
- return self.text
-
-
-
-class ServerToClientMessage(Message):
- msglen = 0
-
-
- @classmethod
- def from_json(cls, jsonlist):
- assert len(jsonlist) == cls.msglen, InvalidMessageError
- assert jsonlist[0] == cls.msgType(), InvalidMessageError
- return cls(*jsonlist[1:])
-
-
-class MessageMessage(ServerToClientMessage): # this name feels stupid
- """ A message to inform the client. This is meant to be read by the user"""
-
- typename = "message"
- msglen = 3
-
- def __init__(self, text, type=""):
- self.text = text
- self.type = type
-
- def to_json(self):
- return [self.typename, self.text, self.type]
-
-
-class WorldMessage(ServerToClientMessage):
- """ A message about the world state """
-
- typename = "world"
- msglen = 2
-
- def __init__(self, updates):
- assert isinstance(updates, list), InvalidMessageError
- self.updates = updates
-
- def to_json(self):
- return [self.typename, self.updates]
-
-class ErrorMessage(ServerToClientMessage):
-
- typename = "error"
- msglen = 3
-
- def __init__(self, errType, description=""):
- self.errType = errType
- self.description = description
-
- def to_json(self):
- return [self.typename, self.errType, self.description]
-
-
-
-messages = {message.msgType(): message for message in [
- NameMessage,
- InputMessage,
- ChatMessage,
- WorldMessage,
- ErrorMessage,
- MessageMessage
-]}
-
diff --git a/asciifarm/common/tcommunicate.py b/asciifarm/common/tcommunicate.py
deleted file mode 100644
index b1fc1b0..0000000
--- a/asciifarm/common/tcommunicate.py
+++ /dev/null
@@ -1,32 +0,0 @@
-
-HEADER_SIZE = 4
-
-
-# this module is for sending discree messages over TCP
-# this is achieved by prefixing all messages with their length
-# calls to send and recv will also keep attempting to send all data unless this proves impossible
-
-
-def send(sock, msg):
- length = len(msg)
- header = length.to_bytes(4, byteorder="big")
- totalmsg = header + msg
- sock.sendall(totalmsg)
-
-def receive(sock):
- header = recvall(sock, 4) #sock.recv(4)
- length = int.from_bytes(header, byteorder="big")
- return recvall(sock, length)
-
-def recvall(sock, length):
- chunks = []
- bytes_recd = 0
- while bytes_recd < length:
- chunk = sock.recv(min(length - bytes_recd, 4096))
- if chunk == b'':
- break
- #raise RuntimeError("socket connection broken")
- chunks.append(chunk)
- bytes_recd = bytes_recd + len(chunk)
- return b''.join(chunks)
-
diff --git a/asciifarm/common/utils.py b/asciifarm/common/utils.py
deleted file mode 100644
index 95ac32b..0000000
--- a/asciifarm/common/utils.py
+++ /dev/null
@@ -1,47 +0,0 @@
-
-import os
-
-def clamp(val, lower, upper):
- """ val if it's between lower and upper, else the closest of the two"""
- return max(min(val, upper), lower)
-
-
-def concat(arr):
- """Takes a list of sequences, returns the concatenation of the sequences """
- if isinstance(arr[0], str):
- return "".join(arr)
- if isinstance(arr[0], bytes):
- return b"".join(arr)
- if isinstance(arr[0], list):
- l = []
- for s in arr:
- l += s
- return l
- if isinstance(arr[0], tuple):
- l = []
- for s in arr:
- l += s
- return tuple(l)
- else:
- raise ValueError("type {} can't be concatenated".format(type(arr[0])))
-
-
-def writeFileSafe(filename, data, tempname=None):
- if tempname is None:
- tempname = filename + ".tempfile"
- with open(tempname, 'w') as f:
- f.write(data)
- os.rename(tempname, filename)
-
-
-def readFile(filepath):
- with open(filepath, "r") as f:
- text = f.read()
- return text
-
-
-def get(collection, i, default=None):
- """ Get an element in an indexed collection, or the default in case the index is out of bounds """
- if i < 0 or i >= len(collection):
- return default
- return collection[i]
diff --git a/asciifarm/keybindings/azerty.json b/asciifarm/keybindings/azerty.json
deleted file mode 100644
index 3d69d07..0000000
--- a/asciifarm/keybindings/azerty.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "templates": ["default"],
- "actions": {
- "z": ["move", "north"],
- "w": null,
- "W": null,
- "q": ["move", "west"],
- "a": ["drop"],
- "A": ["take"],
- "Z": ["input", ["attack", ["north"]]],
- "Q": ["input", ["attack", ["west"]]]
- },
- "help": "Controls:\n aqsd or arrows: Move around\n e: Grab\n q: Drop selected\n r: Interact\n f: Attack\n t: Chat\n E: Use selected\n A: Take selected\n xc: select item\n vb: select menu\n ctrl-c: close client"
-}
diff --git a/asciifarm/keybindings/default.json b/asciifarm/keybindings/default.json
deleted file mode 100644
index faf1b40..0000000
--- a/asciifarm/keybindings/default.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-"actions": {
-"w": ["move", "north"],
-"s": ["move", "south"],
-"d": ["move", "east"],
-"a": ["move", "west"],
-"up": ["move", "north"],
-"down": ["move", "south"],
-"right": ["move", "east"],
-"left": ["move", "west"],
-"k": ["move", "north"],
-"j": ["move", "south"],
-"l": ["move", "east"],
-"h": ["move", "west"],
-"e": ["input", ["take", null]],
-"q": ["drop"],
-"Q": ["take"],
-"E": ["use"],
-"R": ["input", ["interact", [null]]],
-"r": ["input", ["interact", [null, "north", "south", "east", "west"]]],
-"x": ["selectitem", -1, true, true],
-"c": ["selectitem", 1, true, true],
-"v": ["selectwidget", -1, true, true],
-"b": ["selectwidget", 1, true, true],
-"-": ["selectitem", -1, true, true],
-"+": ["selectitem", 1, true, true],
-"/": ["selectwidget", -1, true, true],
-"*": ["selectwidget", 1, true, true],
-"f": ["input", ["attack", [null, "north", "south", "east", "west"]]],
-"F": ["input", ["attack", [null]]],
-"W": ["input", ["attack", ["north"]]],
-"S": ["input", ["attack", ["south"]]],
-"D": ["input", ["attack", ["east"]]],
-"A": ["input", ["attack", ["west"]]],
-"t": ["runinput"],
-"enter": ["runinput"],
-"pageup": ["scrollchat", 1],
-"pagedown": ["scrollchat", -1],
-"/": ["runinput", "/"]
-},
-"help": " Controls:\nwasd or arrows: Move around\ne: Grab\nq: Drop selected\nr: Interact\nf: Attack\nt: Chat\nE: Use selected\nxc: select item\nvb: select menu\nctrl-c: close client\nPgUp/PgDn: scroll chat"
-}