Merge branch 'master' into pubsync

This commit is contained in:
Eric Froemling 2020-04-10 00:47:01 -07:00
commit 967d0f19d8
7 changed files with 72 additions and 83 deletions

View File

@ -1556,6 +1556,7 @@
<w>spammy</w>
<w>sparx</w>
<w>spawner</w>
<w>spawners</w>
<w>spawnpoints</w>
<w>spawnpt</w>
<w>spawntype</w>

View File

@ -34,6 +34,7 @@ if TYPE_CHECKING:
from typing import (List, Optional, Dict, Type, Any, Callable, Sequence,
Tuple, Union)
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.bomb import TNTSpawner
import ba
@ -330,7 +331,7 @@ class GameActivity(Activity):
self._map_type.preload()
self._map: Optional[ba.Map] = None
self._powerup_drop_timer: Optional[ba.Timer] = None
self._tnt_objs: Optional[Dict[int, Any]] = None
self._tnt_spawners: Optional[Dict[int, TNTSpawner]] = None
self._tnt_drop_timer: Optional[ba.Timer] = None
self.initial_player_info: Optional[List[Dict[str, Any]]] = None
self._game_scoreboard_name_text: Optional[ba.Actor] = None
@ -1111,12 +1112,8 @@ class GameActivity(Activity):
repeat=True)
self._standard_drop_powerups()
if enable_tnt:
self._tnt_objs = {}
self._tnt_drop_timer = _ba.Timer(5.5,
_general.WeakCall(
self._standard_drop_tnt),
repeat=True)
self._standard_drop_tnt()
self._tnt_spawners = {}
self._setup_standard_tnt_drops()
def _standard_drop_powerup(self, index: int, expire: bool = True) -> None:
# pylint: disable=cyclic-import
@ -1136,24 +1133,15 @@ class GameActivity(Activity):
_ba.timer(i * 0.4, _general.WeakCall(self._standard_drop_powerup,
i))
def _standard_drop_tnt(self) -> None:
def _setup_standard_tnt_drops(self) -> None:
"""Standard tnt drop."""
# pylint: disable=cyclic-import
from bastd.actor import bomb
from bastd.actor.bomb import TNTSpawner
# Drop TNT on the map for any tnt location with no existing tnt box.
for i, point in enumerate(self.map.tnt_points):
assert self._tnt_objs is not None
if i not in self._tnt_objs:
self._tnt_objs[i] = {'absent_ticks': 9999, 'obj': None}
tnt_obj = self._tnt_objs[i]
# Respawn once its been dead for a while.
if not tnt_obj['obj']:
tnt_obj['absent_ticks'] += 1
if tnt_obj['absent_ticks'] > 3:
tnt_obj['obj'] = bomb.Bomb(position=point, bomb_type='tnt')
tnt_obj['absent_ticks'] = 0
assert self._tnt_spawners is not None
if self._tnt_spawners.get(i) is None:
self._tnt_spawners[i] = TNTSpawner(point)
def setup_standard_time_limit(self, duration: float) -> None:
"""

View File

@ -87,16 +87,18 @@ class ServerCallThread(threading.Thread):
activity) if activity is not None else None
def _run_callback(self, arg: Union[None, Dict[str, Any]]) -> None:
# If we were created in an activity context and that activity has
# since died, do nothing (hmm should we be using a context-call
# instead of doing this manually?).
activity = None if self._activity is None else self._activity()
if activity is None or activity.is_expired():
return
# since died, do nothing.
# FIXME: Should we just be using a ContextCall instead of doing
# this check manually?
if self._activity is not None:
activity = self._activity()
if activity is None or activity.is_expired():
return
# Technically we could do the same check for session contexts,
# but not gonna worry about it for now.
assert self._context is not None
assert self._callback is not None
with self._context:
self._callback(arg)

View File

@ -29,6 +29,8 @@ from typing import TYPE_CHECKING
import _ba
import ba
from ba.internal import get_achievements_for_coop_level
from bastd.actor.text import Text
from bastd.actor.zoomtext import ZoomText
if TYPE_CHECKING:
from typing import Optional, Tuple, List, Dict, Any, Sequence
@ -50,9 +52,6 @@ class CoopScoreScreen(ba.Activity):
self.inherits_music = True
self.use_fixed_vr_overlay = True
self._tournament_time_remaining = None
self._tournament_time_remaining_text = None
self._do_new_rating: bool = self.session.tournament_id is not None
self._score_display_sound = ba.getsound("scoreHit01")
@ -134,6 +133,8 @@ class CoopScoreScreen(ba.Activity):
self._name_str: Optional[str] = None
self._friends_loading_status: Optional[ba.Actor] = None
self._score_loading_status: Optional[ba.Actor] = None
self._tournament_time_remaining: Optional[float] = None
self._tournament_time_remaining_text: Optional[Text] = None
self._tournament_time_remaining_text_timer: Optional[ba.Timer] = None
self._player_info = settings['player_info']
@ -288,7 +289,6 @@ class CoopScoreScreen(ba.Activity):
ba.open_url(self._score_link)
def _ui_error(self) -> None:
from bastd.actor.text import Text
with ba.Context(self):
self._next_level_error = Text(
ba.Lstr(resource='completeThisLevelToProceedText'),
@ -515,8 +515,6 @@ class CoopScoreScreen(ba.Activity):
# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
from bastd.actor.text import Text
from bastd.actor.zoomtext import ZoomText
super().on_begin()
# Calc whether the level is complete and other stuff.
@ -893,7 +891,6 @@ class CoopScoreScreen(ba.Activity):
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# delay a bit if results come in too fast
from bastd.actor.text import Text
base_delay = max(0, 1.9 - (ba.time() - self._begin_time))
ts_height = 300
ts_h_offs = -550
@ -1009,7 +1006,6 @@ class CoopScoreScreen(ba.Activity):
# We need to manually run this in the context of our activity
# and only if we aren't shutting down.
# (really should make the submit_score call handle that stuff itself)
from bastd.actor.text import Text
if self.is_expired():
return
with ba.Context(self):
@ -1169,10 +1165,9 @@ class CoopScoreScreen(ba.Activity):
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
from ba.internal import get_tournament_prize_strings
from bastd.actor.text import Text
from bastd.actor.zoomtext import ZoomText
assert self._show_info is not None
available = (self._show_info['results'] is not None)
if available:
error = (self._show_info['results']['error']
if 'error' in self._show_info['results'] else None)
@ -1193,22 +1188,22 @@ class CoopScoreScreen(ba.Activity):
Text(ba.Lstr(resource='coopSelectWindow.timeRemainingText'),
position=(-360, -70 - 100),
color=(1, 1, 1, 0.7),
h_align='center',
v_align='center',
transition='fade_in',
h_align=Text.HAlign.CENTER,
v_align=Text.VAlign.CENTER,
transition=Text.Transition.FADE_IN,
scale=0.8,
maxwidth=300,
transition_delay=2.0).autoretain()
self._tournament_time_remaining_text = Text('',
position=(-360,
-110 - 100),
color=(1, 1, 1, 0.7),
h_align='center',
v_align='center',
transition='fade_in',
scale=1.6,
maxwidth=150,
transition_delay=2.0)
self._tournament_time_remaining_text = Text(
'',
position=(-360, -110 - 100),
color=(1, 1, 1, 0.7),
h_align=Text.HAlign.CENTER,
v_align=Text.VAlign.CENTER,
transition=Text.Transition.FADE_IN,
scale=1.6,
maxwidth=150,
transition_delay=2.0)
# If we're a tournament, show prizes.
try:
@ -1439,8 +1434,6 @@ class CoopScoreScreen(ba.Activity):
ba.timer(0.35, ba.Call(ba.playsound, self.cymbal_sound))
def _show_fail(self) -> None:
from bastd.actor.text import Text
from bastd.actor.zoomtext import ZoomText
ZoomText(ba.Lstr(resource='failText'),
maxwidth=300,
flash=False,
@ -1460,8 +1453,6 @@ class CoopScoreScreen(ba.Activity):
ba.timer(0.35, ba.Call(ba.playsound, self._score_display_sound))
def _show_score_val(self, offs_x: float) -> None:
from bastd.actor.text import Text
from bastd.actor.zoomtext import ZoomText
assert self._score_type is not None
assert self._score is not None
ZoomText((str(self._score) if self._score_type == 'points' else

View File

@ -1047,23 +1047,24 @@ class TNTSpawner:
category: Gameplay Classes
"""
def __init__(self, position: Sequence[float], respawn_time: float = 30.0):
def __init__(self, position: Sequence[float], respawn_time: float = 20.0):
"""Instantiate with given position and respawn_time (in seconds)."""
self._position = position
self._tnt: Optional[Bomb] = None
self._respawn_time = random.uniform(0.8, 1.2) * respawn_time
self._wait_time = 0.0
self._update()
# (go with slightly more than 1 second to avoid timer stacking)
# Go with slightly more than 1 second to avoid timer stacking.
self._update_timer = ba.Timer(1.1,
ba.WeakCall(self._update),
repeat=True)
self._respawn_time = random.uniform(0.8, 1.2) * respawn_time
self._wait_time = 0.0
def _update(self) -> None:
tnt_alive = self._tnt is not None and self._tnt.node
if not tnt_alive:
# respawn if its been long enough.. otherwise just increment our
# how-long-since-we-died value
# Respawn if its been long enough.. otherwise just increment our
# how-long-since-we-died value.
if self._tnt is None or self._wait_time >= self._respawn_time:
self._tnt = Bomb(position=self._position, bomb_type='tnt')
self._wait_time = 0.0

