mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-29 02:23:22 +08:00
Cleaned up some subsystems such as ba.app.lang and ba.app.ui
This commit is contained in:
parent
819d58d681
commit
2d5c2dda9a
@ -3932,24 +3932,24 @@
|
||||
"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/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a3/b4/d47b1f9ca27dc994d225efc9b652",
|
||||
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a1/20/f75ee36d80a99dbfe1ff79db2093",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/94/91/af4a3be510e2570651fbb8d4c297",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/6c/73657b342cb1666dd3ac734a93c8",
|
||||
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a6/9d/8830abe356b106005b793952f60c",
|
||||
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/93/e7/063a5a038904ef0641d405a5e66d",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/69/e8/77839309e3301d62bbf77624d810",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/95/4e/9e5dbd0b19acddc2cd056eca123c",
|
||||
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/0c/bb/3990f398178b924de0b493db14d8",
|
||||
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/85/14/604f8855cfa461797c5e3c5b5aab",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/e1/83/9d2ffd1a9f149a18c005be57db29",
|
||||
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f6/63/9aaf6704f8dcb6e32808d09989a4",
|
||||
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/15/54/bfba7d740c7221a5d46e8e21c756",
|
||||
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4b/1f/ca36bea671a5b88a7e2ccf2e4c4a",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/57/2d/e4b9a67cb21131cdcdfb8287f9e7",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/59/b6/6ffc20f2c0253180496d2dae968c",
|
||||
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cd/4f/d760d9fce637b61efeed648063cc",
|
||||
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7c/5a/d63a634b3886c9cf1b3697d24b75",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/46/80/98efbaeed954d2b008a9bfb77e12",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/89/24/6aae1e666373c46b409e44d7cdf7"
|
||||
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/68/70/ecdec08c0236f5bebbf298c9f4cd",
|
||||
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/85/7f/53fb1e3f1414b4865315d815b17a",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9e/ae/586229f233660b6b49ca655b7c1b",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/a5/c6b90e3629a8041a68827aafec3f",
|
||||
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/d3/66/82b2cab9b1438c30cfd33eefba5e",
|
||||
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/52/37/253765404b79740cd4a7ac5fe325",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c7/69/ecdbcee9df40225475391beb4cb2",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/34/51/cb528ea6eb5ac853794922a0cc34",
|
||||
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/d4/b8/d3a690970ca379d805432bde8533",
|
||||
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/4d/16/b9014f5d983481731b54f7045dd9",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/1e/11/d3c478923937f376eecb25561ae9",
|
||||
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/d6/b4/b7854676ec826e42aa3ecb394b49",
|
||||
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/76/bf185c1ea65f3c25cfa63666511b",
|
||||
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/49/30/5acd0d56b736a3729e7689cd4e18",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/e8/197874ac6d756c341628ea4bb518",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/50/6f/140a3d77288e2355605c9d0c942a",
|
||||
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1c/ca/62d29425e0ad0b17c145ecbc97fe",
|
||||
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c0/fa/52a38fc153714bff3bcda077ed75",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/75/5b/e84edb24f5df313bb6ebafa91b19",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0f/2e/af28c0026c0379e5441e789e9721"
|
||||
}
|
||||
3
.idea/dictionaries/ericf.xml
generated
3
.idea/dictionaries/ericf.xml
generated
@ -850,6 +850,7 @@
|
||||
<w>getplayer</w>
|
||||
<w>getpt</w>
|
||||
<w>getremote</w>
|
||||
<w>getres</w>
|
||||
<w>getscanresults</w>
|
||||
<w>getscoreconfig</w>
|
||||
<w>getsession</w>
|
||||
@ -1015,6 +1016,7 @@
|
||||
<w>intex</w>
|
||||
<w>intp</w>
|
||||
<w>introspectable</w>
|
||||
<w>iobj</w>
|
||||
<w>ipaddress</w>
|
||||
<w>ipos</w>
|
||||
<w>iprof</w>
|
||||
@ -1359,6 +1361,7 @@
|
||||
<w>nline</w>
|
||||
<w>nlines</w>
|
||||
<w>nntplib</w>
|
||||
<w>noassets</w>
|
||||
<w>nodeactor</w>
|
||||
<w>nodepos</w>
|
||||
<w>nodpi</w>
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
### 1.5.26 (20178)
|
||||
### 1.5.27 (20218)
|
||||
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
|
||||
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
|
||||
|
||||
### 1.5.26 (20217)
|
||||
- Simplified licensing header on python scripts.
|
||||
- General project refactoring in order to open source most of the C++ layer.
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_keyboard.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_lang.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_language.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_map.cpython-38.opt-1.pyc",
|
||||
@ -91,7 +91,7 @@
|
||||
"ba_data/python/ba/_hooks.py",
|
||||
"ba_data/python/ba/_input.py",
|
||||
"ba_data/python/ba/_keyboard.py",
|
||||
"ba_data/python/ba/_lang.py",
|
||||
"ba_data/python/ba/_language.py",
|
||||
"ba_data/python/ba/_level.py",
|
||||
"ba_data/python/ba/_lobby.py",
|
||||
"ba_data/python/ba/_map.py",
|
||||
|
||||
@ -161,7 +161,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
build/ba_data/python/ba/_hooks.py \
|
||||
build/ba_data/python/ba/_input.py \
|
||||
build/ba_data/python/ba/_keyboard.py \
|
||||
build/ba_data/python/ba/_lang.py \
|
||||
build/ba_data/python/ba/_language.py \
|
||||
build/ba_data/python/ba/_level.py \
|
||||
build/ba_data/python/ba/_lobby.py \
|
||||
build/ba_data/python/ba/_map.py \
|
||||
@ -399,7 +399,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
build/ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_keyboard.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_lang.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_language.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_map.cpython-38.opt-1.pyc \
|
||||
|
||||
@ -42,9 +42,10 @@ from ba._gameactivity import GameActivity
|
||||
from ba._gameresults import GameResults
|
||||
from ba._settings import (Setting, IntSetting, FloatSetting, ChoiceSetting,
|
||||
BoolSetting, IntChoiceSetting, FloatChoiceSetting)
|
||||
from ba._lang import Lstr, setlanguage, get_valid_languages
|
||||
from ba._language import Lstr, LanguageSubsystem
|
||||
from ba._map import Map, getmaps
|
||||
from ba._session import Session
|
||||
from ba._ui import UISubsystem
|
||||
from ba._servermode import ServerController
|
||||
from ba._score import ScoreType, ScoreConfig
|
||||
from ba._stats import PlayerScoredMessage, PlayerRecord, Stats
|
||||
@ -70,7 +71,8 @@ from ba._messages import (UNHANDLED, OutOfBoundsMessage, DeathType, DieMessage,
|
||||
ShouldShatterMessage, ImpactDamageMessage,
|
||||
FreezeMessage, ThawMessage, HitMessage,
|
||||
CelebrateMessage)
|
||||
from ba._music import setmusic, MusicPlayer, MusicType, MusicPlayMode
|
||||
from ba._music import (setmusic, MusicPlayer, MusicType, MusicPlayMode,
|
||||
MusicSubsystem)
|
||||
from ba._powerup import PowerupMessage, PowerupAcceptMessage
|
||||
from ba._multiteamsession import MultiTeamSession
|
||||
from ba.ui import Window, UIController, uicleanupcheck
|
||||
|
||||
@ -18,7 +18,7 @@ def handle_account_gained_tickets(count: int) -> None:
|
||||
|
||||
(internal)
|
||||
"""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='getTicketsWindow.receivedTicketsText',
|
||||
subs=[('${COUNT}', str(count))]),
|
||||
color=(0, 1, 0))
|
||||
@ -166,7 +166,7 @@ def have_pro_options() -> bool:
|
||||
|
||||
def show_post_purchase_message() -> None:
|
||||
"""(internal)"""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeType
|
||||
app = _ba.app
|
||||
cur_time = _ba.time(TimeType.REAL)
|
||||
@ -183,14 +183,15 @@ def show_post_purchase_message() -> None:
|
||||
def on_account_state_changed() -> None:
|
||||
"""(internal)"""
|
||||
import time
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
app = _ba.app
|
||||
|
||||
# Run any pending promo codes we had queued up while not signed in.
|
||||
if _ba.get_account_state() == 'signed_in' and app.pending_promo_codes:
|
||||
for code in app.pending_promo_codes:
|
||||
_ba.screenmessage(_lang.Lstr(resource='submittingPromoCodeText'),
|
||||
color=(0, 1, 0))
|
||||
_ba.screenmessage(
|
||||
_language.Lstr(resource='submittingPromoCodeText'),
|
||||
color=(0, 1, 0))
|
||||
_ba.add_transaction({
|
||||
'type': 'PROMO_CODE',
|
||||
'expire_time': time.time() + 5,
|
||||
|
||||
@ -269,7 +269,7 @@ class Achievement:
|
||||
@property
|
||||
def display_name(self) -> ba.Lstr:
|
||||
"""Return a ba.Lstr for this Achievement's name."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
name: Union[ba.Lstr, str]
|
||||
try:
|
||||
if self._level_name != '':
|
||||
@ -289,16 +289,18 @@ class Achievement:
|
||||
@property
|
||||
def description(self) -> ba.Lstr:
|
||||
"""Get a ba.Lstr for the Achievement's brief description."""
|
||||
from ba._lang import Lstr, get_resource
|
||||
if 'description' in get_resource('achievements')[self._name]:
|
||||
from ba._language import Lstr
|
||||
if 'description' in _ba.app.lang.get_resource('achievements')[
|
||||
self._name]:
|
||||
return Lstr(resource='achievements.' + self._name + '.description')
|
||||
return Lstr(resource='achievements.' + self._name + '.descriptionFull')
|
||||
|
||||
@property
|
||||
def description_complete(self) -> ba.Lstr:
|
||||
"""Get a ba.Lstr for the Achievement's description when completed."""
|
||||
from ba._lang import Lstr, get_resource
|
||||
if 'descriptionComplete' in get_resource('achievements')[self._name]:
|
||||
from ba._language import Lstr
|
||||
if 'descriptionComplete' in _ba.app.lang.get_resource('achievements')[
|
||||
self._name]:
|
||||
return Lstr(resource='achievements.' + self._name +
|
||||
'.descriptionComplete')
|
||||
return Lstr(resource='achievements.' + self._name +
|
||||
@ -307,7 +309,7 @@ class Achievement:
|
||||
@property
|
||||
def description_full(self) -> ba.Lstr:
|
||||
"""Get a ba.Lstr for the Achievement's full description."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
|
||||
return Lstr(
|
||||
resource='achievements.' + self._name + '.descriptionFull',
|
||||
@ -318,7 +320,7 @@ class Achievement:
|
||||
@property
|
||||
def description_full_complete(self) -> ba.Lstr:
|
||||
"""Get a ba.Lstr for the Achievement's full desc. when completed."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
return Lstr(
|
||||
resource='achievements.' + self._name + '.descriptionFullComplete',
|
||||
subs=[('${LEVEL}',
|
||||
@ -353,7 +355,7 @@ class Achievement:
|
||||
Shows the Achievement icon, name, and description.
|
||||
"""
|
||||
# pylint: disable=cyclic-import
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import SpecialChar
|
||||
from ba._coopsession import CoopSession
|
||||
from bastd.actor.image import Image
|
||||
@ -657,7 +659,7 @@ class Achievement:
|
||||
from bastd.actor.text import Text
|
||||
from bastd.actor.image import Image
|
||||
from ba._general import WeakCall
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._messages import DieMessage
|
||||
from ba._enums import TimeType, SpecialChar
|
||||
app = _ba.app
|
||||
|
||||
@ -167,7 +167,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
|
||||
def on_begin(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bastd.actor.text import Text
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
super().on_begin()
|
||||
|
||||
# Pop up a 'press any button to continue' statement after our
|
||||
@ -176,9 +176,9 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
|
||||
if _ba.app.ui.uiscale is UIScale.LARGE:
|
||||
# FIXME: Need a better way to determine whether we've probably
|
||||
# got a keyboard.
|
||||
sval = _lang.Lstr(resource='pressAnyKeyButtonText')
|
||||
sval = _language.Lstr(resource='pressAnyKeyButtonText')
|
||||
else:
|
||||
sval = _lang.Lstr(resource='pressAnyButtonText')
|
||||
sval = _language.Lstr(resource='pressAnyButtonText')
|
||||
|
||||
Text(self._custom_continue_message
|
||||
if self._custom_continue_message is not None else sval,
|
||||
|
||||
@ -11,7 +11,7 @@ import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ba
|
||||
from ba import _lang, _meta
|
||||
from ba import _language, _meta
|
||||
from bastd.actor import spazappearance
|
||||
from typing import Optional, Dict, Set, Any, Type, Tuple, Callable, List
|
||||
|
||||
@ -49,87 +49,6 @@ class App:
|
||||
assert isinstance(self._env['config_file_path'], str)
|
||||
return self._env['config_file_path']
|
||||
|
||||
@property
|
||||
def locale(self) -> str:
|
||||
"""Raw country/language code detected by the game (such as 'en_US').
|
||||
|
||||
Generally for language-specific code you should look at
|
||||
ba.App.language, which is the language the game is using
|
||||
(which may differ from locale if the user sets a language, etc.)
|
||||
"""
|
||||
assert isinstance(self._env['locale'], str)
|
||||
return self._env['locale']
|
||||
|
||||
def can_display_language(self, language: str) -> bool:
|
||||
"""Tell whether we can display a particular language.
|
||||
|
||||
(internal)
|
||||
|
||||
On some platforms we don't have unicode rendering yet
|
||||
which limits the languages we can draw.
|
||||
"""
|
||||
|
||||
# We don't yet support full unicode display on windows or linux :-(.
|
||||
if (language in {
|
||||
'Chinese', 'ChineseTraditional', 'Persian', 'Korean', 'Arabic',
|
||||
'Hindi', 'Vietnamese'
|
||||
} and not _ba.can_display_full_unicode()):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_default_language(self) -> str:
|
||||
languages = {
|
||||
'de': 'German',
|
||||
'es': 'Spanish',
|
||||
'sk': 'Slovak',
|
||||
'it': 'Italian',
|
||||
'nl': 'Dutch',
|
||||
'da': 'Danish',
|
||||
'pt': 'Portuguese',
|
||||
'fr': 'French',
|
||||
'el': 'Greek',
|
||||
'ru': 'Russian',
|
||||
'pl': 'Polish',
|
||||
'sv': 'Swedish',
|
||||
'eo': 'Esperanto',
|
||||
'cs': 'Czech',
|
||||
'hr': 'Croatian',
|
||||
'hu': 'Hungarian',
|
||||
'be': 'Belarussian',
|
||||
'ro': 'Romanian',
|
||||
'ko': 'Korean',
|
||||
'fa': 'Persian',
|
||||
'ar': 'Arabic',
|
||||
'zh': 'Chinese',
|
||||
'tr': 'Turkish',
|
||||
'id': 'Indonesian',
|
||||
'sr': 'Serbian',
|
||||
'uk': 'Ukrainian',
|
||||
'vi': 'Vietnamese',
|
||||
'vec': 'Venetian',
|
||||
'hi': 'Hindi'
|
||||
}
|
||||
|
||||
# Special case for Chinese: map specific variations to traditional.
|
||||
# (otherwise will map to 'Chinese' which is simplified)
|
||||
if self.locale in ('zh_HANT', 'zh_TW'):
|
||||
language = 'ChineseTraditional'
|
||||
else:
|
||||
language = languages.get(self.locale[:2], 'English')
|
||||
if not self.can_display_language(language):
|
||||
language = 'English'
|
||||
return language
|
||||
|
||||
@property
|
||||
def language(self) -> str:
|
||||
"""The name of the language the game is running in.
|
||||
|
||||
This can be selected explicitly by the user or may be set
|
||||
automatically based on ba.App.locale or other factors.
|
||||
"""
|
||||
assert isinstance(self.config, dict)
|
||||
return self.config.get('Lang', self.default_language)
|
||||
|
||||
@property
|
||||
def user_agent_string(self) -> str:
|
||||
"""String containing various bits of info about OS/device/etc."""
|
||||
@ -254,7 +173,8 @@ class App:
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
from ba._music import MusicSubsystem
|
||||
from ba._ui import UI
|
||||
from ba._language import LanguageSubsystem
|
||||
from ba._ui import UISubsystem
|
||||
|
||||
# Config.
|
||||
self.config_file_healthy = False
|
||||
@ -283,7 +203,6 @@ class App:
|
||||
self.active_plugins: Dict[str, ba.Plugin] = {}
|
||||
|
||||
# Misc.
|
||||
self.default_language = self._get_default_language()
|
||||
self.metascan: Optional[_meta.ScanResults] = None
|
||||
self.tips: List[str] = []
|
||||
self.stress_test_reset_timer: Optional[ba.Timer] = None
|
||||
@ -326,8 +245,7 @@ class App:
|
||||
self.music = MusicSubsystem()
|
||||
|
||||
# Language.
|
||||
self.language_target: Optional[_lang.AttrDict] = None
|
||||
self.language_merged: Optional[_lang.AttrDict] = None
|
||||
self.lang = LanguageSubsystem()
|
||||
|
||||
# Achievements.
|
||||
self.achievements: List[ba.Achievement] = []
|
||||
@ -358,7 +276,7 @@ class App:
|
||||
self.coop_session_args: Dict = {}
|
||||
|
||||
# UI.
|
||||
self.ui = UI()
|
||||
self.ui = UISubsystem()
|
||||
|
||||
self.value_test_defaults: dict = {}
|
||||
self.first_main_menu = True # FIXME: Move to mainmenu class.
|
||||
@ -551,7 +469,7 @@ class App:
|
||||
activity: Optional[ba.Activity] = _ba.get_foreground_host_activity()
|
||||
if (activity is not None and activity.allow_pausing
|
||||
and not _ba.have_connected_clients()):
|
||||
from ba import _gameutils, _lang
|
||||
from ba import _gameutils, _language
|
||||
from ba._nodeactor import NodeActor
|
||||
|
||||
# FIXME: Shouldn't be touching scene stuff here;
|
||||
@ -567,10 +485,14 @@ class App:
|
||||
_ba.newnode(
|
||||
'text',
|
||||
attrs={
|
||||
'text': _lang.Lstr(resource='pausedByHostText'),
|
||||
'client_only': True,
|
||||
'flatness': 1.0,
|
||||
'h_align': 'center'
|
||||
'text':
|
||||
_language.Lstr(resource='pausedByHostText'),
|
||||
'client_only':
|
||||
True,
|
||||
'flatness':
|
||||
1.0,
|
||||
'h_align':
|
||||
'center'
|
||||
}))
|
||||
|
||||
def resume(self) -> None:
|
||||
@ -705,7 +627,7 @@ class App:
|
||||
|
||||
def do_remove_in_game_ads_message(self) -> None:
|
||||
"""(internal)"""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeType
|
||||
|
||||
# Print this message once every 10 minutes at most.
|
||||
@ -730,7 +652,7 @@ class App:
|
||||
|
||||
def handle_deep_link(self, url: str) -> None:
|
||||
"""Handle a deep link URL."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeType
|
||||
appname = _ba.appname()
|
||||
if url.startswith(f'{appname}://code/'):
|
||||
|
||||
@ -40,8 +40,8 @@ def is_browser_likely_available() -> bool:
|
||||
|
||||
def get_remote_app_name() -> ba.Lstr:
|
||||
"""(internal)"""
|
||||
from ba import _lang
|
||||
return _lang.Lstr(resource='remote_app.app_name')
|
||||
from ba import _language
|
||||
return _language.Lstr(resource='remote_app.app_name')
|
||||
|
||||
|
||||
def should_submit_debug_info() -> bool:
|
||||
@ -213,15 +213,15 @@ def print_live_object_warnings(when: Any,
|
||||
|
||||
def print_corrupt_file_error() -> None:
|
||||
"""Print an error if a corrupt file is found."""
|
||||
from ba._lang import get_resource
|
||||
from ba._general import Call
|
||||
from ba._enums import TimeType
|
||||
_ba.timer(
|
||||
2.0,
|
||||
lambda: _ba.screenmessage(get_resource('internal.corruptFileText').
|
||||
replace('${EMAIL}', 'support@froemling.net'),
|
||||
color=(1, 0, 0)),
|
||||
timetype=TimeType.REAL)
|
||||
_ba.timer(2.0,
|
||||
lambda: _ba.screenmessage(
|
||||
_ba.app.lang.get_resource('internal.corruptFileText').
|
||||
replace('${EMAIL}', 'support@froemling.net'),
|
||||
color=(1, 0, 0),
|
||||
),
|
||||
timetype=TimeType.REAL)
|
||||
_ba.timer(2.0,
|
||||
Call(_ba.playsound, _ba.getsound('error')),
|
||||
timetype=TimeType.REAL)
|
||||
|
||||
@ -151,13 +151,14 @@ def run_media_reload_benchmark() -> None:
|
||||
def delay_add(start_time: float) -> None:
|
||||
|
||||
def doit(start_time_2: float) -> None:
|
||||
from ba import _lang
|
||||
_ba.screenmessage(
|
||||
_lang.get_resource('debugWindow.totalReloadTimeText').replace(
|
||||
'${TIME}', str(_ba.time(TimeType.REAL) - start_time_2)))
|
||||
_ba.app.lang.get_resource(
|
||||
'debugWindow.totalReloadTimeText').replace(
|
||||
'${TIME}',
|
||||
str(_ba.time(TimeType.REAL) - start_time_2)))
|
||||
_ba.print_load_info()
|
||||
if _ba.app.config.resolve('Texture Quality') != 'High':
|
||||
_ba.screenmessage(_lang.get_resource(
|
||||
_ba.screenmessage(_ba.app.lang.get_resource(
|
||||
'debugWindow.reloadBenchmarkBestResultsText'),
|
||||
color=(1, 1, 0))
|
||||
|
||||
|
||||
@ -14,7 +14,10 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class Collision:
|
||||
"""A class providing info about occurring collisions."""
|
||||
"""A class providing info about occurring collisions.
|
||||
|
||||
Category: Gameplay Classes
|
||||
"""
|
||||
|
||||
@property
|
||||
def position(self) -> ba.Vec3:
|
||||
@ -62,5 +65,8 @@ _collision = Collision()
|
||||
|
||||
|
||||
def getcollision() -> Collision:
|
||||
"""Return the in-progress collision."""
|
||||
"""Return the in-progress collision.
|
||||
|
||||
Category: Gameplay Functions
|
||||
"""
|
||||
return _collision
|
||||
|
||||
@ -139,7 +139,7 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]):
|
||||
def _show_remaining_achievements(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from ba._achievement import get_achievements_for_coop_level
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from bastd.actor.text import Text
|
||||
ts_h_offs = 30
|
||||
v_offs = -200
|
||||
|
||||
@ -229,7 +229,7 @@ class CoopSession(Session):
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=cyclic-import
|
||||
from ba._activitytypes import JoinActivity, TransitionActivity
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._general import WeakCall
|
||||
from ba._coopgame import CoopGameActivity
|
||||
from ba._gameresults import GameResults
|
||||
|
||||
@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, TypeVar
|
||||
|
||||
from ba._activity import Activity
|
||||
from ba._score import ScoreConfig
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._messages import PlayerDiedMessage, StandMessage
|
||||
from ba._error import NotFoundError, print_error, print_exception
|
||||
from ba._general import Call, WeakCall
|
||||
|
||||
@ -106,7 +106,7 @@ class GameResults:
|
||||
(properly formatted for the score type.)
|
||||
"""
|
||||
from ba._gameutils import timestring
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeFormat
|
||||
from ba._score import ScoreType
|
||||
if not self._game_set:
|
||||
|
||||
@ -267,7 +267,7 @@ def timestring(timeval: float,
|
||||
use a 'timedisplay' Node and attribute connections.
|
||||
|
||||
"""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
|
||||
# Temp sanity check while we transition from milliseconds to seconds
|
||||
# based time values.
|
||||
|
||||
@ -40,31 +40,31 @@ def set_config_fullscreen_off() -> None:
|
||||
|
||||
|
||||
def not_signed_in_screen_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='notSignedInErrorText'))
|
||||
|
||||
|
||||
def connecting_to_party_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='internal.connectingToPartyText'),
|
||||
color=(1, 1, 1))
|
||||
|
||||
|
||||
def rejecting_invite_already_in_party_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(
|
||||
Lstr(resource='internal.rejectingInviteAlreadyInPartyText'),
|
||||
color=(1, 0.5, 0))
|
||||
|
||||
|
||||
def connection_failed_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='internal.connectionFailedText'),
|
||||
color=(1, 0.5, 0))
|
||||
|
||||
|
||||
def temporarily_unavailable_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
_ba.screenmessage(
|
||||
Lstr(resource='getTicketsWindow.unavailableTemporarilyText'),
|
||||
@ -72,20 +72,20 @@ def temporarily_unavailable_message() -> None:
|
||||
|
||||
|
||||
def in_progress_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
_ba.screenmessage(Lstr(resource='getTicketsWindow.inProgressText'),
|
||||
color=(1, 0, 0))
|
||||
|
||||
|
||||
def error_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
_ba.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
|
||||
|
||||
def purchase_not_valid_error() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
_ba.screenmessage(Lstr(resource='store.purchaseNotValidError',
|
||||
subs=[('${EMAIL}', 'support@froemling.net')]),
|
||||
@ -93,28 +93,28 @@ def purchase_not_valid_error() -> None:
|
||||
|
||||
|
||||
def purchase_already_in_progress_error() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
_ba.screenmessage(Lstr(resource='store.purchaseAlreadyInProgressText'),
|
||||
color=(1, 0, 0))
|
||||
|
||||
|
||||
def gear_vr_controller_warning() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
_ba.screenmessage(Lstr(resource='usesExternalControllerText'),
|
||||
color=(1, 0, 0))
|
||||
|
||||
|
||||
def orientation_reset_cb_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(
|
||||
Lstr(resource='internal.vrOrientationResetCardboardText'),
|
||||
color=(0, 1, 0))
|
||||
|
||||
|
||||
def orientation_reset_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='internal.vrOrientationResetText'),
|
||||
color=(0, 1, 0))
|
||||
|
||||
@ -133,8 +133,8 @@ def launch_main_menu_session() -> None:
|
||||
|
||||
|
||||
def language_test_toggle() -> None:
|
||||
from ba._lang import setlanguage
|
||||
setlanguage('Gibberish' if _ba.app.language == 'English' else 'English')
|
||||
_ba.app.lang.setlanguage('Gibberish' if _ba.app.lang.language ==
|
||||
'English' else 'English')
|
||||
|
||||
|
||||
def award_in_control_achievement() -> None:
|
||||
@ -156,7 +156,7 @@ def launch_coop_game(name: str) -> None:
|
||||
|
||||
|
||||
def purchases_restored_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='getTicketsWindow.purchasesRestoredText'),
|
||||
color=(0, 1, 0))
|
||||
|
||||
@ -168,7 +168,7 @@ def dismiss_wii_remotes_window() -> None:
|
||||
|
||||
|
||||
def unavailable_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='getTicketsWindow.unavailableText'),
|
||||
color=(1, 0, 0))
|
||||
|
||||
@ -185,7 +185,7 @@ def set_last_ad_network(sval: str) -> None:
|
||||
|
||||
|
||||
def no_game_circle_message() -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
_ba.screenmessage(Lstr(resource='noGameCircleText'), color=(1, 0, 0))
|
||||
|
||||
|
||||
@ -250,7 +250,7 @@ def read_config() -> None:
|
||||
|
||||
def ui_remote_press() -> None:
|
||||
"""Handle a press by a remote device that is only usable for nav."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
|
||||
# Can be called without a context; need a context for getsound.
|
||||
with _ba.Context('ui'):
|
||||
|
||||
@ -1,464 +0,0 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Language related functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ba
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union, Sequence
|
||||
|
||||
|
||||
class Lstr:
|
||||
"""Used to define strings in a language-independent way.
|
||||
|
||||
category: General Utility Classes
|
||||
|
||||
These should be used whenever possible in place of hard-coded strings
|
||||
so that in-game or UI elements show up correctly on all clients in their
|
||||
currently-active language.
|
||||
|
||||
To see available resource keys, look at any of the bs_language_*.py files
|
||||
in the game or the translations pages at bombsquadgame.com/translate.
|
||||
|
||||
# EXAMPLE 1: specify a string from a resource path
|
||||
mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText')
|
||||
|
||||
# EXAMPLE 2: specify a translated string via a category and english value;
|
||||
# if a translated value is available, it will be used; otherwise the
|
||||
# english value will be. To see available translation categories, look
|
||||
# under the 'translations' resource section.
|
||||
mynode.text = ba.Lstr(translate=('gameDescriptions', 'Defeat all enemies'))
|
||||
|
||||
# EXAMPLE 3: specify a raw value and some substitutions. Substitutions can
|
||||
# be used with resource and translate modes as well.
|
||||
mynode.text = ba.Lstr(value='${A} / ${B}',
|
||||
subs=[('${A}', str(score)), ('${B}', str(total))])
|
||||
|
||||
# EXAMPLE 4: Lstrs can be nested. This example would display the resource
|
||||
# at res_a but replace ${NAME} with the value of the resource at res_b
|
||||
mytextnode.text = ba.Lstr(resource='res_a',
|
||||
subs=[('${NAME}', ba.Lstr(resource='res_b'))])
|
||||
"""
|
||||
|
||||
# pylint: disable=redefined-outer-name, dangerous-default-value
|
||||
# noinspection PyDefaultArgument
|
||||
@overload
|
||||
def __init__(self,
|
||||
*,
|
||||
resource: str,
|
||||
fallback_resource: str = '',
|
||||
fallback_value: str = '',
|
||||
subs: Sequence[Tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr from a string resource."""
|
||||
...
|
||||
|
||||
# noinspection PyShadowingNames,PyDefaultArgument
|
||||
@overload
|
||||
def __init__(self,
|
||||
*,
|
||||
translate: Tuple[str, str],
|
||||
subs: Sequence[Tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr by translating a string in a category."""
|
||||
...
|
||||
|
||||
# noinspection PyDefaultArgument
|
||||
@overload
|
||||
def __init__(self,
|
||||
*,
|
||||
value: str,
|
||||
subs: Sequence[Tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr from a raw string value."""
|
||||
...
|
||||
|
||||
# pylint: enable=redefined-outer-name, dangerous-default-value
|
||||
|
||||
def __init__(self, *args: Any, **keywds: Any) -> None:
|
||||
"""Instantiate a Lstr.
|
||||
|
||||
Pass a value for either 'resource', 'translate',
|
||||
or 'value'. (see Lstr help for examples).
|
||||
'subs' can be a sequence of 2-member sequences consisting of values
|
||||
and replacements.
|
||||
'fallback_resource' can be a resource key that will be used if the
|
||||
main one is not present for
|
||||
the current language in place of falling back to the english value
|
||||
('resource' mode only).
|
||||
'fallback_value' can be a literal string that will be used if neither
|
||||
the resource nor the fallback resource is found ('resource' mode only).
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
if args:
|
||||
raise TypeError('Lstr accepts only keyword arguments')
|
||||
|
||||
# Basically just store the exact args they passed.
|
||||
# However if they passed any Lstr values for subs,
|
||||
# replace them with that Lstr's dict.
|
||||
self.args = keywds
|
||||
our_type = type(self)
|
||||
|
||||
if isinstance(self.args.get('value'), our_type):
|
||||
raise TypeError("'value' must be a regular string; not an Lstr")
|
||||
|
||||
if 'subs' in self.args:
|
||||
subs_new = []
|
||||
for key, value in keywds['subs']:
|
||||
if isinstance(value, our_type):
|
||||
subs_new.append((key, value.args))
|
||||
else:
|
||||
subs_new.append((key, value))
|
||||
self.args['subs'] = subs_new
|
||||
|
||||
# As of protocol 31 we support compact key names
|
||||
# ('t' instead of 'translate', etc). Convert as needed.
|
||||
if 'translate' in keywds:
|
||||
keywds['t'] = keywds['translate']
|
||||
del keywds['translate']
|
||||
if 'resource' in keywds:
|
||||
keywds['r'] = keywds['resource']
|
||||
del keywds['resource']
|
||||
if 'value' in keywds:
|
||||
keywds['v'] = keywds['value']
|
||||
del keywds['value']
|
||||
if 'fallback' in keywds:
|
||||
from ba import _error
|
||||
_error.print_error(
|
||||
'deprecated "fallback" arg passed to Lstr(); use '
|
||||
'either "fallback_resource" or "fallback_value"',
|
||||
once=True)
|
||||
keywds['f'] = keywds['fallback']
|
||||
del keywds['fallback']
|
||||
if 'fallback_resource' in keywds:
|
||||
keywds['f'] = keywds['fallback_resource']
|
||||
del keywds['fallback_resource']
|
||||
if 'subs' in keywds:
|
||||
keywds['s'] = keywds['subs']
|
||||
del keywds['subs']
|
||||
if 'fallback_value' in keywds:
|
||||
keywds['fv'] = keywds['fallback_value']
|
||||
del keywds['fallback_value']
|
||||
|
||||
def evaluate(self) -> str:
|
||||
"""Evaluate the Lstr and returns a flat string in the current language.
|
||||
|
||||
You should avoid doing this as much as possible and instead pass
|
||||
and store Lstr values.
|
||||
"""
|
||||
return _ba.evaluate_lstr(self._get_json())
|
||||
|
||||
def is_flat_value(self) -> bool:
|
||||
"""Return whether the Lstr is a 'flat' value.
|
||||
|
||||
This is defined as a simple string value incorporating no translations,
|
||||
resources, or substitutions. In this case it may be reasonable to
|
||||
replace it with a raw string value, perform string manipulation on it,
|
||||
etc.
|
||||
"""
|
||||
return bool('v' in self.args and not self.args.get('s', []))
|
||||
|
||||
def _get_json(self) -> str:
|
||||
try:
|
||||
return json.dumps(self.args, separators=(',', ':'))
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('_get_json failed for', self.args)
|
||||
return 'JSON_ERR'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return '<ba.Lstr: ' + self._get_json() + '>'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return '<ba.Lstr: ' + self._get_json() + '>'
|
||||
|
||||
@staticmethod
|
||||
def from_json(json_string: str) -> ba.Lstr:
|
||||
"""Given a json string, returns a ba.Lstr. Does no data validation."""
|
||||
lstr = Lstr(value='')
|
||||
lstr.args = json.loads(json_string)
|
||||
return lstr
|
||||
|
||||
|
||||
def setlanguage(language: Optional[str],
|
||||
print_change: bool = True,
|
||||
store_to_config: bool = True) -> None:
|
||||
"""Set the active language used for the game.
|
||||
|
||||
category: General Utility Functions
|
||||
|
||||
Pass None to use OS default language.
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
cfg = _ba.app.config
|
||||
cur_language = cfg.get('Lang', None)
|
||||
|
||||
# Store this in the config if its changing.
|
||||
if language != cur_language and store_to_config:
|
||||
if language is None:
|
||||
if 'Lang' in cfg:
|
||||
del cfg['Lang'] # Clear it out for default.
|
||||
else:
|
||||
cfg['Lang'] = language
|
||||
cfg.commit()
|
||||
switched = True
|
||||
else:
|
||||
switched = False
|
||||
|
||||
with open('ba_data/data/languages/english.json') as infile:
|
||||
lenglishvalues = json.loads(infile.read())
|
||||
|
||||
# None implies default.
|
||||
if language is None:
|
||||
language = _ba.app.default_language
|
||||
try:
|
||||
if language == 'English':
|
||||
lmodvalues = None
|
||||
else:
|
||||
lmodfile = 'ba_data/data/languages/' + language.lower() + '.json'
|
||||
with open(lmodfile) as infile:
|
||||
lmodvalues = json.loads(infile.read())
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('Exception importing language:', language)
|
||||
_ba.screenmessage("Error setting language to '" + language +
|
||||
"'; see log for details",
|
||||
color=(1, 0, 0))
|
||||
switched = False
|
||||
lmodvalues = None
|
||||
|
||||
# Create an attrdict of *just* our target language.
|
||||
_ba.app.language_target = AttrDict()
|
||||
langtarget = _ba.app.language_target
|
||||
assert langtarget is not None
|
||||
_add_to_attr_dict(langtarget,
|
||||
lmodvalues if lmodvalues is not None else lenglishvalues)
|
||||
|
||||
# Create an attrdict of our target language overlaid on our base (english).
|
||||
languages = [lenglishvalues]
|
||||
if lmodvalues is not None:
|
||||
languages.append(lmodvalues)
|
||||
lfull = AttrDict()
|
||||
for lmod in languages:
|
||||
_add_to_attr_dict(lfull, lmod)
|
||||
_ba.app.language_merged = lfull
|
||||
|
||||
# Pass some keys/values in for low level code to use;
|
||||
# start with everything in their 'internal' section.
|
||||
internal_vals = [
|
||||
v for v in list(lfull['internal'].items()) if isinstance(v[1], str)
|
||||
]
|
||||
|
||||
# Cherry-pick various other values to include.
|
||||
# (should probably get rid of the 'internal' section
|
||||
# and do everything this way)
|
||||
for value in [
|
||||
'replayNameDefaultText', 'replayWriteErrorText',
|
||||
'replayVersionErrorText', 'replayReadErrorText'
|
||||
]:
|
||||
internal_vals.append((value, lfull[value]))
|
||||
internal_vals.append(
|
||||
('axisText', lfull['configGamepadWindow']['axisText']))
|
||||
internal_vals.append(('buttonText', lfull['buttonText']))
|
||||
lmerged = _ba.app.language_merged
|
||||
assert lmerged is not None
|
||||
random_names = [
|
||||
n.strip() for n in lmerged['randomPlayerNamesText'].split(',')
|
||||
]
|
||||
random_names = [n for n in random_names if n != '']
|
||||
_ba.set_internal_language_keys(internal_vals, random_names)
|
||||
if switched and print_change:
|
||||
_ba.screenmessage(Lstr(resource='languageSetText',
|
||||
subs=[('${LANGUAGE}',
|
||||
Lstr(translate=('languages', language)))
|
||||
]),
|
||||
color=(0, 1, 0))
|
||||
|
||||
|
||||
def _add_to_attr_dict(dst: AttrDict, src: Dict) -> None:
|
||||
for key, value in list(src.items()):
|
||||
if isinstance(value, dict):
|
||||
try:
|
||||
dst_dict = dst[key]
|
||||
except Exception:
|
||||
dst_dict = dst[key] = AttrDict()
|
||||
if not isinstance(dst_dict, AttrDict):
|
||||
raise RuntimeError("language key '" + key +
|
||||
"' is defined both as a dict and value")
|
||||
_add_to_attr_dict(dst_dict, value)
|
||||
else:
|
||||
if not isinstance(value, (float, int, bool, str, str, type(None))):
|
||||
raise TypeError("invalid value type for res '" + key + "': " +
|
||||
str(type(value)))
|
||||
dst[key] = value
|
||||
|
||||
|
||||
class AttrDict(dict):
|
||||
"""A dict that can be accessed with dot notation.
|
||||
|
||||
(so foo.bar is equivalent to foo['bar'])
|
||||
"""
|
||||
|
||||
def __getattr__(self, attr: str) -> Any:
|
||||
val = self[attr]
|
||||
assert not isinstance(val, bytes)
|
||||
return val
|
||||
|
||||
def __setattr__(self, attr: str, value: Any) -> None:
|
||||
raise Exception()
|
||||
|
||||
|
||||
def get_resource(resource: str,
|
||||
fallback_resource: str = None,
|
||||
fallback_value: Any = None) -> Any:
|
||||
"""Return a translation resource by name."""
|
||||
try:
|
||||
# If we have no language set, go ahead and set it.
|
||||
if _ba.app.language_merged is None:
|
||||
language = _ba.app.language
|
||||
try:
|
||||
setlanguage(language,
|
||||
print_change=False,
|
||||
store_to_config=False)
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('exception setting language to',
|
||||
language)
|
||||
|
||||
# Try english as a fallback.
|
||||
if language != 'English':
|
||||
print('Resorting to fallback language (English)')
|
||||
try:
|
||||
setlanguage('English',
|
||||
print_change=False,
|
||||
store_to_config=False)
|
||||
except Exception:
|
||||
_error.print_exception(
|
||||
'error setting language to english fallback')
|
||||
|
||||
# If they provided a fallback_resource value, try the
|
||||
# target-language-only dict first and then fall back to trying the
|
||||
# fallback_resource value in the merged dict.
|
||||
if fallback_resource is not None:
|
||||
try:
|
||||
values = _ba.app.language_target
|
||||
splits = resource.split('.')
|
||||
dicts = splits[:-1]
|
||||
key = splits[-1]
|
||||
for dct in dicts:
|
||||
assert values is not None
|
||||
values = values[dct]
|
||||
assert values is not None
|
||||
val = values[key]
|
||||
return val
|
||||
except Exception:
|
||||
# FIXME: Shouldn't we try the fallback resource in the merged
|
||||
# dict AFTER we try the main resource in the merged dict?
|
||||
try:
|
||||
values = _ba.app.language_merged
|
||||
splits = fallback_resource.split('.')
|
||||
dicts = splits[:-1]
|
||||
key = splits[-1]
|
||||
for dct in dicts:
|
||||
assert values is not None
|
||||
values = values[dct]
|
||||
assert values is not None
|
||||
val = values[key]
|
||||
return val
|
||||
|
||||
except Exception:
|
||||
# If we got nothing for fallback_resource, default to the
|
||||
# normal code which checks or primary value in the merge
|
||||
# dict; there's a chance we can get an english value for
|
||||
# it (which we weren't looking for the first time through).
|
||||
pass
|
||||
|
||||
values = _ba.app.language_merged
|
||||
splits = resource.split('.')
|
||||
dicts = splits[:-1]
|
||||
key = splits[-1]
|
||||
for dct in dicts:
|
||||
assert values is not None
|
||||
values = values[dct]
|
||||
assert values is not None
|
||||
val = values[key]
|
||||
return val
|
||||
|
||||
except Exception:
|
||||
# Ok, looks like we couldn't find our main or fallback resource
|
||||
# anywhere. Now if we've been given a fallback value, return it;
|
||||
# otherwise fail.
|
||||
from ba import _error
|
||||
if fallback_value is not None:
|
||||
return fallback_value
|
||||
raise _error.NotFoundError(
|
||||
f"Resource not found: '{resource}'") from None
|
||||
|
||||
|
||||
def translate(category: str,
|
||||
strval: str,
|
||||
raise_exceptions: bool = False,
|
||||
print_errors: bool = False) -> str:
|
||||
"""Translate a value (or return the value if no translation available)
|
||||
|
||||
Generally you should use ba.Lstr which handles dynamic translation,
|
||||
as opposed to this which returns a flat string.
|
||||
"""
|
||||
try:
|
||||
translated = get_resource('translations')[category][strval]
|
||||
except Exception as exc:
|
||||
if raise_exceptions:
|
||||
raise
|
||||
if print_errors:
|
||||
print(('Translate error: category=\'' + category + '\' name=\'' +
|
||||
strval + '\' exc=' + str(exc) + ''))
|
||||
translated = None
|
||||
translated_out: str
|
||||
if translated is None:
|
||||
translated_out = strval
|
||||
else:
|
||||
translated_out = translated
|
||||
assert isinstance(translated_out, str)
|
||||
return translated_out
|
||||
|
||||
|
||||
def get_valid_languages() -> List[str]:
|
||||
"""Return a list containing names of all available languages.
|
||||
|
||||
category: General Utility Functions
|
||||
|
||||
Languages that may be present but are not displayable on the running
|
||||
version of the game are ignored.
|
||||
"""
|
||||
langs = set()
|
||||
app = _ba.app
|
||||
try:
|
||||
names = os.listdir('ba_data/data/languages')
|
||||
names = [n.replace('.json', '').capitalize() for n in names]
|
||||
|
||||
# FIXME: our simple capitalization fails on multi-word names;
|
||||
# should handle this in a better way...
|
||||
for i, name in enumerate(names):
|
||||
if name == 'Chinesetraditional':
|
||||
names[i] = 'ChineseTraditional'
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception()
|
||||
names = []
|
||||
for name in names:
|
||||
if app.can_display_language(name):
|
||||
langs.add(name)
|
||||
return sorted(name for name in names if app.can_display_language(name))
|
||||
|
||||
|
||||
def is_custom_unicode_char(char: str) -> bool:
|
||||
"""Return whether a char is in the custom unicode range we use."""
|
||||
assert isinstance(char, str)
|
||||
if len(char) != 1:
|
||||
raise ValueError('Invalid Input; must be length 1')
|
||||
return 0xE000 <= ord(char) <= 0xF8FF
|
||||
562
assets/src/ba_data/python/ba/_language.py
Normal file
562
assets/src/ba_data/python/ba/_language.py
Normal file
@ -0,0 +1,562 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Language related functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ba
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union, Sequence
|
||||
|
||||
|
||||
class LanguageSubsystem:
|
||||
"""Wraps up language related app functionality.
|
||||
|
||||
Category: App Classes
|
||||
|
||||
To use this class, access the single instance of it at 'ba.app.lang'.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.language_target: Optional[AttrDict] = None
|
||||
self.language_merged: Optional[AttrDict] = None
|
||||
self.default_language = self._get_default_language()
|
||||
|
||||
def _can_display_language(self, language: str) -> bool:
|
||||
"""Tell whether we can display a particular language.
|
||||
|
||||
On some platforms we don't have unicode rendering yet
|
||||
which limits the languages we can draw.
|
||||
"""
|
||||
|
||||
# We don't yet support full unicode display on windows or linux :-(.
|
||||
if (language in {
|
||||
'Chinese', 'ChineseTraditional', 'Persian', 'Korean', 'Arabic',
|
||||
'Hindi', 'Vietnamese'
|
||||
} and not _ba.can_display_full_unicode()):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def locale(self) -> str:
|
||||
"""Raw country/language code detected by the game (such as 'en_US').
|
||||
|
||||
Generally for language-specific code you should look at
|
||||
ba.App.language, which is the language the game is using
|
||||
(which may differ from locale if the user sets a language, etc.)
|
||||
"""
|
||||
env = _ba.env()
|
||||
assert isinstance(env['locale'], str)
|
||||
return env['locale']
|
||||
|
||||
def _get_default_language(self) -> str:
|
||||
languages = {
|
||||
'de': 'German',
|
||||
'es': 'Spanish',
|
||||
'sk': 'Slovak',
|
||||
'it': 'Italian',
|
||||
'nl': 'Dutch',
|
||||
'da': 'Danish',
|
||||
'pt': 'Portuguese',
|
||||
'fr': 'French',
|
||||
'el': 'Greek',
|
||||
'ru': 'Russian',
|
||||
'pl': 'Polish',
|
||||
'sv': 'Swedish',
|
||||
'eo': 'Esperanto',
|
||||
'cs': 'Czech',
|
||||
'hr': 'Croatian',
|
||||
'hu': 'Hungarian',
|
||||
'be': 'Belarussian',
|
||||
'ro': 'Romanian',
|
||||
'ko': 'Korean',
|
||||
'fa': 'Persian',
|
||||
'ar': 'Arabic',
|
||||
'zh': 'Chinese',
|
||||
'tr': 'Turkish',
|
||||
'id': 'Indonesian',
|
||||
'sr': 'Serbian',
|
||||
'uk': 'Ukrainian',
|
||||
'vi': 'Vietnamese',
|
||||
'vec': 'Venetian',
|
||||
'hi': 'Hindi'
|
||||
}
|
||||
|
||||
# Special case for Chinese: map specific variations to traditional.
|
||||
# (otherwise will map to 'Chinese' which is simplified)
|
||||
if self.locale in ('zh_HANT', 'zh_TW'):
|
||||
language = 'ChineseTraditional'
|
||||
else:
|
||||
language = languages.get(self.locale[:2], 'English')
|
||||
if not self._can_display_language(language):
|
||||
language = 'English'
|
||||
return language
|
||||
|
||||
@property
|
||||
def language(self) -> str:
|
||||
"""The name of the language the game is running in.
|
||||
|
||||
This can be selected explicitly by the user or may be set
|
||||
automatically based on ba.App.locale or other factors.
|
||||
"""
|
||||
assert isinstance(_ba.app.config, dict)
|
||||
return _ba.app.config.get('Lang', self.default_language)
|
||||
|
||||
@property
|
||||
def available_languages(self) -> List[str]:
|
||||
"""A list of all available languages.
|
||||
|
||||
Note that languages that may be present in game assets but which
|
||||
are not displayable on the running version of the game are not
|
||||
included here.
|
||||
"""
|
||||
langs = set()
|
||||
try:
|
||||
names = os.listdir('ba_data/data/languages')
|
||||
names = [n.replace('.json', '').capitalize() for n in names]
|
||||
|
||||
# FIXME: our simple capitalization fails on multi-word names;
|
||||
# should handle this in a better way...
|
||||
for i, name in enumerate(names):
|
||||
if name == 'Chinesetraditional':
|
||||
names[i] = 'ChineseTraditional'
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception()
|
||||
names = []
|
||||
for name in names:
|
||||
if self._can_display_language(name):
|
||||
langs.add(name)
|
||||
return sorted(name for name in names
|
||||
if self._can_display_language(name))
|
||||
|
||||
def setlanguage(self,
|
||||
language: Optional[str],
|
||||
print_change: bool = True,
|
||||
store_to_config: bool = True) -> None:
|
||||
"""Set the active language used for the game.
|
||||
|
||||
Pass None to use OS default language.
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
cfg = _ba.app.config
|
||||
cur_language = cfg.get('Lang', None)
|
||||
|
||||
# Store this in the config if its changing.
|
||||
if language != cur_language and store_to_config:
|
||||
if language is None:
|
||||
if 'Lang' in cfg:
|
||||
del cfg['Lang'] # Clear it out for default.
|
||||
else:
|
||||
cfg['Lang'] = language
|
||||
cfg.commit()
|
||||
switched = True
|
||||
else:
|
||||
switched = False
|
||||
|
||||
with open('ba_data/data/languages/english.json') as infile:
|
||||
lenglishvalues = json.loads(infile.read())
|
||||
|
||||
# None implies default.
|
||||
if language is None:
|
||||
language = self.default_language
|
||||
try:
|
||||
if language == 'English':
|
||||
lmodvalues = None
|
||||
else:
|
||||
lmodfile = 'ba_data/data/languages/' + language.lower(
|
||||
) + '.json'
|
||||
with open(lmodfile) as infile:
|
||||
lmodvalues = json.loads(infile.read())
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('Exception importing language:', language)
|
||||
_ba.screenmessage("Error setting language to '" + language +
|
||||
"'; see log for details",
|
||||
color=(1, 0, 0))
|
||||
switched = False
|
||||
lmodvalues = None
|
||||
|
||||
# Create an attrdict of *just* our target language.
|
||||
self.language_target = AttrDict()
|
||||
langtarget = self.language_target
|
||||
assert langtarget is not None
|
||||
_add_to_attr_dict(
|
||||
langtarget,
|
||||
lmodvalues if lmodvalues is not None else lenglishvalues)
|
||||
|
||||
# Create an attrdict of our target language overlaid
|
||||
# on our base (english).
|
||||
languages = [lenglishvalues]
|
||||
if lmodvalues is not None:
|
||||
languages.append(lmodvalues)
|
||||
lfull = AttrDict()
|
||||
for lmod in languages:
|
||||
_add_to_attr_dict(lfull, lmod)
|
||||
self.language_merged = lfull
|
||||
|
||||
# Pass some keys/values in for low level code to use;
|
||||
# start with everything in their 'internal' section.
|
||||
internal_vals = [
|
||||
v for v in list(lfull['internal'].items())
|
||||
if isinstance(v[1], str)
|
||||
]
|
||||
|
||||
# Cherry-pick various other values to include.
|
||||
# (should probably get rid of the 'internal' section
|
||||
# and do everything this way)
|
||||
for value in [
|
||||
'replayNameDefaultText', 'replayWriteErrorText',
|
||||
'replayVersionErrorText', 'replayReadErrorText'
|
||||
]:
|
||||
internal_vals.append((value, lfull[value]))
|
||||
internal_vals.append(
|
||||
('axisText', lfull['configGamepadWindow']['axisText']))
|
||||
internal_vals.append(('buttonText', lfull['buttonText']))
|
||||
lmerged = self.language_merged
|
||||
assert lmerged is not None
|
||||
random_names = [
|
||||
n.strip() for n in lmerged['randomPlayerNamesText'].split(',')
|
||||
]
|
||||
random_names = [n for n in random_names if n != '']
|
||||
_ba.set_internal_language_keys(internal_vals, random_names)
|
||||
if switched and print_change:
|
||||
_ba.screenmessage(Lstr(resource='languageSetText',
|
||||
subs=[('${LANGUAGE}',
|
||||
Lstr(translate=('languages',
|
||||
language)))]),
|
||||
color=(0, 1, 0))
|
||||
|
||||
def get_resource(self,
|
||||
resource: str,
|
||||
fallback_resource: str = None,
|
||||
fallback_value: Any = None) -> Any:
|
||||
"""Return a translation resource by name.
|
||||
|
||||
DEPRECATED; use ba.Lstr functionality for these purposes.
|
||||
"""
|
||||
try:
|
||||
# If we have no language set, go ahead and set it.
|
||||
if self.language_merged is None:
|
||||
language = self.language
|
||||
try:
|
||||
self.setlanguage(language,
|
||||
print_change=False,
|
||||
store_to_config=False)
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('exception setting language to',
|
||||
language)
|
||||
|
||||
# Try english as a fallback.
|
||||
if language != 'English':
|
||||
print('Resorting to fallback language (English)')
|
||||
try:
|
||||
self.setlanguage('English',
|
||||
print_change=False,
|
||||
store_to_config=False)
|
||||
except Exception:
|
||||
_error.print_exception(
|
||||
'error setting language to english fallback')
|
||||
|
||||
# If they provided a fallback_resource value, try the
|
||||
# target-language-only dict first and then fall back to trying the
|
||||
# fallback_resource value in the merged dict.
|
||||
if fallback_resource is not None:
|
||||
try:
|
||||
values = self.language_target
|
||||
splits = resource.split('.')
|
||||
dicts = splits[:-1]
|
||||
key = splits[-1]
|
||||
for dct in dicts:
|
||||
assert values is not None
|
||||
values = values[dct]
|
||||
assert values is not None
|
||||
val = values[key]
|
||||
return val
|
||||
except Exception:
|
||||
# FIXME: Shouldn't we try the fallback resource in the
|
||||
# merged dict AFTER we try the main resource in the
|
||||
# merged dict?
|
||||
try:
|
||||
values = self.language_merged
|
||||
splits = fallback_resource.split('.')
|
||||
dicts = splits[:-1]
|
||||
key = splits[-1]
|
||||
for dct in dicts:
|
||||
assert values is not None
|
||||
values = values[dct]
|
||||
assert values is not None
|
||||
val = values[key]
|
||||
return val
|
||||
|
||||
except Exception:
|
||||
# If we got nothing for fallback_resource, default
|
||||
# to the normal code which checks or primary
|
||||
# value in the merge dict; there's a chance we can
|
||||
# get an english value for it (which we weren't
|
||||
# looking for the first time through).
|
||||
pass
|
||||
|
||||
values = self.language_merged
|
||||
splits = resource.split('.')
|
||||
dicts = splits[:-1]
|
||||
key = splits[-1]
|
||||
for dct in dicts:
|
||||
assert values is not None
|
||||
values = values[dct]
|
||||
assert values is not None
|
||||
val = values[key]
|
||||
return val
|
||||
|
||||
except Exception:
|
||||
# Ok, looks like we couldn't find our main or fallback resource
|
||||
# anywhere. Now if we've been given a fallback value, return it;
|
||||
# otherwise fail.
|
||||
from ba import _error
|
||||
if fallback_value is not None:
|
||||
return fallback_value
|
||||
raise _error.NotFoundError(
|
||||
f"Resource not found: '{resource}'") from None
|
||||
|
||||
def translate(self,
|
||||
category: str,
|
||||
strval: str,
|
||||
raise_exceptions: bool = False,
|
||||
print_errors: bool = False) -> str:
|
||||
"""Translate a value (or return the value if no translation available)
|
||||
|
||||
DEPRECATED; use ba.Lstr functionality for these purposes.
|
||||
"""
|
||||
try:
|
||||
translated = self.get_resource('translations')[category][strval]
|
||||
except Exception as exc:
|
||||
if raise_exceptions:
|
||||
raise
|
||||
if print_errors:
|
||||
print(('Translate error: category=\'' + category +
|
||||
'\' name=\'' + strval + '\' exc=' + str(exc) + ''))
|
||||
translated = None
|
||||
translated_out: str
|
||||
if translated is None:
|
||||
translated_out = strval
|
||||
else:
|
||||
translated_out = translated
|
||||
assert isinstance(translated_out, str)
|
||||
return translated_out
|
||||
|
||||
def is_custom_unicode_char(self, char: str) -> bool:
|
||||
"""Return whether a char is in the custom unicode range we use."""
|
||||
assert isinstance(char, str)
|
||||
if len(char) != 1:
|
||||
raise ValueError('Invalid Input; must be length 1')
|
||||
return 0xE000 <= ord(char) <= 0xF8FF
|
||||
|
||||
|
||||
class Lstr:
|
||||
"""Used to define strings in a language-independent way.
|
||||
|
||||
category: General Utility Classes
|
||||
|
||||
These should be used whenever possible in place of hard-coded strings
|
||||
so that in-game or UI elements show up correctly on all clients in their
|
||||
currently-active language.
|
||||
|
||||
To see available resource keys, look at any of the bs_language_*.py files
|
||||
in the game or the translations pages at bombsquadgame.com/translate.
|
||||
|
||||
# EXAMPLE 1: specify a string from a resource path
|
||||
mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText')
|
||||
|
||||
# EXAMPLE 2: specify a translated string via a category and english value;
|
||||
# if a translated value is available, it will be used; otherwise the
|
||||
# english value will be. To see available translation categories, look
|
||||
# under the 'translations' resource section.
|
||||
mynode.text = ba.Lstr(translate=('gameDescriptions', 'Defeat all enemies'))
|
||||
|
||||
# EXAMPLE 3: specify a raw value and some substitutions. Substitutions can
|
||||
# be used with resource and translate modes as well.
|
||||
mynode.text = ba.Lstr(value='${A} / ${B}',
|
||||
subs=[('${A}', str(score)), ('${B}', str(total))])
|
||||
|
||||
# EXAMPLE 4: Lstrs can be nested. This example would display the resource
|
||||
# at res_a but replace ${NAME} with the value of the resource at res_b
|
||||
mytextnode.text = ba.Lstr(resource='res_a',
|
||||
subs=[('${NAME}', ba.Lstr(resource='res_b'))])
|
||||
"""
|
||||
|
||||
# pylint: disable=dangerous-default-value
|
||||
# noinspection PyDefaultArgument
|
||||
@overload
|
||||
def __init__(self,
|
||||
*,
|
||||
resource: str,
|
||||
fallback_resource: str = '',
|
||||
fallback_value: str = '',
|
||||
subs: Sequence[Tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr from a string resource."""
|
||||
...
|
||||
|
||||
# noinspection PyShadowingNames,PyDefaultArgument
|
||||
@overload
|
||||
def __init__(self,
|
||||
*,
|
||||
translate: Tuple[str, str],
|
||||
subs: Sequence[Tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr by translating a string in a category."""
|
||||
...
|
||||
|
||||
# noinspection PyDefaultArgument
|
||||
@overload
|
||||
def __init__(self,
|
||||
*,
|
||||
value: str,
|
||||
subs: Sequence[Tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr from a raw string value."""
|
||||
...
|
||||
|
||||
# pylint: enable=redefined-outer-name, dangerous-default-value
|
||||
|
||||
def __init__(self, *args: Any, **keywds: Any) -> None:
|
||||
"""Instantiate a Lstr.
|
||||
|
||||
Pass a value for either 'resource', 'translate',
|
||||
or 'value'. (see Lstr help for examples).
|
||||
'subs' can be a sequence of 2-member sequences consisting of values
|
||||
and replacements.
|
||||
'fallback_resource' can be a resource key that will be used if the
|
||||
main one is not present for
|
||||
the current language in place of falling back to the english value
|
||||
('resource' mode only).
|
||||
'fallback_value' can be a literal string that will be used if neither
|
||||
the resource nor the fallback resource is found ('resource' mode only).
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
if args:
|
||||
raise TypeError('Lstr accepts only keyword arguments')
|
||||
|
||||
# Basically just store the exact args they passed.
|
||||
# However if they passed any Lstr values for subs,
|
||||
# replace them with that Lstr's dict.
|
||||
self.args = keywds
|
||||
our_type = type(self)
|
||||
|
||||
if isinstance(self.args.get('value'), our_type):
|
||||
raise TypeError("'value' must be a regular string; not an Lstr")
|
||||
|
||||
if 'subs' in self.args:
|
||||
subs_new = []
|
||||
for key, value in keywds['subs']:
|
||||
if isinstance(value, our_type):
|
||||
subs_new.append((key, value.args))
|
||||
else:
|
||||
subs_new.append((key, value))
|
||||
self.args['subs'] = subs_new
|
||||
|
||||
# As of protocol 31 we support compact key names
|
||||
# ('t' instead of 'translate', etc). Convert as needed.
|
||||
if 'translate' in keywds:
|
||||
keywds['t'] = keywds['translate']
|
||||
del keywds['translate']
|
||||
if 'resource' in keywds:
|
||||
keywds['r'] = keywds['resource']
|
||||
del keywds['resource']
|
||||
if 'value' in keywds:
|
||||
keywds['v'] = keywds['value']
|
||||
del keywds['value']
|
||||
if 'fallback' in keywds:
|
||||
from ba import _error
|
||||
_error.print_error(
|
||||
'deprecated "fallback" arg passed to Lstr(); use '
|
||||
'either "fallback_resource" or "fallback_value"',
|
||||
once=True)
|
||||
keywds['f'] = keywds['fallback']
|
||||
del keywds['fallback']
|
||||
if 'fallback_resource' in keywds:
|
||||
keywds['f'] = keywds['fallback_resource']
|
||||
del keywds['fallback_resource']
|
||||
if 'subs' in keywds:
|
||||
keywds['s'] = keywds['subs']
|
||||
del keywds['subs']
|
||||
if 'fallback_value' in keywds:
|
||||
keywds['fv'] = keywds['fallback_value']
|
||||
del keywds['fallback_value']
|
||||
|
||||
def evaluate(self) -> str:
|
||||
"""Evaluate the Lstr and returns a flat string in the current language.
|
||||
|
||||
You should avoid doing this as much as possible and instead pass
|
||||
and store Lstr values.
|
||||
"""
|
||||
return _ba.evaluate_lstr(self._get_json())
|
||||
|
||||
def is_flat_value(self) -> bool:
|
||||
"""Return whether the Lstr is a 'flat' value.
|
||||
|
||||
This is defined as a simple string value incorporating no translations,
|
||||
resources, or substitutions. In this case it may be reasonable to
|
||||
replace it with a raw string value, perform string manipulation on it,
|
||||
etc.
|
||||
"""
|
||||
return bool('v' in self.args and not self.args.get('s', []))
|
||||
|
||||
def _get_json(self) -> str:
|
||||
try:
|
||||
return json.dumps(self.args, separators=(',', ':'))
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('_get_json failed for', self.args)
|
||||
return 'JSON_ERR'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return '<ba.Lstr: ' + self._get_json() + '>'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return '<ba.Lstr: ' + self._get_json() + '>'
|
||||
|
||||
@staticmethod
|
||||
def from_json(json_string: str) -> ba.Lstr:
|
||||
"""Given a json string, returns a ba.Lstr. Does no data validation."""
|
||||
lstr = Lstr(value='')
|
||||
lstr.args = json.loads(json_string)
|
||||
return lstr
|
||||
|
||||
|
||||
def _add_to_attr_dict(dst: AttrDict, src: Dict) -> None:
|
||||
for key, value in list(src.items()):
|
||||
if isinstance(value, dict):
|
||||
try:
|
||||
dst_dict = dst[key]
|
||||
except Exception:
|
||||
dst_dict = dst[key] = AttrDict()
|
||||
if not isinstance(dst_dict, AttrDict):
|
||||
raise RuntimeError("language key '" + key +
|
||||
"' is defined both as a dict and value")
|
||||
_add_to_attr_dict(dst_dict, value)
|
||||
else:
|
||||
if not isinstance(value, (float, int, bool, str, str, type(None))):
|
||||
raise TypeError("invalid value type for res '" + key + "': " +
|
||||
str(type(value)))
|
||||
dst[key] = value
|
||||
|
||||
|
||||
class AttrDict(dict):
|
||||
"""A dict that can be accessed with dot notation.
|
||||
|
||||
(so foo.bar is equivalent to foo['bar'])
|
||||
"""
|
||||
|
||||
def __getattr__(self, attr: str) -> Any:
|
||||
val = self[attr]
|
||||
assert not isinstance(val, bytes)
|
||||
return val
|
||||
|
||||
def __setattr__(self, attr: str, value: Any) -> None:
|
||||
raise Exception()
|
||||
@ -62,8 +62,8 @@ class Level:
|
||||
@property
|
||||
def displayname(self) -> ba.Lstr:
|
||||
"""The localized name for this Level."""
|
||||
from ba import _lang
|
||||
return _lang.Lstr(
|
||||
from ba import _language
|
||||
return _language.Lstr(
|
||||
translate=('coopLevelNames', self._displayname
|
||||
if self._displayname is not None else self._name),
|
||||
subs=[('${GAME}',
|
||||
|
||||
@ -11,7 +11,7 @@ from typing import TYPE_CHECKING
|
||||
import _ba
|
||||
from ba._error import print_exception, print_error, NotFoundError
|
||||
from ba._gameutils import animate, animate_array
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import SpecialChar, InputType
|
||||
from ba._profile import get_player_profile_colors
|
||||
|
||||
|
||||
@ -48,8 +48,8 @@ def get_map_display_string(name: str) -> ba.Lstr:
|
||||
|
||||
Category: Asset Functions
|
||||
"""
|
||||
from ba import _lang
|
||||
return _lang.Lstr(translate=('mapsNames', name))
|
||||
from ba import _language
|
||||
return _language.Lstr(translate=('mapsNames', name))
|
||||
|
||||
|
||||
def getmaps(playtype: str) -> List[str]:
|
||||
|
||||
@ -48,7 +48,7 @@ def start_scan() -> None:
|
||||
def handle_scan_results(results: ScanResults) -> None:
|
||||
"""Called in the game thread with results of a completed scan."""
|
||||
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._plugin import PotentialPlugin
|
||||
|
||||
# Warnings generally only get printed locally for users' benefit
|
||||
|
||||
@ -238,7 +238,7 @@ class MultiTeamSession(Session):
|
||||
from ba._math import normalized_color
|
||||
from ba._general import Call
|
||||
from ba._gameutils import cameraflash
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._freeforallsession import FreeForAllSession
|
||||
from ba._messages import CelebrateMessage
|
||||
_ba.timer(delay, Call(_ba.playsound, _ba.getsound('boxingBell')))
|
||||
|
||||
@ -120,6 +120,8 @@ class MusicSubsystem:
|
||||
"""Subsystem for music playback in the app.
|
||||
|
||||
Category: App Classes
|
||||
|
||||
To use this class, access the single instance of it at 'ba.app.music'.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
||||
@ -291,7 +291,7 @@ class EmptyPlayer(Player['ba.EmptyTeam']):
|
||||
defining a ba.Activity that does not need custom types of its own.
|
||||
|
||||
Note that EmptyPlayer defines its team type as EmptyTeam and vice versa,
|
||||
so if you want to define your own class for one of them you must do so
|
||||
so if you want to define your own class for one of them you should do so
|
||||
for both.
|
||||
"""
|
||||
|
||||
|
||||
@ -166,7 +166,7 @@ class ServerController:
|
||||
return False
|
||||
|
||||
def _execute_shutdown(self) -> None:
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
if self._executing_shutdown:
|
||||
return
|
||||
self._executing_shutdown = True
|
||||
|
||||
@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
from ba._error import print_error, print_exception, NodeNotFoundError
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._player import Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@ -131,7 +131,7 @@ class PlayerRecord:
|
||||
"""Submit a kill for this player entry."""
|
||||
# FIXME Clean this up.
|
||||
# pylint: disable=too-many-statements
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._general import Call
|
||||
self._multi_kill_count += 1
|
||||
stats = self._stats()
|
||||
@ -341,7 +341,7 @@ class Stats:
|
||||
from bastd.actor.popuptext import PopupText
|
||||
from ba import _math
|
||||
from ba._gameactivity import GameActivity
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
del victim_player # Currently unused.
|
||||
name = player.getname()
|
||||
s_player = self._player_records[name]
|
||||
@ -428,7 +428,7 @@ class Stats:
|
||||
killed: bool = False,
|
||||
killer: ba.Player = None) -> None:
|
||||
"""Should be called when a player is killed."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
name = player.getname()
|
||||
prec = self._player_records[name]
|
||||
prec.streak = 0
|
||||
|
||||
@ -21,15 +21,16 @@ def get_store_item(item: str) -> Dict[str, Any]:
|
||||
def get_store_item_name_translated(item_name: str) -> ba.Lstr:
|
||||
"""Return a ba.Lstr for a store item name."""
|
||||
# pylint: disable=cyclic-import
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
from ba import _map
|
||||
item_info = get_store_item(item_name)
|
||||
if item_name.startswith('characters.'):
|
||||
return _lang.Lstr(translate=('characterNames', item_info['character']))
|
||||
return _language.Lstr(translate=('characterNames',
|
||||
item_info['character']))
|
||||
if item_name in ['upgrades.pro', 'pro']:
|
||||
return _lang.Lstr(resource='store.bombSquadProNameText',
|
||||
subs=[('${APP_NAME}',
|
||||
_lang.Lstr(resource='titleText'))])
|
||||
return _language.Lstr(resource='store.bombSquadProNameText',
|
||||
subs=[('${APP_NAME}',
|
||||
_language.Lstr(resource='titleText'))])
|
||||
if item_name.startswith('maps.'):
|
||||
map_type: Type[ba.Map] = item_info['map_type']
|
||||
return _map.get_map_display_string(map_type.name)
|
||||
@ -37,7 +38,7 @@ def get_store_item_name_translated(item_name: str) -> ba.Lstr:
|
||||
gametype: Type[ba.GameActivity] = item_info['gametype']
|
||||
return gametype.get_display_string()
|
||||
if item_name.startswith('icons.'):
|
||||
return _lang.Lstr(resource='editProfileWindow.iconText')
|
||||
return _language.Lstr(resource='editProfileWindow.iconText')
|
||||
raise ValueError('unrecognized item: ' + item_name)
|
||||
|
||||
|
||||
|
||||
@ -207,6 +207,6 @@ class EmptyTeam(Team['ba.EmptyPlayer']):
|
||||
defining a ba.Activity that does not need custom types of its own.
|
||||
|
||||
Note that EmptyPlayer defines its team type as EmptyTeam and vice versa,
|
||||
so if you want to define your own class for one of them you must do so
|
||||
so if you want to define your own class for one of them you should do so
|
||||
for both.
|
||||
"""
|
||||
|
||||
@ -15,8 +15,13 @@ if TYPE_CHECKING:
|
||||
import ba
|
||||
|
||||
|
||||
class UI:
|
||||
"""UI subsystem for the app."""
|
||||
class UISubsystem:
|
||||
"""Consolidated UI functionality for the app.
|
||||
|
||||
Category: App Classes
|
||||
|
||||
To use this class, access the single instance of it at 'ba.app.ui'.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
env = _ba.env()
|
||||
|
||||
@ -6,8 +6,3 @@ Classes or functions can be relocated here when they are deprecated.
|
||||
Any code using them should migrate to alternative methods, as
|
||||
deprecated items will eventually be fully removed.
|
||||
"""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
|
||||
# The Lstr class should be used for all string resources.
|
||||
from ba._lang import get_resource, translate
|
||||
|
||||
@ -68,7 +68,7 @@ class _MacMusicAppThread(threading.Thread):
|
||||
def run(self) -> None:
|
||||
"""Run the Music.app thread."""
|
||||
from ba._general import Call
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeType
|
||||
_ba.set_thread_name('BA_MacMusicAppThread')
|
||||
_ba.mac_music_app_init()
|
||||
@ -212,7 +212,6 @@ class _MacMusicAppThread(threading.Thread):
|
||||
|
||||
def _play_current_playlist(self) -> None:
|
||||
try:
|
||||
from ba import _lang
|
||||
from ba._general import Call
|
||||
assert self._current_playlist is not None
|
||||
if _ba.mac_music_app_play_playlist(self._current_playlist):
|
||||
@ -220,8 +219,8 @@ class _MacMusicAppThread(threading.Thread):
|
||||
else:
|
||||
_ba.pushcall(Call(
|
||||
_ba.screenmessage,
|
||||
_lang.get_resource('playlistNotFoundText') + ': \'' +
|
||||
self._current_playlist + '\'', (1, 0, 0)),
|
||||
_ba.app.lang.get_resource('playlistNotFoundText') +
|
||||
': \'' + self._current_playlist + '\'', (1, 0, 0)),
|
||||
from_other_thread=True)
|
||||
except Exception:
|
||||
from ba import _error
|
||||
|
||||
@ -17,7 +17,7 @@ def get_human_readable_user_scripts_path() -> str:
|
||||
|
||||
This is NOT a valid filesystem path; may be something like "(SD Card)".
|
||||
"""
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
app = _ba.app
|
||||
path: Optional[str] = app.python_directory_user
|
||||
if path is None:
|
||||
@ -32,14 +32,14 @@ def get_human_readable_user_scripts_path() -> str:
|
||||
if (ext_storage_path is not None
|
||||
and app.python_directory_user.startswith(ext_storage_path)):
|
||||
path = ('<' +
|
||||
_lang.Lstr(resource='externalStorageText').evaluate() +
|
||||
_language.Lstr(resource='externalStorageText').evaluate() +
|
||||
'>' + app.python_directory_user[len(ext_storage_path):])
|
||||
return path
|
||||
|
||||
|
||||
def _request_storage_permission() -> bool:
|
||||
"""If needed, requests storage permission from the user (& return true)."""
|
||||
from ba._lang import Lstr
|
||||
from ba._language import Lstr
|
||||
from ba._enums import Permission
|
||||
if not _ba.have_permission(Permission.STORAGE):
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
|
||||
@ -62,9 +62,9 @@ class OSMusicPlayer(MusicPlayer):
|
||||
def _on_play_folder_cb(self,
|
||||
result: Union[str, List[str]],
|
||||
error: Optional[str] = None) -> None:
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
if error is not None:
|
||||
rstr = (_lang.Lstr(
|
||||
rstr = (_language.Lstr(
|
||||
resource='internal.errorPlayingMusicText').evaluate())
|
||||
if isinstance(result, str):
|
||||
err_str = (rstr.replace('${MUSIC}', os.path.basename(result)) +
|
||||
@ -103,7 +103,7 @@ class _PickFolderSongThread(threading.Thread):
|
||||
self._path = path
|
||||
|
||||
def run(self) -> None:
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
from ba._general import Call
|
||||
do_print_error = True
|
||||
try:
|
||||
@ -119,8 +119,8 @@ class _PickFolderSongThread(threading.Thread):
|
||||
if not all_files:
|
||||
do_print_error = False
|
||||
raise RuntimeError(
|
||||
_lang.Lstr(resource='internal.noMusicFilesInFolderText').
|
||||
evaluate())
|
||||
_language.Lstr(resource='internal.noMusicFilesInFolderText'
|
||||
).evaluate())
|
||||
_ba.pushcall(Call(self._callback, all_files, None),
|
||||
from_other_thread=True)
|
||||
except Exception as exc:
|
||||
|
||||
@ -23,7 +23,6 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
assert isinstance(self._winner, ba.SessionTeam)
|
||||
|
||||
def on_begin(self) -> None:
|
||||
from ba.deprecated import get_resource
|
||||
ba.set_analytics_screen('Teams Score Screen')
|
||||
super().on_begin()
|
||||
|
||||
@ -37,7 +36,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
# 'First to 4'.
|
||||
session = self.session
|
||||
assert isinstance(session, ba.MultiTeamSession)
|
||||
if get_resource('bestOfUseFirstToInstead'):
|
||||
if ba.app.lang.get_resource('bestOfUseFirstToInstead'):
|
||||
best_txt = ba.Lstr(resource='firstToSeriesText',
|
||||
subs=[('${COUNT}',
|
||||
str(session.get_series_length() / 2 + 1))
|
||||
|
||||
@ -33,7 +33,6 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
# pylint: disable=too-many-statements
|
||||
from bastd.actor.text import Text
|
||||
from bastd.actor.image import Image
|
||||
from ba.deprecated import get_resource
|
||||
ba.set_analytics_screen('FreeForAll Series Victory Screen' if self.
|
||||
_is_ffa else 'Teams Series Victory Screen')
|
||||
if ba.app.ui.uiscale is ba.UIScale.LARGE:
|
||||
@ -72,7 +71,8 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
tval = 6.4
|
||||
t_incr = 0.12
|
||||
|
||||
always_use_first_to = get_resource('bestOfUseFirstToInstead')
|
||||
always_use_first_to = ba.app.lang.get_resource(
|
||||
'bestOfUseFirstToInstead')
|
||||
|
||||
session = self.session
|
||||
if self._is_ffa:
|
||||
|
||||
@ -492,7 +492,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
||||
ba.getmodel('logoTransparent'))
|
||||
|
||||
# If language has changed, recreate our logo text/graphics.
|
||||
lang = app.language
|
||||
lang = app.lang.language
|
||||
if lang != self._language:
|
||||
self._language = lang
|
||||
y = 20
|
||||
@ -511,7 +511,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
||||
# We draw higher in kiosk mode (make sure to test this
|
||||
# when making adjustments) for now we're hard-coded for
|
||||
# a few languages.. should maybe look into generalizing this?..
|
||||
if app.language == 'Chinese':
|
||||
if app.lang.language == 'Chinese':
|
||||
base_x = -270.0
|
||||
x = base_x - 20.0
|
||||
spacing = 85.0 * base_scale
|
||||
|
||||
@ -1575,7 +1575,7 @@ class GatherWindow(ba.Window):
|
||||
{
|
||||
'type': 'PUBLIC_PARTY_QUERY',
|
||||
'proto': app.protocol_version,
|
||||
'lang': app.language
|
||||
'lang': app.lang.language
|
||||
},
|
||||
callback=ba.WeakCall(self._on_public_party_query_result))
|
||||
_ba.run_transactions()
|
||||
|
||||
@ -22,7 +22,6 @@ class HelpWindow(ba.Window):
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-locals
|
||||
from ba.internal import get_remote_app_name
|
||||
from ba.deprecated import get_resource
|
||||
ba.set_analytics_screen('Help Window')
|
||||
|
||||
# If they provided an origin-widget, scale up from that.
|
||||
@ -38,6 +37,8 @@ class HelpWindow(ba.Window):
|
||||
|
||||
self._r = 'helpWindow'
|
||||
|
||||
getres = ba.app.lang.get_resource
|
||||
|
||||
self._main_menu = main_menu
|
||||
uiscale = ba.app.ui.uiscale
|
||||
width = 950 if uiscale is ba.UIScale.SMALL else 750
|
||||
@ -111,8 +112,8 @@ class HelpWindow(ba.Window):
|
||||
label=ba.charstr(ba.SpecialChar.BACK))
|
||||
|
||||
self._sub_width = 660
|
||||
self._sub_height = 1590 + get_resource(
|
||||
self._r + '.someDaysExtraSpace') + get_resource(
|
||||
self._sub_height = 1590 + ba.app.lang.get_resource(
|
||||
self._r + '.someDaysExtraSpace') + ba.app.lang.get_resource(
|
||||
self._r + '.orPunchingSomethingExtraSpace')
|
||||
|
||||
self._subcontainer = ba.containerwidget(parent=self._scrollwidget,
|
||||
@ -212,8 +213,7 @@ class HelpWindow(ba.Window):
|
||||
color=paragraph,
|
||||
v_align='center',
|
||||
flatness=1.0)
|
||||
v -= (spacing * 25.0 +
|
||||
get_resource(self._r + '.someDaysExtraSpace'))
|
||||
v -= (spacing * 25.0 + getres(self._r + '.someDaysExtraSpace'))
|
||||
txt_scale = 0.66
|
||||
txt = ba.Lstr(resource=self._r +
|
||||
'.orPunchingSomethingText').evaluate()
|
||||
@ -228,7 +228,7 @@ class HelpWindow(ba.Window):
|
||||
v_align='center',
|
||||
flatness=1.0)
|
||||
v -= (spacing * 27.0 +
|
||||
get_resource(self._r + '.orPunchingSomethingExtraSpace'))
|
||||
getres(self._r + '.orPunchingSomethingExtraSpace'))
|
||||
txt_scale = 1.0
|
||||
txt = ba.Lstr(resource=self._r + '.canHelpText',
|
||||
subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))
|
||||
@ -387,7 +387,7 @@ class HelpWindow(ba.Window):
|
||||
texture=ba.gettexture('buttonPunch'),
|
||||
color=(1, 0.7, 0.3))
|
||||
|
||||
txt_scale = get_resource(self._r + '.punchInfoTextScale')
|
||||
txt_scale = getres(self._r + '.punchInfoTextScale')
|
||||
txt = ba.Lstr(resource=self._r + '.punchInfoText').evaluate()
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(h - sep - 185 + 70, v + 120),
|
||||
@ -409,7 +409,7 @@ class HelpWindow(ba.Window):
|
||||
color=(1, 0.3, 0.3))
|
||||
|
||||
txt = ba.Lstr(resource=self._r + '.bombInfoText').evaluate()
|
||||
txt_scale = get_resource(self._r + '.bombInfoTextScale')
|
||||
txt_scale = getres(self._r + '.bombInfoTextScale')
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(h + sep + 50 + 60, v - 35),
|
||||
size=(0, 0),
|
||||
@ -431,7 +431,7 @@ class HelpWindow(ba.Window):
|
||||
color=(0.5, 0.5, 1))
|
||||
|
||||
txtl = ba.Lstr(resource=self._r + '.pickUpInfoText')
|
||||
txt_scale = get_resource(self._r + '.pickUpInfoTextScale')
|
||||
txt_scale = getres(self._r + '.pickUpInfoTextScale')
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(h + 60 + 120, v + sep + 50),
|
||||
size=(0, 0),
|
||||
@ -452,7 +452,7 @@ class HelpWindow(ba.Window):
|
||||
color=(0.4, 1, 0.4))
|
||||
|
||||
txt = ba.Lstr(resource=self._r + '.jumpInfoText').evaluate()
|
||||
txt_scale = get_resource(self._r + '.jumpInfoTextScale')
|
||||
txt_scale = getres(self._r + '.jumpInfoTextScale')
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(h - 250 + 75, v - sep - 15 + 30),
|
||||
size=(0, 0),
|
||||
@ -464,7 +464,7 @@ class HelpWindow(ba.Window):
|
||||
v_align='top')
|
||||
|
||||
txt = ba.Lstr(resource=self._r + '.runInfoText').evaluate()
|
||||
txt_scale = get_resource(self._r + '.runInfoTextScale')
|
||||
txt_scale = getres(self._r + '.runInfoTextScale')
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(h, v - sep - 100),
|
||||
size=(0, 0),
|
||||
@ -503,7 +503,7 @@ class HelpWindow(ba.Window):
|
||||
texture=logo_tex)
|
||||
|
||||
v -= spacing * 50.0
|
||||
txt_scale = get_resource(self._r + '.powerupsSubtitleTextScale')
|
||||
txt_scale = getres(self._r + '.powerupsSubtitleTextScale')
|
||||
txt = ba.Lstr(resource=self._r + '.powerupsSubtitleText').evaluate()
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(h, v),
|
||||
|
||||
@ -22,9 +22,7 @@ class LeagueRankWindow(ba.Window):
|
||||
transition: str = 'in_right',
|
||||
modal: bool = False,
|
||||
origin_widget: ba.Widget = None):
|
||||
# pylint: disable=too-many-statements
|
||||
from ba.internal import get_cached_league_rank_data
|
||||
from ba.deprecated import get_resource
|
||||
ba.set_analytics_screen('League Rank Window')
|
||||
|
||||
self._league_rank_data: Optional[Dict[str, Any]] = None
|
||||
@ -46,7 +44,7 @@ class LeagueRankWindow(ba.Window):
|
||||
self._height = (657 if uiscale is ba.UIScale.SMALL else
|
||||
710 if uiscale is ba.UIScale.MEDIUM else 800)
|
||||
self._r = 'coopSelectWindow'
|
||||
self._rdict = get_resource(self._r)
|
||||
self._rdict = ba.app.lang.get_resource(self._r)
|
||||
top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
|
||||
|
||||
self._league_url_arg = ''
|
||||
|
||||
@ -164,10 +164,10 @@ class AdvancedSettingsWindow(ba.Window):
|
||||
|
||||
def _update_lang_status(self) -> None:
|
||||
if self._complete_langs_list is not None:
|
||||
up_to_date = (ba.app.language in self._complete_langs_list)
|
||||
up_to_date = (ba.app.lang.language in self._complete_langs_list)
|
||||
ba.textwidget(
|
||||
edit=self._lang_status_text,
|
||||
text='' if ba.app.language == 'Test' else ba.Lstr(
|
||||
text='' if ba.app.lang.language == 'Test' else ba.Lstr(
|
||||
resource=self._r + '.translationNoUpdateNeededText')
|
||||
if up_to_date else ba.Lstr(resource=self._r +
|
||||
'.translationUpdateNeededText'),
|
||||
@ -189,6 +189,8 @@ class AdvancedSettingsWindow(ba.Window):
|
||||
from bastd.ui.config import ConfigCheckBox
|
||||
from ba.modutils import show_user_scripts
|
||||
|
||||
available_languages = ba.app.lang.available_languages
|
||||
|
||||
# Don't rebuild if the menu is open or if our language and
|
||||
# language-list hasn't changed.
|
||||
# NOTE - although we now support widgets updating their own
|
||||
@ -196,12 +198,11 @@ class AdvancedSettingsWindow(ba.Window):
|
||||
# menu based on the language so still need this. ...however we could
|
||||
# make this more limited to it only rebuilds that one menu instead
|
||||
# of everything.
|
||||
if self._menu_open or (
|
||||
self._prev_lang == _ba.app.config.get('Lang', None)
|
||||
and self._prev_lang_list == ba.get_valid_languages()):
|
||||
if self._menu_open or (self._prev_lang == _ba.app.config.get(
|
||||
'Lang', None) and self._prev_lang_list == available_languages):
|
||||
return
|
||||
self._prev_lang = _ba.app.config.get('Lang', None)
|
||||
self._prev_lang_list = ba.get_valid_languages()
|
||||
self._prev_lang_list = available_languages
|
||||
|
||||
# Clear out our sub-container.
|
||||
children = self._subcontainer.get_children()
|
||||
@ -248,7 +249,7 @@ class AdvancedSettingsWindow(ba.Window):
|
||||
h_align='right',
|
||||
v_align='center')
|
||||
|
||||
languages = ba.get_valid_languages()
|
||||
languages = _ba.app.lang.available_languages
|
||||
cur_lang = _ba.app.config.get('Lang', None)
|
||||
if cur_lang is None:
|
||||
cur_lang = 'Auto'
|
||||
@ -289,9 +290,9 @@ class AdvancedSettingsWindow(ba.Window):
|
||||
button_size=(250, 60),
|
||||
choices_display=([
|
||||
ba.Lstr(value=(ba.Lstr(resource='autoText').evaluate() + ' (' +
|
||||
ba.Lstr(translate=(
|
||||
'languages',
|
||||
ba.app.default_language)).evaluate() + ')'))
|
||||
ba.Lstr(translate=('languages',
|
||||
ba.app.lang.default_language
|
||||
)).evaluate() + ')'))
|
||||
] + [ba.Lstr(value=langs_full[l]) for l in languages]),
|
||||
current_choice=cur_lang)
|
||||
|
||||
@ -689,7 +690,7 @@ class AdvancedSettingsWindow(ba.Window):
|
||||
self._menu_open = False
|
||||
|
||||
def _on_menu_choice(self, choice: str) -> None:
|
||||
ba.setlanguage(None if choice == 'Auto' else choice)
|
||||
ba.app.lang.setlanguage(None if choice == 'Auto' else choice)
|
||||
self._save_state()
|
||||
ba.timer(0.1, ba.WeakCall(self._rebuild), timetype=ba.TimeType.REAL)
|
||||
|
||||
|
||||
@ -157,7 +157,6 @@ class SoundtrackEditWindow(ba.Window):
|
||||
ba.widget(edit=cancel_button, down_widget=self._text_field)
|
||||
|
||||
def _refresh(self) -> None:
|
||||
from ba.deprecated import get_resource
|
||||
for widget in self._col.get_children():
|
||||
widget.delete()
|
||||
|
||||
@ -183,8 +182,9 @@ class SoundtrackEditWindow(ba.Window):
|
||||
'Scores',
|
||||
'Victory',
|
||||
]
|
||||
|
||||
# FIXME: We should probably convert this to use translations.
|
||||
type_names_translated = get_resource('soundtrackTypeNames')
|
||||
type_names_translated = ba.app.lang.get_resource('soundtrackTypeNames')
|
||||
prev_type_button: Optional[ba.Widget] = None
|
||||
prev_test_button: Optional[ba.Widget] = None
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ class TrophiesWindow(popup.PopupWindow):
|
||||
position: Tuple[float, float],
|
||||
data: Dict[str, Any],
|
||||
scale: float = None):
|
||||
from ba.deprecated import get_resource
|
||||
self._data = data
|
||||
uiscale = ba.app.ui.uiscale
|
||||
if scale is None:
|
||||
@ -76,7 +75,9 @@ class TrophiesWindow(popup.PopupWindow):
|
||||
trophy_types = [['0a'], ['0b'], ['1'], ['2'], ['3'], ['4']]
|
||||
sub_height = 40 + len(trophy_types) * incr
|
||||
|
||||
eq_text = get_resource('coopSelectWindow.powerRankingPointsEqualsText')
|
||||
eq_text = ba.Lstr(
|
||||
resource='coopSelectWindow.powerRankingPointsEqualsText').evaluate(
|
||||
)
|
||||
|
||||
self._subcontainer = ba.containerwidget(parent=self._scrollwidget,
|
||||
size=(sub_width, sub_height),
|
||||
@ -84,25 +85,27 @@ class TrophiesWindow(popup.PopupWindow):
|
||||
|
||||
total_pts = 0
|
||||
|
||||
multi_txt = get_resource('coopSelectWindow.powerRankingPointsMultText')
|
||||
multi_txt = ba.Lstr(
|
||||
resource='coopSelectWindow.powerRankingPointsMultText').evaluate()
|
||||
|
||||
total_pts += self._create_trophy_type_widgets(eq_text, incr, multi_txt,
|
||||
sub_height, sub_width,
|
||||
trophy_types)
|
||||
|
||||
ba.textwidget(parent=self._subcontainer,
|
||||
position=(sub_width * 1.0,
|
||||
sub_height - 20 - incr * len(trophy_types)),
|
||||
maxwidth=sub_width * 0.5,
|
||||
scale=0.7,
|
||||
color=(0.7, 0.8, 1.0),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
text=get_resource('coopSelectWindow.totalText') + ' ' +
|
||||
eq_text.replace('${NUMBER}', str(total_pts)),
|
||||
size=(0, 0),
|
||||
h_align='right',
|
||||
v_align='center')
|
||||
ba.textwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(sub_width * 1.0,
|
||||
sub_height - 20 - incr * len(trophy_types)),
|
||||
maxwidth=sub_width * 0.5,
|
||||
scale=0.7,
|
||||
color=(0.7, 0.8, 1.0),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
text=ba.Lstr(resource='coopSelectWindow.totalText').evaluate() +
|
||||
' ' + eq_text.replace('${NUMBER}', str(total_pts)),
|
||||
size=(0, 0),
|
||||
h_align='right',
|
||||
v_align='center')
|
||||
|
||||
def _create_trophy_type_widgets(self, eq_text: str, incr: int,
|
||||
multi_txt: str, sub_height: int,
|
||||
|
||||
@ -353,6 +353,7 @@
|
||||
<w>getpublicpartyenabled</w>
|
||||
<w>getpublicpartymaxsize</w>
|
||||
<w>getqrcodetexture</w>
|
||||
<w>getres</w>
|
||||
<w>getsession</w>
|
||||
<w>getsound</w>
|
||||
<w>gettexture</w>
|
||||
@ -415,6 +416,7 @@
|
||||
<w>internalformat</w>
|
||||
<w>interuptions</w>
|
||||
<w>invote</w>
|
||||
<w>iobj</w>
|
||||
<w>iserverget</w>
|
||||
<w>iserverput</w>
|
||||
<w>isinst</w>
|
||||
@ -552,6 +554,7 @@
|
||||
<w>nitpicky</w>
|
||||
<w>nlpos</w>
|
||||
<w>nmemb</w>
|
||||
<w>noassets</w>
|
||||
<w>nodetype</w>
|
||||
<w>nofilename</w>
|
||||
<w>noglobs</w>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
||||
<h4><em>last updated on 2020-10-15 for Ballistica version 1.5.26 build 20213</em></h4>
|
||||
<h4><em>last updated on 2020-10-15 for Ballistica version 1.5.27 build 20218</em></h4>
|
||||
<p>This page documents the Python classes and functions in the 'ba' module,
|
||||
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
|
||||
<hr>
|
||||
@ -20,6 +20,7 @@
|
||||
<li><a href="#class_ba_NodeActor">ba.NodeActor</a></li>
|
||||
</ul>
|
||||
<li><a href="#class_ba_Chooser">ba.Chooser</a></li>
|
||||
<li><a href="#class_ba_Collision">ba.Collision</a></li>
|
||||
<li><a href="#class_ba_GameResults">ba.GameResults</a></li>
|
||||
<li><a href="#class_ba_GameTip">ba.GameTip</a></li>
|
||||
<li><a href="#class_ba_InputDevice">ba.InputDevice</a></li>
|
||||
@ -62,6 +63,7 @@
|
||||
<li><a href="#function_ba_emitfx">ba.emitfx()</a></li>
|
||||
<li><a href="#function_ba_existing">ba.existing()</a></li>
|
||||
<li><a href="#function_ba_getactivity">ba.getactivity()</a></li>
|
||||
<li><a href="#function_ba_getcollision">ba.getcollision()</a></li>
|
||||
<li><a href="#function_ba_getnodes">ba.getnodes()</a></li>
|
||||
<li><a href="#function_ba_getsession">ba.getsession()</a></li>
|
||||
<li><a href="#function_ba_newnode">ba.newnode()</a></li>
|
||||
@ -84,7 +86,6 @@
|
||||
<ul>
|
||||
<li><a href="#function_ba_charstr">ba.charstr()</a></li>
|
||||
<li><a href="#function_ba_do_once">ba.do_once()</a></li>
|
||||
<li><a href="#function_ba_get_valid_languages">ba.get_valid_languages()</a></li>
|
||||
<li><a href="#function_ba_getclass">ba.getclass()</a></li>
|
||||
<li><a href="#function_ba_is_browser_likely_available">ba.is_browser_likely_available()</a></li>
|
||||
<li><a href="#function_ba_is_point_in_box">ba.is_point_in_box()</a></li>
|
||||
@ -100,7 +101,6 @@
|
||||
<li><a href="#function_ba_safecolor">ba.safecolor()</a></li>
|
||||
<li><a href="#function_ba_screenmessage">ba.screenmessage()</a></li>
|
||||
<li><a href="#function_ba_set_analytics_screen">ba.set_analytics_screen()</a></li>
|
||||
<li><a href="#function_ba_setlanguage">ba.setlanguage()</a></li>
|
||||
<li><a href="#function_ba_storagename">ba.storagename()</a></li>
|
||||
<li><a href="#function_ba_time">ba.time()</a></li>
|
||||
<li><a href="#function_ba_timer">ba.timer()</a></li>
|
||||
@ -153,10 +153,13 @@
|
||||
<li><a href="#class_ba_AppDelegate">ba.AppDelegate</a></li>
|
||||
<li><a href="#class_ba_Campaign">ba.Campaign</a></li>
|
||||
<li><a href="#class_ba_Keyboard">ba.Keyboard</a></li>
|
||||
<li><a href="#class_ba_LanguageSubsystem">ba.LanguageSubsystem</a></li>
|
||||
<li><a href="#class_ba_MusicPlayer">ba.MusicPlayer</a></li>
|
||||
<li><a href="#class_ba_MusicSubsystem">ba.MusicSubsystem</a></li>
|
||||
<li><a href="#class_ba_Plugin">ba.Plugin</a></li>
|
||||
<li><a href="#class_ba_PotentialPlugin">ba.PotentialPlugin</a></li>
|
||||
<li><a href="#class_ba_ServerController">ba.ServerController</a></li>
|
||||
<li><a href="#class_ba_UISubsystem">ba.UISubsystem</a></li>
|
||||
</ul>
|
||||
<h4><a name="class_category_User_Interface_Classes">User Interface Classes</a></h4>
|
||||
<ul>
|
||||
@ -216,14 +219,6 @@
|
||||
<li><a href="#class_ba_WidgetNotFoundError">ba.WidgetNotFoundError</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
<h4><a name="class_category_Misc_Classes">Misc Classes</a></h4>
|
||||
<ul>
|
||||
<li><a href="#class_ba_Collision">ba.Collision</a></li>
|
||||
</ul>
|
||||
<h4><a name="function_category_Misc_Functions">Misc Functions</a></h4>
|
||||
<ul>
|
||||
<li><a href="#function_ba_getcollision">ba.getcollision()</a></li>
|
||||
</ul>
|
||||
<h4><a name="class_category_Protocols">Protocols</a></h4>
|
||||
<ul>
|
||||
<li><a href="#class_ba_Existable">ba.Existable</a></li>
|
||||
@ -810,7 +805,7 @@ likely result in errors.</p>
|
||||
</p>
|
||||
|
||||
<h3>Attributes:</h3>
|
||||
<h5><a href="#attr_ba_App__api_version">api_version</a>, <a href="#attr_ba_App__build_number">build_number</a>, <a href="#attr_ba_App__config">config</a>, <a href="#attr_ba_App__config_file_path">config_file_path</a>, <a href="#attr_ba_App__debug_build">debug_build</a>, <a href="#attr_ba_App__language">language</a>, <a href="#attr_ba_App__locale">locale</a>, <a href="#attr_ba_App__on_tv">on_tv</a>, <a href="#attr_ba_App__platform">platform</a>, <a href="#attr_ba_App__python_directory_app">python_directory_app</a>, <a href="#attr_ba_App__python_directory_app_site">python_directory_app_site</a>, <a href="#attr_ba_App__python_directory_user">python_directory_user</a>, <a href="#attr_ba_App__subplatform">subplatform</a>, <a href="#attr_ba_App__test_build">test_build</a>, <a href="#attr_ba_App__ui_bounds">ui_bounds</a>, <a href="#attr_ba_App__user_agent_string">user_agent_string</a>, <a href="#attr_ba_App__version">version</a>, <a href="#attr_ba_App__vr_mode">vr_mode</a></h5>
|
||||
<h5><a href="#attr_ba_App__api_version">api_version</a>, <a href="#attr_ba_App__build_number">build_number</a>, <a href="#attr_ba_App__config">config</a>, <a href="#attr_ba_App__config_file_path">config_file_path</a>, <a href="#attr_ba_App__debug_build">debug_build</a>, <a href="#attr_ba_App__on_tv">on_tv</a>, <a href="#attr_ba_App__platform">platform</a>, <a href="#attr_ba_App__python_directory_app">python_directory_app</a>, <a href="#attr_ba_App__python_directory_app_site">python_directory_app_site</a>, <a href="#attr_ba_App__python_directory_user">python_directory_user</a>, <a href="#attr_ba_App__subplatform">subplatform</a>, <a href="#attr_ba_App__test_build">test_build</a>, <a href="#attr_ba_App__ui_bounds">ui_bounds</a>, <a href="#attr_ba_App__user_agent_string">user_agent_string</a>, <a href="#attr_ba_App__version">version</a>, <a href="#attr_ba_App__vr_mode">vr_mode</a></h5>
|
||||
<dl>
|
||||
<dt><h4><a name="attr_ba_App__api_version">api_version</a></h4></dt><dd>
|
||||
<p><span>int</span></p>
|
||||
@ -849,23 +844,6 @@ likely result in errors.</p>
|
||||
builds due to compiler optimizations being disabled and extra
|
||||
checks being run.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_App__language">language</a></h4></dt><dd>
|
||||
<p><span>str</span></p>
|
||||
<p>The name of the language the game is running in.</p>
|
||||
|
||||
<p> This can be selected explicitly by the user or may be set
|
||||
automatically based on <a href="#attr_ba_App__locale">ba.App.locale</a> or other factors.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_App__locale">locale</a></h4></dt><dd>
|
||||
<p><span>str</span></p>
|
||||
<p>Raw country/language code detected by the game (such as 'en_US').</p>
|
||||
|
||||
<p> Generally for language-specific code you should look at
|
||||
<a href="#attr_ba_App__language">ba.App.language</a>, which is the language the game is using
|
||||
(which may differ from locale if the user sets a language, etc.)</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_App__on_tv">on_tv</a></h4></dt><dd>
|
||||
<p><span>bool</span></p>
|
||||
@ -1441,6 +1419,9 @@ mycall()</pre>
|
||||
</p>
|
||||
<p>A class providing info about occurring collisions.</p>
|
||||
|
||||
<p>Category: <a href="#class_category_Gameplay_Classes">Gameplay Classes</a>
|
||||
</p>
|
||||
|
||||
<h3>Attributes:</h3>
|
||||
<h5><a href="#attr_ba_Collision__opposingbody">opposingbody</a>, <a href="#attr_ba_Collision__opposingnode">opposingnode</a>, <a href="#attr_ba_Collision__position">position</a>, <a href="#attr_ba_Collision__sourcenode">sourcenode</a></h5>
|
||||
<dl>
|
||||
@ -2119,7 +2100,7 @@ its time with lingering corpses, sound effects, etc.</p>
|
||||
defining a <a href="#class_ba_Activity">ba.Activity</a> that does not need custom types of its own.</p>
|
||||
|
||||
<p> Note that EmptyPlayer defines its team type as EmptyTeam and vice versa,
|
||||
so if you want to define your own class for one of them you must do so
|
||||
so if you want to define your own class for one of them you should do so
|
||||
for both.
|
||||
</p>
|
||||
|
||||
@ -2182,7 +2163,7 @@ its time with lingering corpses, sound effects, etc.</p>
|
||||
defining a <a href="#class_ba_Activity">ba.Activity</a> that does not need custom types of its own.</p>
|
||||
|
||||
<p> Note that EmptyPlayer defines its team type as EmptyTeam and vice versa,
|
||||
so if you want to define your own class for one of them you must do so
|
||||
so if you want to define your own class for one of them you should do so
|
||||
for both.
|
||||
</p>
|
||||
|
||||
@ -3077,6 +3058,85 @@ prefs, etc.</p>
|
||||
<p><span>Dict[str, Tuple[str, ...]]</span></p>
|
||||
<p>Extra chars like emojis.</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_LanguageSubsystem">ba.LanguageSubsystem</a></strong></h3>
|
||||
<p><em><top level class></em>
|
||||
</p>
|
||||
<p>Wraps up language related app functionality.</p>
|
||||
|
||||
<p>Category: <a href="#class_category_App_Classes">App Classes</a></p>
|
||||
|
||||
<p> To use this class, access the single instance of it at 'ba.app.lang'.
|
||||
</p>
|
||||
|
||||
<h3>Attributes:</h3>
|
||||
<h5><a href="#attr_ba_LanguageSubsystem__available_languages">available_languages</a>, <a href="#attr_ba_LanguageSubsystem__language">language</a>, <a href="#attr_ba_LanguageSubsystem__locale">locale</a></h5>
|
||||
<dl>
|
||||
<dt><h4><a name="attr_ba_LanguageSubsystem__available_languages">available_languages</a></h4></dt><dd>
|
||||
<p><span>List[str]</span></p>
|
||||
<p>A list of all available languages.</p>
|
||||
|
||||
<p> Note that languages that may be present in game assets but which
|
||||
are not displayable on the running version of the game are not
|
||||
included here.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_LanguageSubsystem__language">language</a></h4></dt><dd>
|
||||
<p><span>str</span></p>
|
||||
<p>The name of the language the game is running in.</p>
|
||||
|
||||
<p> This can be selected explicitly by the user or may be set
|
||||
automatically based on <a href="#class_ba_App">ba.App</a>.locale or other factors.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_LanguageSubsystem__locale">locale</a></h4></dt><dd>
|
||||
<p><span>str</span></p>
|
||||
<p>Raw country/language code detected by the game (such as 'en_US').</p>
|
||||
|
||||
<p> Generally for language-specific code you should look at
|
||||
<a href="#class_ba_App">ba.App</a>.language, which is the language the game is using
|
||||
(which may differ from locale if the user sets a language, etc.)</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Methods:</h3>
|
||||
<h5><a href="#method_ba_LanguageSubsystem____init__"><constructor></a>, <a href="#method_ba_LanguageSubsystem__get_resource">get_resource()</a>, <a href="#method_ba_LanguageSubsystem__is_custom_unicode_char">is_custom_unicode_char()</a>, <a href="#method_ba_LanguageSubsystem__setlanguage">setlanguage()</a>, <a href="#method_ba_LanguageSubsystem__translate">translate()</a></h5>
|
||||
<dl>
|
||||
<dt><h4><a name="method_ba_LanguageSubsystem____init__"><constructor></a></dt></h4><dd>
|
||||
<p><span>ba.LanguageSubsystem()</span></p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_LanguageSubsystem__get_resource">get_resource()</a></dt></h4><dd>
|
||||
<p><span>get_resource(self, resource: str, fallback_resource: str = None, fallback_value: Any = None) -> Any</span></p>
|
||||
|
||||
<p>Return a translation resource by name.</p>
|
||||
|
||||
<p>DEPRECATED; use <a href="#class_ba_Lstr">ba.Lstr</a> functionality for these purposes.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_LanguageSubsystem__is_custom_unicode_char">is_custom_unicode_char()</a></dt></h4><dd>
|
||||
<p><span>is_custom_unicode_char(self, char: str) -> bool</span></p>
|
||||
|
||||
<p>Return whether a char is in the custom unicode range we use.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_LanguageSubsystem__setlanguage">setlanguage()</a></dt></h4><dd>
|
||||
<p><span>setlanguage(self, language: Optional[str], print_change: bool = True, store_to_config: bool = True) -> None</span></p>
|
||||
|
||||
<p>Set the active language used for the game.</p>
|
||||
|
||||
<p>Pass None to use OS default language.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_LanguageSubsystem__translate">translate()</a></dt></h4><dd>
|
||||
<p><span>translate(self, category: str, strval: str, raise_exceptions: bool = False, print_errors: bool = False) -> str</span></p>
|
||||
|
||||
<p>Translate a value (or return the value if no translation available)</p>
|
||||
|
||||
<p>DEPRECATED; use <a href="#class_ba_Lstr">ba.Lstr</a> functionality for these purposes.</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
@ -3907,6 +3967,96 @@ signify that the default soundtrack should be used..</p>
|
||||
<li>TEST</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_MusicSubsystem">ba.MusicSubsystem</a></strong></h3>
|
||||
<p><em><top level class></em>
|
||||
</p>
|
||||
<p>Subsystem for music playback in the app.</p>
|
||||
|
||||
<p>Category: <a href="#class_category_App_Classes">App Classes</a></p>
|
||||
|
||||
<p> To use this class, access the single instance of it at 'ba.app.music'.
|
||||
</p>
|
||||
|
||||
<h3>Methods:</h3>
|
||||
<h5><a href="#method_ba_MusicSubsystem____init__"><constructor></a>, <a href="#method_ba_MusicSubsystem__do_play_music">do_play_music()</a>, <a href="#method_ba_MusicSubsystem__get_music_player">get_music_player()</a>, <a href="#method_ba_MusicSubsystem__get_soundtrack_entry_name">get_soundtrack_entry_name()</a>, <a href="#method_ba_MusicSubsystem__get_soundtrack_entry_type">get_soundtrack_entry_type()</a>, <a href="#method_ba_MusicSubsystem__have_music_player">have_music_player()</a>, <a href="#method_ba_MusicSubsystem__music_volume_changed">music_volume_changed()</a>, <a href="#method_ba_MusicSubsystem__on_app_launch">on_app_launch()</a>, <a href="#method_ba_MusicSubsystem__on_app_resume">on_app_resume()</a>, <a href="#method_ba_MusicSubsystem__on_app_shutdown">on_app_shutdown()</a>, <a href="#method_ba_MusicSubsystem__set_music_play_mode">set_music_play_mode()</a>, <a href="#method_ba_MusicSubsystem__supports_soundtrack_entry_type">supports_soundtrack_entry_type()</a></h5>
|
||||
<dl>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem____init__"><constructor></a></dt></h4><dd>
|
||||
<p><span>ba.MusicSubsystem()</span></p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__do_play_music">do_play_music()</a></dt></h4><dd>
|
||||
<p><span>do_play_music(self, musictype: Union[MusicType, str, None], continuous: bool = False, mode: MusicPlayMode = <MusicPlayMode.REGULAR: regular>, testsoundtrack: Dict[str, Any] = None) -> None</span></p>
|
||||
|
||||
<p>Plays the requested music type/mode.</p>
|
||||
|
||||
<p>For most cases, setmusic() is the proper call to use, which itself
|
||||
calls this. Certain cases, however, such as soundtrack testing, may
|
||||
require calling this directly.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__get_music_player">get_music_player()</a></dt></h4><dd>
|
||||
<p><span>get_music_player(self) -> MusicPlayer</span></p>
|
||||
|
||||
<p>Returns the system music player, instantiating if necessary.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__get_soundtrack_entry_name">get_soundtrack_entry_name()</a></dt></h4><dd>
|
||||
<p><span>get_soundtrack_entry_name(self, entry: Any) -> str</span></p>
|
||||
|
||||
<p>Given a soundtrack entry, returns its name.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__get_soundtrack_entry_type">get_soundtrack_entry_type()</a></dt></h4><dd>
|
||||
<p><span>get_soundtrack_entry_type(self, entry: Any) -> str</span></p>
|
||||
|
||||
<p>Given a soundtrack entry, returns its type, taking into
|
||||
account what is supported locally.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__have_music_player">have_music_player()</a></dt></h4><dd>
|
||||
<p><span>have_music_player(self) -> bool</span></p>
|
||||
|
||||
<p>Returns whether a music player is present.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__music_volume_changed">music_volume_changed()</a></dt></h4><dd>
|
||||
<p><span>music_volume_changed(self, val: float) -> None</span></p>
|
||||
|
||||
<p>Should be called when changing the music volume.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__on_app_launch">on_app_launch()</a></dt></h4><dd>
|
||||
<p><span>on_app_launch(self) -> None</span></p>
|
||||
|
||||
<p>Should be called by app on_app_launch().</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__on_app_resume">on_app_resume()</a></dt></h4><dd>
|
||||
<p><span>on_app_resume(self) -> None</span></p>
|
||||
|
||||
<p>Should be run when the app resumes from a suspended state.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__on_app_shutdown">on_app_shutdown()</a></dt></h4><dd>
|
||||
<p><span>on_app_shutdown(self) -> None</span></p>
|
||||
|
||||
<p>Should be called when the app is shutting down.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__set_music_play_mode">set_music_play_mode()</a></dt></h4><dd>
|
||||
<p><span>set_music_play_mode(self, mode: MusicPlayMode, force_restart: bool = False) -> None</span></p>
|
||||
|
||||
<p>Sets music play mode; used for soundtrack testing/etc.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_MusicSubsystem__supports_soundtrack_entry_type">supports_soundtrack_entry_type()</a></dt></h4><dd>
|
||||
<p><span>supports_soundtrack_entry_type(self, entry_type: str) -> bool</span></p>
|
||||
|
||||
<p>Return whether provided soundtrack entry type is supported here.</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_MusicType">ba.MusicType</a></strong></h3>
|
||||
<p>Inherits from: enum.Enum</p>
|
||||
<p>Types of music available to play in-game.</p>
|
||||
@ -5777,6 +5927,69 @@ self.t = <a href="#class_ba_Timer">ba.Timer</a>(0.3, say_it, repeat=True)
|
||||
<li>SMALL</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_UISubsystem">ba.UISubsystem</a></strong></h3>
|
||||
<p><em><top level class></em>
|
||||
</p>
|
||||
<p>Consolidated UI functionality for the app.</p>
|
||||
|
||||
<p>Category: <a href="#class_category_App_Classes">App Classes</a></p>
|
||||
|
||||
<p> To use this class, access the single instance of it at 'ba.app.ui'.
|
||||
</p>
|
||||
|
||||
<h3>Attributes:</h3>
|
||||
<dl>
|
||||
<dt><h4><a name="attr_ba_UISubsystem__uiscale">uiscale</a></h4></dt><dd>
|
||||
<p><span><a href="#class_ba_UIScale">ba.UIScale</a></span></p>
|
||||
<p>Current ui scale for the app.</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Methods:</h3>
|
||||
<h5><a href="#method_ba_UISubsystem____init__"><constructor></a>, <a href="#method_ba_UISubsystem__clear_main_menu_window">clear_main_menu_window()</a>, <a href="#method_ba_UISubsystem__get_main_menu_location">get_main_menu_location()</a>, <a href="#method_ba_UISubsystem__has_main_menu_window">has_main_menu_window()</a>, <a href="#method_ba_UISubsystem__on_app_launch">on_app_launch()</a>, <a href="#method_ba_UISubsystem__set_main_menu_location">set_main_menu_location()</a>, <a href="#method_ba_UISubsystem__set_main_menu_window">set_main_menu_window()</a></h5>
|
||||
<dl>
|
||||
<dt><h4><a name="method_ba_UISubsystem____init__"><constructor></a></dt></h4><dd>
|
||||
<p><span>ba.UISubsystem()</span></p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_UISubsystem__clear_main_menu_window">clear_main_menu_window()</a></dt></h4><dd>
|
||||
<p><span>clear_main_menu_window(self, transition: str = None) -> None</span></p>
|
||||
|
||||
<p>Clear any existing 'main' window with the provided transition.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_UISubsystem__get_main_menu_location">get_main_menu_location()</a></dt></h4><dd>
|
||||
<p><span>get_main_menu_location(self) -> Optional[str]</span></p>
|
||||
|
||||
<p>Return the current named main menu location, if any.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_UISubsystem__has_main_menu_window">has_main_menu_window()</a></dt></h4><dd>
|
||||
<p><span>has_main_menu_window(self) -> bool</span></p>
|
||||
|
||||
<p>Return whether a main menu window is present.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_UISubsystem__on_app_launch">on_app_launch()</a></dt></h4><dd>
|
||||
<p><span>on_app_launch(self) -> None</span></p>
|
||||
|
||||
<p>Should be run on app launch.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_UISubsystem__set_main_menu_location">set_main_menu_location()</a></dt></h4><dd>
|
||||
<p><span>set_main_menu_location(self, location: str) -> None</span></p>
|
||||
|
||||
<p>Set the location represented by the current main menu window.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_UISubsystem__set_main_menu_window">set_main_menu_window()</a></dt></h4><dd>
|
||||
<p><span>set_main_menu_window(self, window: <a href="#class_ba_Widget">ba.Widget</a>) -> None</span></p>
|
||||
|
||||
<p>Set the current 'main' window, replacing any existing.</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_Vec3">ba.Vec3</a></strong></h3>
|
||||
<p><em><top level class></em>
|
||||
</p>
|
||||
@ -6259,17 +6472,6 @@ method) and will convert it to a None value if it does not exist.
|
||||
For more info, see notes on 'existables' here:
|
||||
https://ballistica.net/wiki/Coding-Style-Guide</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_get_valid_languages">ba.get_valid_languages()</a></strong></h3>
|
||||
<p><span>get_valid_languages() -> List[str]</span></p>
|
||||
|
||||
<p>Return a list containing names of all available languages.</p>
|
||||
|
||||
<p>Category: <a href="#function_category_General_Utility_Functions">General Utility Functions</a></p>
|
||||
|
||||
<p>Languages that may be present but are not displayable on the running
|
||||
version of the game are ignored.</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_getactivity">ba.getactivity()</a></strong></h3>
|
||||
<p><span>getactivity(doraise: bool = True) -> <varies></span></p>
|
||||
@ -6317,6 +6519,8 @@ in the background if necessary.</p>
|
||||
|
||||
<p>Return the in-progress collision.</p>
|
||||
|
||||
<p>Category: <a href="#function_category_Gameplay_Functions">Gameplay Functions</a></p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_getmaps">ba.getmaps()</a></strong></h3>
|
||||
<p><span>getmaps(playtype: str) -> List[str]</span></p>
|
||||
@ -6732,16 +6936,6 @@ are applied to the Widget.</p>
|
||||
'screen' should be a string description of an app location
|
||||
('Main Menu', etc.)</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_setlanguage">ba.setlanguage()</a></strong></h3>
|
||||
<p><span>setlanguage(language: Optional[str], print_change: bool = True, store_to_config: bool = True) -> None</span></p>
|
||||
|
||||
<p>Set the active language used for the game.</p>
|
||||
|
||||
<p>Category: <a href="#function_category_General_Utility_Functions">General Utility Functions</a></p>
|
||||
|
||||
<p>Pass None to use OS default language.</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_setmusic">ba.setmusic()</a></strong></h3>
|
||||
<p><span>setmusic(musictype: Optional[MusicType], continuous: bool = False) -> None</span></p>
|
||||
|
||||
@ -29,8 +29,8 @@
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't change here.
|
||||
const int kAppBuildNumber = 20216;
|
||||
const char* kAppVersion = "1.5.26";
|
||||
const int kAppBuildNumber = 20218;
|
||||
const char* kAppVersion = "1.5.27";
|
||||
|
||||
// Our standalone globals.
|
||||
// These are separated out for easy access.
|
||||
|
||||
@ -10,7 +10,7 @@ def get_binding_values() -> object:
|
||||
import json
|
||||
import copy
|
||||
import ba
|
||||
from ba import _lang
|
||||
from ba import _language
|
||||
from ba import _music
|
||||
from ba import _input
|
||||
from ba import _apputils
|
||||
@ -99,8 +99,8 @@ def get_binding_values() -> object:
|
||||
party.handle_party_invite, # kHandlePartyInviteCall
|
||||
_music.do_play_music, # kDoPlayMusicCall
|
||||
ba.app.handle_deep_link, # kDeepLinkCall
|
||||
_lang.get_resource, # kGetResourceCall
|
||||
_lang.translate, # kTranslateCall
|
||||
ba.app.lang.get_resource, # kGetResourceCall
|
||||
ba.app.lang.translate, # kTranslateCall
|
||||
ba.Lstr, # kLStrClass
|
||||
ba.Call, # kCallClass
|
||||
_apputils.garbage_collect, # kGarbageCollectCall
|
||||
@ -122,5 +122,5 @@ def get_binding_values() -> object:
|
||||
_enums.SpecialChar, # kSpecialCharClass
|
||||
_player.Player, # kPlayerClass
|
||||
_hooks.get_player_icon, # kGetPlayerIconCall
|
||||
_lang.Lstr.from_json, # kLstrFromJsonCall
|
||||
_language.Lstr.from_json, # kLstrFromJsonCall
|
||||
) # yapf: disable
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user