Initial commit with a mostly working engine.
Basic commands are being parsed. I think the only weird part is the 'use' command because it needs to possibly target two things. A tiny test example is provided in __main__, this will probably be broken out later. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
190
.gitignore
vendored
Normal file
190
.gitignore
vendored
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/vim,python
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,python
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
### Vim ###
|
||||||
|
# Swap
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
!*.svg # comment out if you don't need vector files
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-rt-v][a-z]
|
||||||
|
[._]ss[a-gi-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
|
||||||
|
# Session
|
||||||
|
Session.vim
|
||||||
|
Sessionx.vim
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
.netrwhist
|
||||||
|
*~
|
||||||
|
# Auto-generated tag files
|
||||||
|
tags
|
||||||
|
# Persistent undo
|
||||||
|
[._]*.un~
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/vim,python
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
|
|
||||||
|
# Support for Project snippet scope
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
|
||||||
3
.pylintrc
Normal file
3
.pylintrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[MASTER]
|
||||||
|
disable=wildcard-import,
|
||||||
|
unused-wildcard-import
|
||||||
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"python.formatting.provider": "black",
|
||||||
|
"python.linting.enabled": true,
|
||||||
|
"python.pythonPath": "/home/alek/.local/share/virtualenvs/adventuregame-Kcj1_Ep-/bin/python",
|
||||||
|
"python.languageServer": "Jedi",
|
||||||
|
"python.linting.mypyEnabled": true
|
||||||
|
}
|
||||||
18
Pipfile
Normal file
18
Pipfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
black = "==21.9b0"
|
||||||
|
mypy = "*"
|
||||||
|
pylint = "*"
|
||||||
|
pytest = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.9"
|
||||||
|
|
||||||
|
[pipenv]
|
||||||
|
allow_prereleases = true
|
||||||
338
Pipfile.lock
generated
Normal file
338
Pipfile.lock
generated
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "2782f015f061a958c6a0bbb02c739c5a6967777ccfa578d2a8de9b996dcd0ff4"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.9"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {},
|
||||||
|
"develop": {
|
||||||
|
"astroid": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:11f7356737b624c42e21e71fe85eea6875cb94c03c82ac76bd535a0ff10b0f25",
|
||||||
|
"sha256:abc423a1e85bc1553954a14f2053473d2b7f8baf32eae62a328be24f436b5107"
|
||||||
|
],
|
||||||
|
"markers": "python_version ~= '3.6'",
|
||||||
|
"version": "==2.8.5"
|
||||||
|
},
|
||||||
|
"attrs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||||
|
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||||
|
"version": "==21.2.0"
|
||||||
|
},
|
||||||
|
"black": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115",
|
||||||
|
"sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==21.9b0"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
|
||||||
|
"sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==8.0.3"
|
||||||
|
},
|
||||||
|
"iniconfig": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
|
||||||
|
"sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
|
||||||
|
],
|
||||||
|
"version": "==1.1.1"
|
||||||
|
},
|
||||||
|
"isort": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
|
||||||
|
"sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
|
||||||
|
"version": "==5.10.1"
|
||||||
|
},
|
||||||
|
"lazy-object-proxy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653",
|
||||||
|
"sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61",
|
||||||
|
"sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2",
|
||||||
|
"sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837",
|
||||||
|
"sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3",
|
||||||
|
"sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43",
|
||||||
|
"sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726",
|
||||||
|
"sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3",
|
||||||
|
"sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587",
|
||||||
|
"sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8",
|
||||||
|
"sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a",
|
||||||
|
"sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd",
|
||||||
|
"sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f",
|
||||||
|
"sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad",
|
||||||
|
"sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4",
|
||||||
|
"sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b",
|
||||||
|
"sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf",
|
||||||
|
"sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981",
|
||||||
|
"sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741",
|
||||||
|
"sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e",
|
||||||
|
"sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93",
|
||||||
|
"sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||||
|
"version": "==1.6.0"
|
||||||
|
},
|
||||||
|
"mccabe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||||
|
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||||
|
],
|
||||||
|
"version": "==0.6.1"
|
||||||
|
},
|
||||||
|
"mypy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9",
|
||||||
|
"sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a",
|
||||||
|
"sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9",
|
||||||
|
"sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e",
|
||||||
|
"sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2",
|
||||||
|
"sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212",
|
||||||
|
"sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b",
|
||||||
|
"sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885",
|
||||||
|
"sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150",
|
||||||
|
"sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703",
|
||||||
|
"sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072",
|
||||||
|
"sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457",
|
||||||
|
"sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e",
|
||||||
|
"sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0",
|
||||||
|
"sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb",
|
||||||
|
"sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97",
|
||||||
|
"sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8",
|
||||||
|
"sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811",
|
||||||
|
"sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6",
|
||||||
|
"sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de",
|
||||||
|
"sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504",
|
||||||
|
"sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921",
|
||||||
|
"sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.910"
|
||||||
|
},
|
||||||
|
"mypy-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||||
|
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||||
|
],
|
||||||
|
"version": "==0.4.3"
|
||||||
|
},
|
||||||
|
"packaging": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966",
|
||||||
|
"sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==21.2"
|
||||||
|
},
|
||||||
|
"pathspec": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
|
||||||
|
"sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
|
||||||
|
],
|
||||||
|
"version": "==0.9.0"
|
||||||
|
},
|
||||||
|
"platformdirs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
|
||||||
|
"sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2.4.0"
|
||||||
|
},
|
||||||
|
"pluggy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||||
|
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==1.0.0"
|
||||||
|
},
|
||||||
|
"py": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
|
||||||
|
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||||
|
"version": "==1.11.0"
|
||||||
|
},
|
||||||
|
"pylint": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126",
|
||||||
|
"sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.11.1"
|
||||||
|
},
|
||||||
|
"pyparsing": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||||
|
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==2.4.7"
|
||||||
|
},
|
||||||
|
"pytest": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
|
||||||
|
"sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==6.2.5"
|
||||||
|
},
|
||||||
|
"regex": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f",
|
||||||
|
"sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc",
|
||||||
|
"sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4",
|
||||||
|
"sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4",
|
||||||
|
"sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8",
|
||||||
|
"sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f",
|
||||||
|
"sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a",
|
||||||
|
"sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef",
|
||||||
|
"sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f",
|
||||||
|
"sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc",
|
||||||
|
"sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50",
|
||||||
|
"sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d",
|
||||||
|
"sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d",
|
||||||
|
"sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733",
|
||||||
|
"sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36",
|
||||||
|
"sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345",
|
||||||
|
"sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0",
|
||||||
|
"sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12",
|
||||||
|
"sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646",
|
||||||
|
"sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667",
|
||||||
|
"sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244",
|
||||||
|
"sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29",
|
||||||
|
"sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec",
|
||||||
|
"sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf",
|
||||||
|
"sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4",
|
||||||
|
"sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449",
|
||||||
|
"sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a",
|
||||||
|
"sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d",
|
||||||
|
"sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb",
|
||||||
|
"sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e",
|
||||||
|
"sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83",
|
||||||
|
"sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e",
|
||||||
|
"sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a",
|
||||||
|
"sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94",
|
||||||
|
"sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc",
|
||||||
|
"sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e",
|
||||||
|
"sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965",
|
||||||
|
"sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0",
|
||||||
|
"sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36",
|
||||||
|
"sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec",
|
||||||
|
"sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23",
|
||||||
|
"sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7",
|
||||||
|
"sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe",
|
||||||
|
"sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6",
|
||||||
|
"sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b",
|
||||||
|
"sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb",
|
||||||
|
"sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b",
|
||||||
|
"sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30",
|
||||||
|
"sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"
|
||||||
|
],
|
||||||
|
"version": "==2021.11.10"
|
||||||
|
},
|
||||||
|
"toml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||||
|
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==0.10.2"
|
||||||
|
},
|
||||||
|
"tomli": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee",
|
||||||
|
"sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==1.2.2"
|
||||||
|
},
|
||||||
|
"typing-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
|
||||||
|
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
|
||||||
|
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.10'",
|
||||||
|
"version": "==3.10.0.2"
|
||||||
|
},
|
||||||
|
"wrapt": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179",
|
||||||
|
"sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096",
|
||||||
|
"sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374",
|
||||||
|
"sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df",
|
||||||
|
"sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185",
|
||||||
|
"sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785",
|
||||||
|
"sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7",
|
||||||
|
"sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909",
|
||||||
|
"sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918",
|
||||||
|
"sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33",
|
||||||
|
"sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068",
|
||||||
|
"sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829",
|
||||||
|
"sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af",
|
||||||
|
"sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79",
|
||||||
|
"sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce",
|
||||||
|
"sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc",
|
||||||
|
"sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36",
|
||||||
|
"sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade",
|
||||||
|
"sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca",
|
||||||
|
"sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32",
|
||||||
|
"sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125",
|
||||||
|
"sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e",
|
||||||
|
"sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709",
|
||||||
|
"sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f",
|
||||||
|
"sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b",
|
||||||
|
"sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb",
|
||||||
|
"sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb",
|
||||||
|
"sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489",
|
||||||
|
"sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640",
|
||||||
|
"sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb",
|
||||||
|
"sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851",
|
||||||
|
"sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d",
|
||||||
|
"sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44",
|
||||||
|
"sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13",
|
||||||
|
"sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2",
|
||||||
|
"sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb",
|
||||||
|
"sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b",
|
||||||
|
"sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9",
|
||||||
|
"sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755",
|
||||||
|
"sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c",
|
||||||
|
"sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a",
|
||||||
|
"sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf",
|
||||||
|
"sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3",
|
||||||
|
"sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229",
|
||||||
|
"sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e",
|
||||||
|
"sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de",
|
||||||
|
"sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554",
|
||||||
|
"sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10",
|
||||||
|
"sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80",
|
||||||
|
"sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056",
|
||||||
|
"sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||||
|
"version": "==1.13.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
0
agame/__init__.py
Normal file
0
agame/__init__.py
Normal file
115
agame/__main__.py
Normal file
115
agame/__main__.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
from agame.action import *
|
||||||
|
from agame.game import *
|
||||||
|
from agame.item import *
|
||||||
|
from agame.room import *
|
||||||
|
from agame.trigger import *
|
||||||
|
|
||||||
|
|
||||||
|
# TODO - take a custom module name that has:
|
||||||
|
# .database
|
||||||
|
# .game
|
||||||
|
# so that you can just RUN it
|
||||||
|
# Something similar to wsgi using "app" in the passed module
|
||||||
|
|
||||||
|
|
||||||
|
# This is the *game* here
|
||||||
|
database = Database()
|
||||||
|
database.add_items(
|
||||||
|
Item(
|
||||||
|
id="glowing_rock",
|
||||||
|
name="glowing rock",
|
||||||
|
desc="This rock is glowing.",
|
||||||
|
synonyms=("rock",),
|
||||||
|
room_desc="You see a ((glowing rock)). You have **got** to have it.",
|
||||||
|
triggers={
|
||||||
|
GET: [
|
||||||
|
PrintAction(
|
||||||
|
"You try to pick up the rock, but it slips out of your greasy hands.",
|
||||||
|
"Maybe you should wash your hands, you disgusting little man.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
LOOK: [PrintAction("Man, that rock looks awesome.")],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Item(
|
||||||
|
id="cell_door",
|
||||||
|
name="door",
|
||||||
|
room_desc="A ((door)) sits on the far wall.",
|
||||||
|
triggers={
|
||||||
|
GET: [PrintAction("The door is pretty attached to its wall.")],
|
||||||
|
OPEN: [
|
||||||
|
CheckVarAction(
|
||||||
|
"cell_door_open",
|
||||||
|
Compare.EQUALS,
|
||||||
|
True,
|
||||||
|
yes=[
|
||||||
|
PrintAction(
|
||||||
|
"It's already open. You push on the door even //more//, just in case."
|
||||||
|
),
|
||||||
|
SleepAction(1.0),
|
||||||
|
PrintAction("..."),
|
||||||
|
SleepAction(1.0),
|
||||||
|
PrintAction("Yup, still open."),
|
||||||
|
],
|
||||||
|
no=[
|
||||||
|
SetVarAction("cell_door_open", True),
|
||||||
|
PrintAction("The door swings open, thanks to you."),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
CLOSE: [
|
||||||
|
CheckVarAction(
|
||||||
|
"cell_door_open",
|
||||||
|
Compare.EQUALS,
|
||||||
|
True,
|
||||||
|
yes=[
|
||||||
|
PrintAction("You close that door. Nice job."),
|
||||||
|
SetVarAction("cell_door_open", False),
|
||||||
|
],
|
||||||
|
no=[PrintAction("The door is already closed.")],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
LOOK: [
|
||||||
|
CheckVarAction(
|
||||||
|
"cell_door_open",
|
||||||
|
Compare.EQUALS,
|
||||||
|
True,
|
||||||
|
yes=[PrintAction("It's a door, wide open, because you opened it.")],
|
||||||
|
no=[PrintAction("A closed door. You can change this.")],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
database.add_rooms(
|
||||||
|
Room(
|
||||||
|
id="start",
|
||||||
|
name="Test room",
|
||||||
|
desc="You're in ((Todd's Test Cell)).",
|
||||||
|
items=[
|
||||||
|
database.items["glowing_rock"].create_inst(),
|
||||||
|
database.items["cell_door"].create_inst(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build the game state
|
||||||
|
game = Game(
|
||||||
|
database=database,
|
||||||
|
room=database.rooms["start"],
|
||||||
|
vars={
|
||||||
|
"cell_door_open": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
game.print_room()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
game.say()
|
||||||
|
line = input("> ")
|
||||||
|
game.say()
|
||||||
|
game.run_command(line)
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
game.say()
|
||||||
|
game.say("Bye.")
|
||||||
|
break
|
||||||
294
agame/action.py
Normal file
294
agame/action.py
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
import dataclasses
|
||||||
|
import time
|
||||||
|
from typing import Any, Optional, Sequence, Union, TYPE_CHECKING
|
||||||
|
import enum
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from agame.game import Game
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"Action",
|
||||||
|
"SleepAction",
|
||||||
|
"PrintAction",
|
||||||
|
"TeleportAction",
|
||||||
|
"GetAction",
|
||||||
|
"CheckInvItemsAction",
|
||||||
|
"CheckRoomItemsAction",
|
||||||
|
"Compare",
|
||||||
|
"Var",
|
||||||
|
"CheckVarAction",
|
||||||
|
"SetVarAction",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Action:
|
||||||
|
"""
|
||||||
|
An action that the game engine can take.
|
||||||
|
|
||||||
|
This is the base class. It can be instantiated as a "dummy" action if
|
||||||
|
needed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
"""
|
||||||
|
Complete this action.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class SleepAction(Action):
|
||||||
|
"""
|
||||||
|
An action that delays the amount of time, in seconds. Decimal values are
|
||||||
|
allowed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
secs: float
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
time.sleep(self.secs)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class PrintAction(Action):
|
||||||
|
"""
|
||||||
|
Prints a message to the screen.
|
||||||
|
"""
|
||||||
|
|
||||||
|
lines: Sequence[str]
|
||||||
|
|
||||||
|
def __init__(self, *lines: str):
|
||||||
|
self.lines = lines
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
if not self.lines:
|
||||||
|
return
|
||||||
|
line = self.lines[0]
|
||||||
|
game.say(line)
|
||||||
|
for line in self.lines[1:]:
|
||||||
|
game.say()
|
||||||
|
game.say(line)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class TeleportAction(Action):
|
||||||
|
"""
|
||||||
|
Moves the player to another room.
|
||||||
|
"""
|
||||||
|
|
||||||
|
room_id: str
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
# TODO
|
||||||
|
raise NotImplementedError("TODO - implement teleport action")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class GetAction(Action):
|
||||||
|
"""
|
||||||
|
Removes an item from the current room and puts it in the player's inventory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
item_id: str
|
||||||
|
pickup_text: Optional[str] = None
|
||||||
|
|
||||||
|
# def __init__(self, item_id: str, pickup_text: )
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
# Find the first instance of the item in the room and remove it
|
||||||
|
item = game.room.remove(self.item_id)
|
||||||
|
assert (
|
||||||
|
item is not None
|
||||||
|
), f"attempted to remove an item (id: {self.item_id}) that does not exist in the current room; this is a game logic error/bug"
|
||||||
|
|
||||||
|
if item.id in game.inventory:
|
||||||
|
# Item in inventory already? Update count
|
||||||
|
game.inventory[item.id].count += item.count
|
||||||
|
else:
|
||||||
|
# Otherwise just add it
|
||||||
|
game.inventory[item.id] = item
|
||||||
|
|
||||||
|
# Print text
|
||||||
|
if self.pickup_text is None:
|
||||||
|
game.say(f"You pick up (({item.name})).")
|
||||||
|
else:
|
||||||
|
game.say(self.pickup_text)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class CheckInvItemsAction(Action):
|
||||||
|
"""
|
||||||
|
Checks if all supplied items are present in the inventory, and executes the
|
||||||
|
appropriate action sequence.
|
||||||
|
"""
|
||||||
|
|
||||||
|
item_ids: Union[str, Sequence[str]]
|
||||||
|
yes: Sequence[Action]
|
||||||
|
no: Sequence[Action]
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
# If the item_ids are just a string, use that as a list.
|
||||||
|
items = [self.item_ids] if isinstance(self.item_ids, str) else self.item_ids
|
||||||
|
for item_id in items:
|
||||||
|
if item_id not in game.inventory:
|
||||||
|
game.do_actions(self.no)
|
||||||
|
return
|
||||||
|
game.do_actions(self.yes)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class CheckRoomItemsAction(Action):
|
||||||
|
"""
|
||||||
|
Checks if all supplied items are present in the current room, and executes the
|
||||||
|
appropriate action sequence.
|
||||||
|
"""
|
||||||
|
|
||||||
|
item_ids: str
|
||||||
|
yes: Sequence[Action]
|
||||||
|
no: Sequence[Action]
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
items = [self.item_ids] if isinstance(self.item_ids, str) else self.item_ids
|
||||||
|
for item_id in items:
|
||||||
|
if item_id not in game.room.items:
|
||||||
|
game.do_actions(self.no)
|
||||||
|
return
|
||||||
|
game.do_actions(self.yes)
|
||||||
|
|
||||||
|
|
||||||
|
class Compare(enum.Enum):
|
||||||
|
"""
|
||||||
|
A comparison for a value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Does what it says on the tin.
|
||||||
|
#
|
||||||
|
# This is type-sensitive and literally just does `==` in Python. Seriously.
|
||||||
|
EQUALS = enum.auto()
|
||||||
|
|
||||||
|
# Also does what it says on the tin.
|
||||||
|
#
|
||||||
|
# This is type-sensitive and literally just does `!=` in Python. Seriously.
|
||||||
|
NOT_EQUALS = enum.auto()
|
||||||
|
|
||||||
|
# Checks if a value is less than to another value.
|
||||||
|
#
|
||||||
|
# This uses the `cmp` to check the values.
|
||||||
|
LESS_THAN = enum.auto()
|
||||||
|
|
||||||
|
# Checks if a value is less than or equal to another value.
|
||||||
|
#
|
||||||
|
# This uses the `cmp` to check the values.
|
||||||
|
LESS_THAN_EQUALS = enum.auto()
|
||||||
|
|
||||||
|
# Checks if a value is greater than to another value.
|
||||||
|
#
|
||||||
|
# This uses the `cmp` to check the values.
|
||||||
|
GREATER_THAN = enum.auto()
|
||||||
|
|
||||||
|
# Checks if a value is greater than or equal to another value.
|
||||||
|
#
|
||||||
|
# This uses the `cmp` to check the values.
|
||||||
|
GREATER_THAN_EQUALS = enum.auto()
|
||||||
|
|
||||||
|
# Checks if the string-ified version of the value matches the other value,
|
||||||
|
# as a regex.
|
||||||
|
MATCHES = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Var:
|
||||||
|
id: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class CheckVarAction(Action):
|
||||||
|
"""
|
||||||
|
Check a variable's value using some kind of comparison.
|
||||||
|
|
||||||
|
If you want to check one variable against another, use the `action.Var`
|
||||||
|
type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The variable to check the value of.
|
||||||
|
var_id: str
|
||||||
|
|
||||||
|
# The comparison operation to use. See `Compare` for comparisons.
|
||||||
|
compare: Compare
|
||||||
|
|
||||||
|
# The constant value to check against.
|
||||||
|
against: Any
|
||||||
|
|
||||||
|
# The action to execute when this is true.
|
||||||
|
yes: Sequence[Action]
|
||||||
|
|
||||||
|
# The action to execute when this is false.
|
||||||
|
no: Sequence[Action]
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
val = game.vars.get(self.var_id, None)
|
||||||
|
compare_val = None
|
||||||
|
if isinstance(self.against, Var):
|
||||||
|
compare_val = game.vars.get(self.against.id, None)
|
||||||
|
else:
|
||||||
|
compare_val = self.against
|
||||||
|
|
||||||
|
result = False
|
||||||
|
if self.compare == Compare.EQUALS:
|
||||||
|
result = val == compare_val
|
||||||
|
elif self.compare == Compare.NOT_EQUALS:
|
||||||
|
result = val != compare_val
|
||||||
|
elif self.compare == Compare.LESS_THAN:
|
||||||
|
if isinstance(val, str) or isinstance(compare_val, str):
|
||||||
|
result = str(val) < str(compare_val)
|
||||||
|
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||||
|
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||||
|
):
|
||||||
|
result = val < compare_val
|
||||||
|
elif self.compare == Compare.LESS_THAN_EQUALS:
|
||||||
|
if isinstance(val, str) or isinstance(compare_val, str):
|
||||||
|
result = str(val) <= str(compare_val)
|
||||||
|
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||||
|
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||||
|
):
|
||||||
|
result = val <= compare_val
|
||||||
|
elif self.compare == Compare.GREATER_THAN:
|
||||||
|
if isinstance(val, str) or isinstance(compare_val, str):
|
||||||
|
result = str(val) > str(compare_val)
|
||||||
|
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||||
|
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||||
|
):
|
||||||
|
result = val > compare_val
|
||||||
|
elif self.compare == Compare.GREATER_THAN_EQUALS:
|
||||||
|
if isinstance(val, str) or isinstance(compare_val, str):
|
||||||
|
result = str(val) >= str(compare_val)
|
||||||
|
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||||
|
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||||
|
):
|
||||||
|
result = val >= compare_val
|
||||||
|
elif self.compare == Compare.MATCHES:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert False, f"{self.compare} isn't a Compare value, ya doink"
|
||||||
|
|
||||||
|
# Check result, do action
|
||||||
|
if result:
|
||||||
|
game.do_actions(self.yes)
|
||||||
|
else:
|
||||||
|
game.do_actions(self.no)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class SetVarAction(Action):
|
||||||
|
"""
|
||||||
|
Set a variable to a specific value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
var_id: str
|
||||||
|
value: Any
|
||||||
|
|
||||||
|
def act(self, game: "Game"):
|
||||||
|
value = (
|
||||||
|
game.vars.get(self.value.id) if isinstance(self.value, Var) else self.value
|
||||||
|
)
|
||||||
|
game.vars[self.var_id] = value
|
||||||
39
agame/color.py
Normal file
39
agame/color.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ("colorize",)
|
||||||
|
|
||||||
|
BOLD_PAT = re.compile(r"\*\*(.+?)\*\*", re.MULTILINE)
|
||||||
|
ITALIC_PAT = re.compile(r"//(.+?)//", re.MULTILINE)
|
||||||
|
INTEREST_PAT = re.compile(r"\(\((.+?)\)\)", re.MULTILINE)
|
||||||
|
SHADOW_PAT = re.compile(r"\{\{(.+?)\}\}", re.MULTILINE)
|
||||||
|
|
||||||
|
BOLD_COL = "\u001b[1m"
|
||||||
|
ITALIC_COL = "\u001b[3m"
|
||||||
|
INTEREST_COL = "\u001b[34;1m"
|
||||||
|
SHADOW_COL = "\u001b[30;1m"
|
||||||
|
RESET_COL = "\u001b[0m"
|
||||||
|
|
||||||
|
|
||||||
|
def colorize(text: str) -> str:
|
||||||
|
"""
|
||||||
|
Colorizes text for output on an ANSI terminal.
|
||||||
|
|
||||||
|
This will use escape codes to replace things.
|
||||||
|
|
||||||
|
Style guide:
|
||||||
|
((This)) is "interest" styling. This will make the text blue.
|
||||||
|
{{This}} is "shadow" styling. This will make the text a dark grey (or at
|
||||||
|
least, more subtle.)
|
||||||
|
"""
|
||||||
|
replacements = [
|
||||||
|
(INTEREST_PAT, INTEREST_COL),
|
||||||
|
(SHADOW_PAT, SHADOW_COL),
|
||||||
|
(BOLD_PAT, BOLD_COL),
|
||||||
|
(ITALIC_PAT, ITALIC_COL),
|
||||||
|
]
|
||||||
|
|
||||||
|
for (pat, col) in replacements:
|
||||||
|
text = pat.sub(col + r"\1" + RESET_COL, text)
|
||||||
|
|
||||||
|
return text
|
||||||
113
agame/game.py
Normal file
113
agame/game.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import dataclasses
|
||||||
|
import textwrap
|
||||||
|
from typing import Any, MutableMapping, Match, Optional, Sequence
|
||||||
|
from agame.action import Action
|
||||||
|
from agame.color import colorize
|
||||||
|
from agame.item import Item, ItemInst
|
||||||
|
from agame.room import Room
|
||||||
|
from agame.trigger import *
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"Database",
|
||||||
|
"Game",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Database:
|
||||||
|
# All items available to the game.
|
||||||
|
items: MutableMapping[str, Item] = dataclasses.field(default_factory=dict)
|
||||||
|
# All rooms available to the game.
|
||||||
|
rooms: MutableMapping[str, Room] = dataclasses.field(default_factory=dict)
|
||||||
|
|
||||||
|
def add_item(self, item: Item):
|
||||||
|
self.items[item.id] = item
|
||||||
|
|
||||||
|
def add_items(self, *items: Item):
|
||||||
|
for item in items:
|
||||||
|
self.add_item(item)
|
||||||
|
|
||||||
|
def add_room(self, room: Room):
|
||||||
|
self.rooms[room.id] = room
|
||||||
|
|
||||||
|
def add_rooms(self, *rooms: Room):
|
||||||
|
for room in rooms:
|
||||||
|
self.add_room(room)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Game:
|
||||||
|
# Game room/items database
|
||||||
|
database: Database
|
||||||
|
# Current room.
|
||||||
|
room: Room
|
||||||
|
# Player inventory.
|
||||||
|
inventory: MutableMapping[str, ItemInst] = dataclasses.field(default_factory=dict)
|
||||||
|
# Variables.
|
||||||
|
vars: MutableMapping[str, Any] = dataclasses.field(default_factory=dict)
|
||||||
|
|
||||||
|
def run_command(self, line: str):
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
return
|
||||||
|
|
||||||
|
triggers: Sequence[Trigger] = [
|
||||||
|
GetTrigger(),
|
||||||
|
UseTrigger(),
|
||||||
|
PutTrigger(),
|
||||||
|
LookTrigger(),
|
||||||
|
OpenTrigger(),
|
||||||
|
CloseTrigger(),
|
||||||
|
GoTrigger(),
|
||||||
|
]
|
||||||
|
|
||||||
|
trigger = None
|
||||||
|
match: Optional[Match] = None
|
||||||
|
for t in triggers:
|
||||||
|
match = t.pattern().fullmatch(line)
|
||||||
|
if match:
|
||||||
|
trigger = t
|
||||||
|
break
|
||||||
|
if not trigger:
|
||||||
|
self.say("I'm not sure what you mean.")
|
||||||
|
return
|
||||||
|
|
||||||
|
assert match, "why were no patterns matched?"
|
||||||
|
trigger.trigger(self, match)
|
||||||
|
|
||||||
|
def do_actions(self, actions: Sequence[Action]):
|
||||||
|
"Executes the supplied actions."
|
||||||
|
for action in actions:
|
||||||
|
action.act(self)
|
||||||
|
|
||||||
|
def print_room(self):
|
||||||
|
"Prints this room's description."
|
||||||
|
self.say(self.room.name)
|
||||||
|
self.say()
|
||||||
|
self.say(self.room.desc)
|
||||||
|
# Look at revealed text
|
||||||
|
for item in self.room.items.values():
|
||||||
|
if not item.revealed or item.room_desc is None:
|
||||||
|
continue
|
||||||
|
if item.room_desc == "":
|
||||||
|
# TODO - pluralization, 'a' vs 'an'
|
||||||
|
self.say(f"You see a (({item.name})).")
|
||||||
|
else:
|
||||||
|
self.say(item.room_desc)
|
||||||
|
|
||||||
|
def say(self, message: Optional[str] = None):
|
||||||
|
"Format, colorize, wrap, and print the message."
|
||||||
|
message = message or ""
|
||||||
|
message = textwrap.fill(message)
|
||||||
|
print(colorize(message))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rooms(self):
|
||||||
|
"Shortcut property for `game.database.rooms`."
|
||||||
|
return self.database.rooms
|
||||||
|
|
||||||
|
@property
|
||||||
|
def items(self):
|
||||||
|
"Shortcut property for `game.database.items`."
|
||||||
|
return self.database.items
|
||||||
131
agame/item.py
Normal file
131
agame/item.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import dataclasses
|
||||||
|
from typing import Mapping, Optional, Sequence
|
||||||
|
from agame.action import Action
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"ItemInst",
|
||||||
|
"Item",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ItemInst:
|
||||||
|
"""
|
||||||
|
An instance of an item in the game.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Reference to the global item that this is an instance of.
|
||||||
|
item: "Item"
|
||||||
|
|
||||||
|
# Gets whether this item can be taken.
|
||||||
|
fixed: bool = False
|
||||||
|
|
||||||
|
# Gets how many of this item instance are present in this stack.
|
||||||
|
count: int = 1
|
||||||
|
|
||||||
|
# Gets whether this item is revealed or not.
|
||||||
|
revealed: bool = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self) -> str:
|
||||||
|
return self.item.id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self.item.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def desc(self) -> Optional[str]:
|
||||||
|
return self.item.desc
|
||||||
|
|
||||||
|
@property
|
||||||
|
def synonyms(self) -> Sequence[str]:
|
||||||
|
return self.item.synonyms
|
||||||
|
|
||||||
|
@property
|
||||||
|
def room_desc(self) -> Optional[str]:
|
||||||
|
return self.item.room_desc
|
||||||
|
|
||||||
|
@property
|
||||||
|
def triggers(self) -> Mapping[str, Sequence[Action]]:
|
||||||
|
return self.item.triggers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def use_actions(self) -> Mapping[str, Sequence[Action]]:
|
||||||
|
return self.item.use_actions
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Item:
|
||||||
|
"""
|
||||||
|
A game item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The ID of this item. This is how items are looked up in the game.
|
||||||
|
id: str
|
||||||
|
|
||||||
|
# The printable name of this item.
|
||||||
|
name: str
|
||||||
|
|
||||||
|
# A long description for this item.
|
||||||
|
desc: Optional[str] = None
|
||||||
|
|
||||||
|
# A list of all synonyms for this item.
|
||||||
|
synonyms: Sequence[str] = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
|
# The description that is used in the context of a room's `look` command.
|
||||||
|
#
|
||||||
|
# When someone wants to look at the entire room, all items that have been
|
||||||
|
# revealed will also be displayed.
|
||||||
|
#
|
||||||
|
# If you want to disable this behavior entirely, `room_desc` should be set
|
||||||
|
# to `None`.
|
||||||
|
#
|
||||||
|
# If you want to use the default text, "You see a (({item.name}))",
|
||||||
|
# `room_desc` should be set to the blank string, `""`.
|
||||||
|
#
|
||||||
|
# Otherwise, the `room_desc` string will be colorized and printed as
|
||||||
|
# written.
|
||||||
|
room_desc: Optional[str] = None
|
||||||
|
|
||||||
|
# A list of triggers that a game may use. Since this is just a mapping of
|
||||||
|
# strings to action sequences, only one set of actions is allowed per
|
||||||
|
# trigger.
|
||||||
|
#
|
||||||
|
# Valid triggers include:
|
||||||
|
# * get
|
||||||
|
# * use
|
||||||
|
# * put
|
||||||
|
# * look
|
||||||
|
# * open
|
||||||
|
# * close
|
||||||
|
# ...more to come
|
||||||
|
triggers: Mapping[str, Sequence[Action]] = dataclasses.field(default_factory=dict)
|
||||||
|
|
||||||
|
# A mapping of other items that this item may be used with, specifically on
|
||||||
|
# the USE command.
|
||||||
|
#
|
||||||
|
# USE is a strange beast, because instead of just one implicit target (which
|
||||||
|
# is verified to exist by the trigger, of all things) we have *two* targets:
|
||||||
|
# the subject and the direct object. And not always!
|
||||||
|
#
|
||||||
|
# For example, we have an item, paintbrush. These are some options:
|
||||||
|
# use paintbrush # <- on what?
|
||||||
|
# use paintbrush on canvas # <- you paint a beautiful masterpiece.
|
||||||
|
# use paintbrush on car # <- that's not allowed (custom text)
|
||||||
|
# use paintbrush on fake item # <- that item doesn't exist
|
||||||
|
#
|
||||||
|
# We can't really represent this with the current str -> sequence[action]
|
||||||
|
# stuff we have in place right now, so a special field will be good enough
|
||||||
|
# until a more insane/robust solution is implemented.
|
||||||
|
use_actions: Mapping[str, Sequence[Action]] = dataclasses.field(
|
||||||
|
default_factory=dict
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_inst(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Creates a new item instance, passing the supplied args and kwargs to the
|
||||||
|
ItemInst constructor.
|
||||||
|
"""
|
||||||
|
return ItemInst(item=self, *args, **kwargs)
|
||||||
46
agame/room.py
Normal file
46
agame/room.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import dataclasses
|
||||||
|
from typing import MutableMapping, Optional, Sequence, Union, TYPE_CHECKING
|
||||||
|
from agame.item import ItemInst
|
||||||
|
from agame.util import search_item_name
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ("Room",)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Room:
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
desc: str
|
||||||
|
items: MutableMapping[str, ItemInst]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: str,
|
||||||
|
name: str,
|
||||||
|
desc: str,
|
||||||
|
items: Union[Sequence[ItemInst], MutableMapping[str, ItemInst]],
|
||||||
|
):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.desc = desc
|
||||||
|
if isinstance(items, MutableMapping):
|
||||||
|
self.items = items
|
||||||
|
else:
|
||||||
|
self.items = {item.id: item for item in items}
|
||||||
|
|
||||||
|
def search_item_name(self, item_name: str) -> Optional[ItemInst]:
|
||||||
|
"""
|
||||||
|
Searches all item instances in the room for the given item name, also
|
||||||
|
checking synonyms. Returns the first item instance found, or none if no
|
||||||
|
synonyms or names were found to match.
|
||||||
|
"""
|
||||||
|
return search_item_name(self.items.values(), item_name)
|
||||||
|
|
||||||
|
def remove(self, item_id: str) -> Optional[ItemInst]:
|
||||||
|
"""
|
||||||
|
Removes an item with the given ID from the room, returning it.
|
||||||
|
|
||||||
|
If it's not present in the room, `None` is returned.
|
||||||
|
"""
|
||||||
|
return self.items.pop(item_id, None)
|
||||||
247
agame/trigger.py
Normal file
247
agame/trigger.py
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
import abc
|
||||||
|
import re
|
||||||
|
from typing import Match, Pattern, TYPE_CHECKING
|
||||||
|
from agame.util import search_item_name
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from agame.game import Game
|
||||||
|
|
||||||
|
|
||||||
|
# Triggers:
|
||||||
|
# get/take/grab/pick up [a[n]/the] x
|
||||||
|
# use [a[n]/the] x [with/on [a[n]/the] y]
|
||||||
|
# put [a[n]/the] x on/in [a[n]/the] y
|
||||||
|
# look [[at] [a[n]/the] x]
|
||||||
|
# open x
|
||||||
|
# close x
|
||||||
|
# go/(go to)/leave/exit x
|
||||||
|
# give [a[n]/the] x to [a[n]/the] y
|
||||||
|
# push [a[n]/the] x
|
||||||
|
# pull [a[n]/the] x
|
||||||
|
# (put down)/drop [a[n]/the] x
|
||||||
|
__all__ = (
|
||||||
|
"Trigger",
|
||||||
|
"GetTrigger",
|
||||||
|
"UseTrigger",
|
||||||
|
"PutTrigger",
|
||||||
|
"LookTrigger",
|
||||||
|
"OpenTrigger",
|
||||||
|
"CloseTrigger",
|
||||||
|
"GoTrigger",
|
||||||
|
"GET",
|
||||||
|
"USE",
|
||||||
|
"PUT",
|
||||||
|
"LOOK",
|
||||||
|
"OPEN",
|
||||||
|
"CLOSE",
|
||||||
|
"GO",
|
||||||
|
)
|
||||||
|
GET = "get"
|
||||||
|
USE = "use"
|
||||||
|
PUT = "put"
|
||||||
|
LOOK = "look"
|
||||||
|
OPEN = "open"
|
||||||
|
CLOSE = "close"
|
||||||
|
GO = "go"
|
||||||
|
|
||||||
|
|
||||||
|
class Trigger(metaclass=abc.ABCMeta):
|
||||||
|
@staticmethod
|
||||||
|
@abc.abstractmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GetTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>get|take|grab|pick[ ]*up)
|
||||||
|
(([ ]+(an?|the))?[ ]+(?P<item>.+))?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
if not item_name:
|
||||||
|
otrigger = match["trigger"].lower().capitalize()
|
||||||
|
game.say(f"{otrigger} what?")
|
||||||
|
return
|
||||||
|
item = game.room.search_item_name(item_name.lower())
|
||||||
|
if item and GET in item.triggers:
|
||||||
|
actions = item.triggers[GET]
|
||||||
|
# if there are any actions, do them. else do the default action
|
||||||
|
game.do_actions(actions)
|
||||||
|
else:
|
||||||
|
game.say("Can't get that.")
|
||||||
|
|
||||||
|
|
||||||
|
class UseTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
# TODO(low) - wouldn't it be cool to specify "use" actions?
|
||||||
|
# e.g. you have a gun item and you want to be allowed to use "shoot" in order to
|
||||||
|
# use the gun.
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>use)
|
||||||
|
(([ ]+(an?|the))?[ ]+(?P<item>.+?)
|
||||||
|
([ ]+(with|on)([ ]+(an?|the))?[ ]+(?P<target>.+))?)?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
if not item_name:
|
||||||
|
game.say("Use what?")
|
||||||
|
return
|
||||||
|
target_name = match["target"]
|
||||||
|
|
||||||
|
# Get the item from inventory or room
|
||||||
|
item = game.room.search_item_name(item_name) or search_item_name(
|
||||||
|
game.inventory.values(), item_name.lower()
|
||||||
|
)
|
||||||
|
if not item:
|
||||||
|
game.say(f"I'm not sure what you mean by {item_name}.")
|
||||||
|
return
|
||||||
|
|
||||||
|
target = None
|
||||||
|
if target_name:
|
||||||
|
# Get the target from inventory or room
|
||||||
|
target = game.room.search_item_name(target_name) or search_item_name(
|
||||||
|
game.inventory.values(), target_name.lower()
|
||||||
|
)
|
||||||
|
if not target:
|
||||||
|
game.say(f"I'm not sure what you mean by {target_name}.")
|
||||||
|
# Check if the target can be used on something
|
||||||
|
elif target.id in item.use_actions:
|
||||||
|
game.do_actions(item.use_actions[target.id])
|
||||||
|
else:
|
||||||
|
game.say("I'm not sure how to do that.")
|
||||||
|
elif USE in item.triggers:
|
||||||
|
# Check if the item can be used by itself
|
||||||
|
game.do_actions(item.triggers[USE])
|
||||||
|
elif item.use_actions:
|
||||||
|
# This item can be used with *something*, but we don't know what.
|
||||||
|
game.say(f"Use (({item_name})) with what?")
|
||||||
|
else:
|
||||||
|
# This can't be used.
|
||||||
|
game.say("I can't really use that.")
|
||||||
|
|
||||||
|
|
||||||
|
class PutTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>put)
|
||||||
|
([ ]+((an?|the)[ ]+)?(?P<item>.+?)
|
||||||
|
((on|in)[ ]+)?((an?|the)[ ]+)?[ ]+(?P<target>.+))?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
|
||||||
|
|
||||||
|
class LookTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>look)
|
||||||
|
([ ]+(at[ ]+)?((an?|the)[ ]+)?(?P<item>.+?))?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
if not item_name:
|
||||||
|
game.print_room()
|
||||||
|
return
|
||||||
|
item = game.room.search_item_name(item_name.lower())
|
||||||
|
if item and LOOK in item.triggers:
|
||||||
|
actions = item.triggers[LOOK]
|
||||||
|
game.do_actions(actions)
|
||||||
|
else:
|
||||||
|
game.say("Can't see that.")
|
||||||
|
|
||||||
|
|
||||||
|
class OpenTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>open)
|
||||||
|
(((an?|the)[ ]+)?[ ]+(?P<item>.+?))?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
if not item_name:
|
||||||
|
game.say("Open what?")
|
||||||
|
return
|
||||||
|
item = game.room.search_item_name(item_name.lower())
|
||||||
|
if item and OPEN in item.triggers:
|
||||||
|
actions = item.triggers[OPEN]
|
||||||
|
game.do_actions(actions)
|
||||||
|
else:
|
||||||
|
game.say("Can't open that.")
|
||||||
|
|
||||||
|
|
||||||
|
class CloseTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>close)
|
||||||
|
(((an?|the)[ ]+)?[ ]+(?P<item>.+?))?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
if not item_name:
|
||||||
|
game.say("Close what?")
|
||||||
|
return
|
||||||
|
item = game.room.search_item_name(item_name.lower())
|
||||||
|
if item and CLOSE in item.triggers:
|
||||||
|
actions = item.triggers[CLOSE]
|
||||||
|
game.do_actions(actions)
|
||||||
|
else:
|
||||||
|
game.say("Can't close that.")
|
||||||
|
|
||||||
|
|
||||||
|
class GoTrigger(Trigger):
|
||||||
|
@staticmethod
|
||||||
|
def pattern() -> Pattern:
|
||||||
|
return re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<trigger>go|go[ ]*to|leave|exit)
|
||||||
|
(((an?|the)[ ]+)?[ ]+(?P<item>.+?))?
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def trigger(self, game: "Game", match: Match):
|
||||||
|
item_name = match["item"]
|
||||||
|
if not item_name:
|
||||||
|
otrigger = match["trigger"].lower().capitalize()
|
||||||
|
game.say(f"{otrigger} where?")
|
||||||
|
return
|
||||||
|
item = game.room.search_item_name(item_name.lower())
|
||||||
|
if item and GO in item.triggers:
|
||||||
|
actions = item.triggers[GO]
|
||||||
|
game.do_actions(actions)
|
||||||
11
agame/util.py
Normal file
11
agame/util.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from typing import Iterable, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from agame.item import ItemInst
|
||||||
|
|
||||||
|
|
||||||
|
def search_item_name(seq: Iterable["ItemInst"], item_name: str) -> Optional["ItemInst"]:
|
||||||
|
for item in seq:
|
||||||
|
if item.name == item_name or item_name in item.synonyms:
|
||||||
|
return item
|
||||||
|
return None
|
||||||
98
tests/test_trigger_regex.py
Normal file
98
tests/test_trigger_regex.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
from agame.trigger import *
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_item():
|
||||||
|
cases = [
|
||||||
|
("get item", "item"),
|
||||||
|
("take item", "item"),
|
||||||
|
("grab item", "item"),
|
||||||
|
("pick up item", "item"),
|
||||||
|
("pickup item", "item"),
|
||||||
|
("get the item", "item"),
|
||||||
|
("take the item", "item"),
|
||||||
|
("grab the item", "item"),
|
||||||
|
("pick up the item", "item"),
|
||||||
|
("pickup the item", "item"),
|
||||||
|
("get a item", "item"),
|
||||||
|
("take a item", "item"),
|
||||||
|
("grab a item", "item"),
|
||||||
|
("pick up a item", "item"),
|
||||||
|
("pickup a item", "item"),
|
||||||
|
("get an item", "item"),
|
||||||
|
("take an item", "item"),
|
||||||
|
("grab an item", "item"),
|
||||||
|
("pick up an item", "item"),
|
||||||
|
("pickup an item", "item"),
|
||||||
|
("get item", "item"),
|
||||||
|
("take item", "item"),
|
||||||
|
("grab item", "item"),
|
||||||
|
("pick up item", "item"),
|
||||||
|
("pickup item", "item"),
|
||||||
|
("get the item", "item"),
|
||||||
|
("take the item", "item"),
|
||||||
|
("grab the item", "item"),
|
||||||
|
("pick up the item", "item"),
|
||||||
|
("pickup the item", "item"),
|
||||||
|
("get a item", "item"),
|
||||||
|
("take a item", "item"),
|
||||||
|
("grab a item", "item"),
|
||||||
|
("pick up a item", "item"),
|
||||||
|
("pickup a item", "item"),
|
||||||
|
("get an item", "item"),
|
||||||
|
("take an item", "item"),
|
||||||
|
("grab an item", "item"),
|
||||||
|
("pick up an item", "item"),
|
||||||
|
("pickup an item", "item"),
|
||||||
|
]
|
||||||
|
|
||||||
|
pat = GetTrigger.pattern()
|
||||||
|
for (line, expected) in cases:
|
||||||
|
line = line.strip()
|
||||||
|
match = pat.fullmatch(line)
|
||||||
|
assert match is not None
|
||||||
|
assert match["item"] == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_use_item():
|
||||||
|
cases = [
|
||||||
|
("use item", "item"),
|
||||||
|
("use the item", "item"),
|
||||||
|
("use a item", "item"),
|
||||||
|
("use an item", "item"),
|
||||||
|
("use item", "item"),
|
||||||
|
("use the item", "item"),
|
||||||
|
("use a item", "item"),
|
||||||
|
("use an item", "item"),
|
||||||
|
("use an item", "item"),
|
||||||
|
]
|
||||||
|
|
||||||
|
pat = UseTrigger.pattern()
|
||||||
|
for (line, expected) in cases:
|
||||||
|
line = line.strip()
|
||||||
|
match = pat.fullmatch(line)
|
||||||
|
assert match is not None
|
||||||
|
assert match["item"] == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_use_item_with():
|
||||||
|
cases = [
|
||||||
|
("use item with other item", "item", "other item"),
|
||||||
|
("use the item with an other item", "item", "other item"),
|
||||||
|
("use a item with a other item", "item", "other item"),
|
||||||
|
("use an item with the other item", "item", "other item"),
|
||||||
|
("use item with other item", "item", "other item"),
|
||||||
|
("use the item with an other item", "item", "other item"),
|
||||||
|
("use a item with a other item", "item", "other item"),
|
||||||
|
("use an item with the other item", "item", "other item"),
|
||||||
|
]
|
||||||
|
|
||||||
|
pat = UseTrigger.pattern()
|
||||||
|
for (line, item, other_item) in cases:
|
||||||
|
match = pat.fullmatch(line)
|
||||||
|
assert match is not None
|
||||||
|
assert match["item"] == item
|
||||||
|
assert match["target"] == other_item
|
||||||
|
|
||||||
|
|
||||||
|
# TODO : put
|
||||||
|
# TODO : look
|
||||||
Reference in New Issue
Block a user