View File

@ -164,7 +164,7 @@ class MeteorShowerGame(ba.TeamGameActivity):
# Augment standard behavior.
super().handlemessage(msg)
death_time = ba.time()
death_time = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
# Record the player's moment of death.
msg.spaz.player.gamedata['death_time'] = death_time
@ -240,8 +240,10 @@ class MeteorShowerGame(ba.TeamGameActivity):
self._meteor_time = max(0.01, self._meteor_time * 0.9)
def end_game(self) -> None:
cur_time = ba.time()
cur_time = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
assert self._timer is not None
start_time = self._timer.getstarttime(
timeformat=ba.TimeFormat.MILLISECONDS)
# Mark 'death-time' as now for any still-living players
# and award players points for how long they lasted.
@ -252,13 +254,14 @@ class MeteorShowerGame(ba.TeamGameActivity):
# Throw an extra fudge factor in so teams that
# didn't die come out ahead of teams that did.
if 'death_time' not in player.gamedata:
player.gamedata['death_time'] = cur_time + 0.001
player.gamedata['death_time'] = cur_time + 1
# Award a per-player score depending on how many seconds
# they lasted (per-player scores only affect teams mode;
# everywhere else just looks at the per-team score).
score = int(player.gamedata['death_time'] -
self._timer.getstarttime())
self._timer.getstarttime(
timeformat=ba.TimeFormat.MILLISECONDS))
if 'death_time' not in player.gamedata:
score += 50 # a bit extra for survivors
self.stats.player_scored(player, score, screenmessage=False)
@ -281,8 +284,7 @@ class MeteorShowerGame(ba.TeamGameActivity):
longest_life = 0
for player in team.players:
longest_life = max(longest_life,
(player.gamedata['death_time'] -
self._timer.getstarttime()))
player.gamedata['death_time'] - start_time)
results.set_team_score(team, longest_life)
self.end(results=results)

