diff --git a/.efrocachemap b/.efrocachemap
index a9052269..564cbb66 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -420,20 +420,20 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/04/0a/c4f7d2794b018593ab0b2bcb07f0",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/06/4d/18777c9a2eb2207a2891a2837a70",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/81/90/23ab1ecc8c55267bd904a9c05344",
- "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/cd/7a/dcf7f0f9436884167abdcb126716",
+ "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/f7/81/01515c4bef514f45bfe3b8e6566b",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/a9/3c/63857b3f5f943205d7a5d8f8e476",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/49/5f/b29bb65369040892fe6601801637",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/ea/14/6485c200c717f82cca7c01dee0b3",
- "assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/a0/ca/b4e3c4ea2c76e462b7e657b2b1c2",
+ "assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/1d/a0/c2b68a77207c0fed98d7cec3b22f",
"assets/build/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/bb/9c/360fc084e6254a087096993af219",
"assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/dd/5a/14ca3ebb92a802315921e2b2b215",
"assets/build/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/3f/46/e4da3c1d2b0ebf916df55c608b28",
"assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/d1/07/37b7adc3dbec7328d26c5325f212",
- "assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/8b/c6/3fbabe88f18df228f6f2984201a5",
+ "assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/56/1f/fe2912d87b3af510469dc40bdf85",
"assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/6e/fd/685a4e1da031474d47a1d9eb2731",
"assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/0d/ae/d2df1bf3c2157b4b5302df54622f",
- "assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/ef/92/4a602f11f6dd3d0310ce98cd5538",
- "assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/cb/10/5d94df639e3e0cb405711e1b907f",
+ "assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/fe/33/cead18bf921903f8c6d922017892",
+ "assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/57/7d/e79791913a06c9c15fec75138e6a",
"assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/17/78/3fd0dca40e632ce53d03a944e7fa",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/11/56/ed2b07866104596338f7ce582d64",
"assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/87/2d/027aa239eb66ea8f496562f4fd83",
@@ -3927,16 +3927,16 @@
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
- "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/41/b4/93f8ac61e4b0fad27790b896a79c",
- "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b9/fe/9e8a1711d318c8f446ce42e2c8b4",
- "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/8d/47/ca91d44facaca3201e8b82f487ed",
- "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/64/bc/97857879f57276567e99db0351dc",
- "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/b4/0415d8af3e4904c640991ddd16f9",
- "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d3/86/aa9ca99515ffa5676c960245659f",
- "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a7/22/1fdd6e64721676f8c40fdf8b3e16",
- "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ce/aa/17c53dbb9513d5ef3b8c0ccde6cf",
- "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/d5/3b/4e1fc9281cad80d6cbb5aca74761",
- "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/3c/79/f6476677ef525451a18c18f120d3",
- "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/63/50/b120c428fc03a18adf24c0363985",
- "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/c8/1d/a5f65b32cd9a0fac2415b61d6875"
+ "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ee/45/cade5b2fd62b09a51257941c7533",
+ "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1b/8f/ff4979b5192a38ef1b2ef0ef0875",
+ "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5a/2a/ef6ffc7421f21c196872ffa43101",
+ "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/08/3b/c5cbdf8034bc872f1aa622a64b58",
+ "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/eb/44/cfded3124db547b6a99aa8d7a947",
+ "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/30/d6/5d712b546730fed241aa3f5e5498",
+ "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/69/d3/7af0c2b55abfa753dd148f7199d9",
+ "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/88/16/70cd1b7b50578910267f91d563aa",
+ "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/d3/a8/1e79f6258790a886ef14d4df5dd5",
+ "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/b8/a7/9cb20c8a6cb09880e2cfbbf427cf",
+ "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/ab/14/6173a6f6b7588a335f96e8f1f240",
+ "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/7e/38/cfc24fc3e5e53ffedde3b372eef8"
}
\ No newline at end of file
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index f280d478..4d9e7d52 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -29,8 +29,8 @@
achname
achs
acinstance
- ack
ack'ed
+ ack
acked
acks
acnt
@@ -149,8 +149,8 @@
bacommon
badguy
bafoundation
- ballistica
ballistica's
+ ballistica
ballisticacore
ballisticacorecb
bamaster
@@ -586,6 +586,7 @@
endmessage
endparen
endtime
+ englishkeyboard
ensurepip
entitylist
entrynew
@@ -779,8 +780,8 @@
gamedata
gameinstance
gamemap
- gamepad
gamepad's
+ gamepad
gamepadadvanced
gamepads
gamepadselect
@@ -1019,6 +1020,7 @@
jsonstrbase
jsontools
jsonutils
+ kbclass
kbytecount
keepaway
keeprefs
@@ -1149,8 +1151,8 @@
lsqlite
lssl
lstart
- lstr
lstr's
+ lstr
lstrs
lsval
ltex
@@ -1771,8 +1773,8 @@
sessionname
sessionplayer
sessionplayers
- sessionteam
sessionteam's
+ sessionteam
sessionteams
sessiontype
setactivity
@@ -2098,8 +2100,8 @@
txtw
typeargs
typecheck
- typechecker
typechecker's
+ typechecker
typedval
typeshed
typestr
diff --git a/assets/.asset_manifest_public.json b/assets/.asset_manifest_public.json
index ecfa5dee..cb611882 100644
--- a/assets/.asset_manifest_public.json
+++ b/assets/.asset_manifest_public.json
@@ -1,5 +1,4 @@
[
- "ba_data/python/__pycache__/keyboards.cpython-38.opt-1.pyc",
"ba_data/python/ba/__init__.py",
"ba_data/python/ba/__pycache__/__init__.cpython-38.opt-1.pyc",
"ba_data/python/ba/__pycache__/_account.cpython-38.opt-1.pyc",
@@ -240,6 +239,10 @@
"ba_data/python/bastd/game/targetpractice.py",
"ba_data/python/bastd/game/thelaststand.py",
"ba_data/python/bastd/gameutils.py",
+ "ba_data/python/bastd/keyboard/__init__.py",
+ "ba_data/python/bastd/keyboard/__pycache__/__init__.cpython-38.opt-1.pyc",
+ "ba_data/python/bastd/keyboard/__pycache__/englishkeyboard.cpython-38.opt-1.pyc",
+ "ba_data/python/bastd/keyboard/englishkeyboard.py",
"ba_data/python/bastd/mainmenu.py",
"ba_data/python/bastd/mapdata/__init__.py",
"ba_data/python/bastd/mapdata/__pycache__/__init__.cpython-38.opt-1.pyc",
@@ -500,7 +503,6 @@
"ba_data/python/efro/json.py",
"ba_data/python/efro/terminal.py",
"ba_data/python/efro/util.py",
- "ba_data/python/keyboards.py",
"server/__pycache__/ballisticacore_server.cpython-38.opt-1.pyc",
"server/ballisticacore_server.py"
]
\ No newline at end of file
diff --git a/assets/Makefile b/assets/Makefile
index 31bfb63c..ab0997b1 100644
--- a/assets/Makefile
+++ b/assets/Makefile
@@ -263,6 +263,8 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/bastd/game/targetpractice.py \
build/ba_data/python/bastd/game/thelaststand.py \
build/ba_data/python/bastd/gameutils.py \
+ build/ba_data/python/bastd/keyboard/__init__.py \
+ build/ba_data/python/bastd/keyboard/englishkeyboard.py \
build/ba_data/python/bastd/mainmenu.py \
build/ba_data/python/bastd/mapdata/__init__.py \
build/ba_data/python/bastd/mapdata/big_g.py \
@@ -381,7 +383,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/bastd/ui/trophies.py \
build/ba_data/python/bastd/ui/url.py \
build/ba_data/python/bastd/ui/watch.py \
- build/ba_data/python/keyboards.py \
build/server/ballisticacore_server.py
SCRIPT_TARGETS_PYC_PUBLIC = \
@@ -499,6 +500,8 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/bastd/game/__pycache__/targetpractice.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/game/__pycache__/thelaststand.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/__pycache__/gameutils.cpython-38.opt-1.pyc \
+ build/ba_data/python/bastd/keyboard/__pycache__/__init__.cpython-38.opt-1.pyc \
+ build/ba_data/python/bastd/keyboard/__pycache__/englishkeyboard.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/__pycache__/mainmenu.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/mapdata/__pycache__/__init__.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/mapdata/__pycache__/big_g.cpython-38.opt-1.pyc \
@@ -617,7 +620,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/bastd/ui/__pycache__/trophies.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/ui/__pycache__/url.cpython-38.opt-1.pyc \
build/ba_data/python/bastd/ui/__pycache__/watch.cpython-38.opt-1.pyc \
- build/ba_data/python/__pycache__/keyboards.cpython-38.opt-1.pyc \
build/server/__pycache__/ballisticacore_server.cpython-38.opt-1.pyc
# Rule to copy src asset scripts to dst.
diff --git a/assets/src/ba_data/python/ba/__init__.py b/assets/src/ba_data/python/ba/__init__.py
index a511f9c1..808338ed 100644
--- a/assets/src/ba_data/python/ba/__init__.py
+++ b/assets/src/ba_data/python/ba/__init__.py
@@ -77,7 +77,7 @@ from ba._campaign import Campaign
from ba._gameutils import (GameTip, animate, animate_array, show_damage_count,
timestring, cameraflash)
from ba._general import (WeakCall, Call, existing, Existable,
- verify_object_death, storagename)
+ verify_object_death, storagename, getclass)
from ba._keyboard import Keyboard
from ba._level import Level
from ba._lobby import Lobby, Chooser
diff --git a/assets/src/ba_data/python/ba/_meta.py b/assets/src/ba_data/python/ba/_meta.py
index 9e97ec9e..3e3a8055 100644
--- a/assets/src/ba_data/python/ba/_meta.py
+++ b/assets/src/ba_data/python/ba/_meta.py
@@ -379,13 +379,13 @@ def get_scan_results() -> ScanResults:
def get_game_types() -> List[Type[ba.GameActivity]]:
"""Return available game types."""
- from ba import _general
- from ba import _gameactivity
+ from ba._general import getclass
+ from ba._gameactivity import GameActivity
gameclassnames = get_scan_results().games
gameclasses = []
for gameclassname in gameclassnames:
try:
- cls = _general.getclass(gameclassname, _gameactivity.GameActivity)
+ cls = getclass(gameclassname, GameActivity)
gameclasses.append(cls)
except Exception:
from ba import _error
diff --git a/assets/src/ba_data/python/bastd/keyboard/__init__.py b/assets/src/ba_data/python/bastd/keyboard/__init__.py
new file mode 100644
index 00000000..32622553
--- /dev/null
+++ b/assets/src/ba_data/python/bastd/keyboard/__init__.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2011-2020 Eric Froemling
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# -----------------------------------------------------------------------------
diff --git a/assets/src/ba_data/python/keyboards.py b/assets/src/ba_data/python/bastd/keyboard/englishkeyboard.py
similarity index 100%
rename from assets/src/ba_data/python/keyboards.py
rename to assets/src/ba_data/python/bastd/keyboard/englishkeyboard.py
diff --git a/assets/src/ba_data/python/bastd/ui/onscreenkeyboard.py b/assets/src/ba_data/python/bastd/ui/onscreenkeyboard.py
index 53ab84c2..17a7899a 100644
--- a/assets/src/ba_data/python/bastd/ui/onscreenkeyboard.py
+++ b/assets/src/ba_data/python/bastd/ui/onscreenkeyboard.py
@@ -228,12 +228,19 @@ class OnScreenKeyboardWindow(ba.Window):
color=key_color_dark,
label=ba.Lstr(resource='spaceKeyText'),
on_activate_call=ba.Call(self._type_char, ' '))
- ba.textwidget(parent=self._root_widget,
- h_align='center',
- position=(210, v - 70),
- size=(key_width * 6.1, key_height + 15),
- text='Double press space to change keyboard',
- scale=0.75)
+
+ # Show change instructions only if we have more than one
+ # keyboard option.
+ if (ba.app.metascan is not None
+ and len(ba.app.metascan.keyboards) > 1):
+ ba.textwidget(
+ parent=self._root_widget,
+ h_align='center',
+ position=(210, v - 70),
+ size=(key_width * 6.1, key_height + 15),
+ text=ba.Lstr(
+ resource='keyboardChangeInstructionsText'),
+ scale=0.75)
btn2 = self._space_button
btn3 = self._emoji_button
ba.widget(edit=btn1, right_widget=btn2, left_widget=btn3)
@@ -250,12 +257,9 @@ class OnScreenKeyboardWindow(ba.Window):
def _get_keyboard(self) -> ba.Keyboard:
assert ba.app.metascan is not None
- path = ba.app.metascan.keyboards[self._keyboard_index]
- classname = path.split('.')[-1]
- module = path[:-len(classname) - 1]
- keyboard = getattr(__import__(module), classname)()
- assert isinstance(keyboard, ba.Keyboard)
- return keyboard
+ classname = ba.app.metascan.keyboards[self._keyboard_index]
+ kbclass = ba.getclass(classname, ba.Keyboard)
+ return kbclass()
def _refresh(self) -> None:
chars: Optional[List[str]] = None
@@ -325,9 +329,11 @@ class OnScreenKeyboardWindow(ba.Window):
self._load_keyboard()
if len(ba.app.metascan.keyboards) < 2:
ba.playsound(ba.getsound('error'))
- ba.screenmessage('No other keyboards available', color=(1, 0, 0))
+ ba.screenmessage(ba.Lstr(resource='keyboardNoOthersAvailableText'),
+ color=(1, 0, 0))
else:
- ba.screenmessage(f'Switching keyboard to "{self._keyboard.name}"',
+ ba.screenmessage(ba.Lstr(resource='keyboardSwitchText',
+ subs=[('${NAME}', self._keyboard.name)]),
color=(0, 1, 0))
def _shift(self) -> None:
@@ -360,13 +366,13 @@ class OnScreenKeyboardWindow(ba.Window):
return
self._last_space_press = ba.time(ba.TimeType.REAL)
- # operate in unicode so we don't do anything funky like chop utf-8
- # chars in half
+ # Operate in unicode so we don't do anything funky like chop utf-8
+ # chars in half.
txt = cast(str, ba.textwidget(query=self._text_field))
txt += char
ba.textwidget(edit=self._text_field, text=txt)
- # if we were caps,
- # go back only if not Shift is pressed twice
+
+ # If we were caps, go back only if not Shift is pressed twice.
if self._mode == 'caps' and not self._double_press_shift:
self._mode = 'normal'
self._refresh()
diff --git a/docs/ba_module.md b/docs/ba_module.md
index d2f091d9..0550db84 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -85,6 +85,7 @@
ba.charstr()
ba.do_once()
ba.get_valid_languages()
+ ba.getclass()
ba.is_browser_likely_available()
ba.is_point_in_box()
ba.log()
@@ -6283,6 +6284,17 @@ Activity has since been created or is transitioning in.
If there is no current Activity, raises a ba.ActivityNotFoundError.
If doraise is False, None will be returned instead in that case.
+
+getclass(name: str, subclassof: Type[T]) -> Type[T]
+
+Given a full class name such as foo.bar.MyClass, return the class.
+
+Category: General Utility Functions
+
+The class will be checked to make sure it is a subclass of the provided
+'subclassof' class, and a TypeError will be raised if not.
+
getcollidemodel(name: str) -> ba.CollideModel