View File

@ -88,7 +88,7 @@ class StoreBrowserWindow(ba.Window):
self._request: Any = None
self._r = 'store'
self._last_buy_time = None
self._last_buy_time: Optional[float] = None
super().__init__(root_widget=ba.containerwidget(
size=(self._width, self._height + extra_top),
@ -179,7 +179,7 @@ class StoreBrowserWindow(ba.Window):
('minigames', ba.Lstr(resource=self._r + '.miniGamesText')),
('characters', ba.Lstr(resource=self._r + '.charactersText')),
('icons', ba.Lstr(resource=self._r + '.iconsText')),
] # yapf : disable
]
tab_results = tabs.create_tab_buttons(self._root_widget,
tabs_def,
@ -454,16 +454,19 @@ class StoreBrowserWindow(ba.Window):
# purchase this. Better to fail now than after we've
# paid locally.
app = ba.app
serverget('bsAccountPurchaseCheck', {
'item': item,
'platform': app.platform,
'subplatform': app.subplatform,
'version': app.version,
'buildNumber': app.build_number,
'purchaseType': 'ticket' if is_ticket_purchase else 'real'
},
callback=ba.WeakCall(self._purchase_check_result, item,
is_ticket_purchase))
serverget(
'bsAccountPurchaseCheck',
{
'item': item,
'platform': app.platform,
'subplatform': app.subplatform,
'version': app.version,
'buildNumber': app.build_number,
'purchaseType': 'ticket' if is_ticket_purchase else 'real'
},
callback=ba.WeakCall(self._purchase_check_result, item,
is_ticket_purchase),
)
def buy(self, item: str) -> None:
"""Attempt to purchase the provided item."""
@ -476,7 +479,8 @@ class StoreBrowserWindow(ba.Window):
# Prevent pressing buy within a few seconds of the last press
# (gives the buttons time to disable themselves and whatnot).
curtime = ba.time(ba.TimeType.REAL)
if self._last_buy_time is None or curtime - self._last_buy_time < 2.0:
if self._last_buy_time is not None and (curtime -
self._last_buy_time) < 2.0:
ba.playsound(ba.getsound('error'))
else:
if _ba.get_account_state() != 'signed_in':
@ -537,8 +541,7 @@ class StoreBrowserWindow(ba.Window):
sales_raw = _ba.get_account_misc_read_val('sales', {})
sales = {}
try:
# look at the current set of sales; filter any with
# time remaining..
# Look at the current set of sales; filter any with time remaining.
for sale_item, sale_info in list(sales_raw.items()):
to_end = (datetime.datetime.utcfromtimestamp(sale_info['e']) -
datetime.datetime.utcnow()).total_seconds()
@ -548,7 +551,7 @@ class StoreBrowserWindow(ba.Window):
'original_price': sale_info['op']
}
except Exception:
ba.print_exception("Error parsing sales")
ba.print_exception("Error parsing sales.")
assert self.button_infos is not None
for b_type, b_info in self.button_infos.items():
@ -602,7 +605,8 @@ class StoreBrowserWindow(ba.Window):
price_text_right = ''
else:
price = _ba.get_account_misc_read_val('price.' + b_type, 0)
# color button differently if we cant afford this
# Color the button differently if we cant afford this.
if _ba.get_account_state() == 'signed_in':
if _ba.get_account_ticket_count() < price:
color = (0.6, 0.61, 0.6)