mirror of
https://github.com/hypervortex/VH-Bombsquad-Modded-Server-Files
synced 2026-08-15 13:04:50 +00:00
Initial commit
This commit is contained in:
parent
bc49523c99
commit
44d606cce7
1929 changed files with 612166 additions and 0 deletions
1
dist/ba_data/python/bastd/activity/__init__.py
vendored
Normal file
1
dist/ba_data/python/bastd/activity/__init__.py
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
BIN
dist/ba_data/python/bastd/activity/__pycache__/__init__.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/__init__.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/bastd/activity/__pycache__/coopscore.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/coopscore.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/bastd/activity/__pycache__/drawscore.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/drawscore.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/bastd/activity/__pycache__/dualteamscore.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/dualteamscore.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/bastd/activity/__pycache__/multiteamjoin.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/multiteamjoin.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/bastd/activity/__pycache__/multiteamscore.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/multiteamscore.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/bastd/activity/__pycache__/multiteamvictory.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bastd/activity/__pycache__/multiteamvictory.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
112
dist/ba_data/python/bastd/activity/coopjoin.py
vendored
Normal file
112
dist/ba_data/python/bastd/activity/coopjoin.py
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the co-op join screen."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from ba.internal import JoinActivity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class CoopJoinActivity(JoinActivity):
|
||||
"""Join-screen for co-op mode."""
|
||||
|
||||
# We can assume our session is a CoopSession.
|
||||
session: ba.CoopSession
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
super().__init__(settings)
|
||||
session = self.session
|
||||
assert isinstance(session, ba.CoopSession)
|
||||
|
||||
def on_transition_in(self) -> None:
|
||||
from bastd.actor.controlsguide import ControlsGuide
|
||||
from bastd.actor.text import Text
|
||||
|
||||
super().on_transition_in()
|
||||
assert isinstance(self.session, ba.CoopSession)
|
||||
assert self.session.campaign
|
||||
Text(
|
||||
self.session.campaign.getlevel(
|
||||
self.session.campaign_level_name
|
||||
).displayname,
|
||||
scale=1.3,
|
||||
h_attach=Text.HAttach.CENTER,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
v_attach=Text.VAttach.TOP,
|
||||
transition=Text.Transition.FADE_IN,
|
||||
transition_delay=4.0,
|
||||
color=(1, 1, 1, 0.6),
|
||||
position=(0, -95),
|
||||
).autoretain()
|
||||
ControlsGuide(delay=1.0).autoretain()
|
||||
|
||||
ba.pushcall(self._show_remaining_achievements)
|
||||
|
||||
def _show_remaining_achievements(self) -> None:
|
||||
from bastd.actor.text import Text
|
||||
|
||||
# We only show achievements and challenges for CoopGameActivities.
|
||||
session = self.session
|
||||
assert isinstance(session, ba.CoopSession)
|
||||
gameinstance = session.get_current_game_instance()
|
||||
if not isinstance(gameinstance, ba.CoopGameActivity):
|
||||
return
|
||||
|
||||
delay = 1.0
|
||||
vpos = -140.0
|
||||
|
||||
# Now list our remaining achievements for this level.
|
||||
assert self.session.campaign is not None
|
||||
assert isinstance(self.session, ba.CoopSession)
|
||||
levelname = (
|
||||
self.session.campaign.name + ':' + self.session.campaign_level_name
|
||||
)
|
||||
ts_h_offs = 60
|
||||
|
||||
if not (ba.app.demo_mode or ba.app.arcade_mode):
|
||||
achievements = [
|
||||
a
|
||||
for a in ba.app.ach.achievements_for_coop_level(levelname)
|
||||
if not a.complete
|
||||
]
|
||||
have_achievements = bool(achievements)
|
||||
achievements = [a for a in achievements if not a.complete]
|
||||
vrmode = ba.app.vr_mode
|
||||
if have_achievements:
|
||||
Text(
|
||||
ba.Lstr(resource='achievementsRemainingText'),
|
||||
host_only=True,
|
||||
position=(ts_h_offs - 10, vpos),
|
||||
transition=Text.Transition.FADE_IN,
|
||||
scale=1.1 * 0.76,
|
||||
h_attach=Text.HAttach.LEFT,
|
||||
v_attach=Text.VAttach.TOP,
|
||||
color=(1, 1, 1.2, 1) if vrmode else (0.8, 0.8, 1, 1),
|
||||
shadow=1.0,
|
||||
flatness=1.0 if vrmode else 0.6,
|
||||
transition_delay=delay,
|
||||
).autoretain()
|
||||
hval = ts_h_offs + 50
|
||||
vpos -= 35
|
||||
for ach in achievements:
|
||||
delay += 0.05
|
||||
ach.create_display(hval, vpos, delay, style='in_game')
|
||||
vpos -= 55
|
||||
if not achievements:
|
||||
Text(
|
||||
ba.Lstr(resource='noAchievementsRemainingText'),
|
||||
host_only=True,
|
||||
position=(ts_h_offs + 15, vpos + 10),
|
||||
transition=Text.Transition.FADE_IN,
|
||||
scale=0.7,
|
||||
h_attach=Text.HAttach.LEFT,
|
||||
v_attach=Text.VAttach.TOP,
|
||||
color=(1, 1, 1, 0.5),
|
||||
transition_delay=delay + 0.5,
|
||||
).autoretain()
|
||||
1777
dist/ba_data/python/bastd/activity/coopscore.py
vendored
Normal file
1777
dist/ba_data/python/bastd/activity/coopscore.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
36
dist/ba_data/python/bastd/activity/drawscore.py
vendored
Normal file
36
dist/ba_data/python/bastd/activity/drawscore.py
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the draw screen."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
from bastd.actor.zoomtext import ZoomText
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
"""Score screen shown after a draw."""
|
||||
|
||||
default_music = None # Awkward silence...
|
||||
|
||||
def on_begin(self) -> None:
|
||||
ba.set_analytics_screen('Draw Score Screen')
|
||||
super().on_begin()
|
||||
ZoomText(
|
||||
ba.Lstr(resource='drawText'),
|
||||
position=(0, 0),
|
||||
maxwidth=400,
|
||||
shiftposition=(-220, 0),
|
||||
shiftdelay=2.0,
|
||||
flash=False,
|
||||
trail=False,
|
||||
jitter=1.0,
|
||||
).autoretain()
|
||||
ba.timer(0.35, ba.Call(ba.playsound, self._score_display_sound))
|
||||
self.show_player_scores(results=self.settings_raw.get('results', None))
|
||||
168
dist/ba_data/python/bastd/activity/dualteamscore.py
vendored
Normal file
168
dist/ba_data/python/bastd/activity/dualteamscore.py
vendored
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the end screen in dual-team mode."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
from bastd.actor.zoomtext import ZoomText
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
"""Scorescreen between rounds of a dual-team session."""
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
super().__init__(settings=settings)
|
||||
self._winner: ba.SessionTeam = settings['winner']
|
||||
assert isinstance(self._winner, ba.SessionTeam)
|
||||
|
||||
def on_begin(self) -> None:
|
||||
ba.set_analytics_screen('Teams Score Screen')
|
||||
super().on_begin()
|
||||
|
||||
height = 130
|
||||
active_team_count = len(self.teams)
|
||||
vval = (height * active_team_count) / 2 - height / 2
|
||||
i = 0
|
||||
shift_time = 2.5
|
||||
|
||||
# Usually we say 'Best of 7', but if the language prefers we can say
|
||||
# 'First to 4'.
|
||||
session = self.session
|
||||
assert isinstance(session, ba.MultiTeamSession)
|
||||
if ba.app.lang.get_resource('bestOfUseFirstToInstead'):
|
||||
best_txt = ba.Lstr(
|
||||
resource='firstToSeriesText',
|
||||
subs=[('${COUNT}', str(session.get_series_length() / 2 + 1))],
|
||||
)
|
||||
else:
|
||||
best_txt = ba.Lstr(
|
||||
resource='bestOfSeriesText',
|
||||
subs=[('${COUNT}', str(session.get_series_length()))],
|
||||
)
|
||||
|
||||
ZoomText(
|
||||
best_txt,
|
||||
position=(0, 175),
|
||||
shiftposition=(-250, 175),
|
||||
shiftdelay=2.5,
|
||||
flash=False,
|
||||
trail=False,
|
||||
h_align='center',
|
||||
scale=0.25,
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
jitter=3.0,
|
||||
).autoretain()
|
||||
for team in self.session.sessionteams:
|
||||
ba.timer(
|
||||
i * 0.15 + 0.15,
|
||||
ba.WeakCall(
|
||||
self._show_team_name,
|
||||
vval - i * height,
|
||||
team,
|
||||
i * 0.2,
|
||||
shift_time - (i * 0.150 + 0.150),
|
||||
),
|
||||
)
|
||||
ba.timer(
|
||||
i * 0.150 + 0.5,
|
||||
ba.Call(ba.playsound, self._score_display_sound_small),
|
||||
)
|
||||
scored = team is self._winner
|
||||
delay = 0.2
|
||||
if scored:
|
||||
delay = 1.2
|
||||
ba.timer(
|
||||
i * 0.150 + 0.2,
|
||||
ba.WeakCall(
|
||||
self._show_team_old_score,
|
||||
vval - i * height,
|
||||
team,
|
||||
shift_time - (i * 0.15 + 0.2),
|
||||
),
|
||||
)
|
||||
ba.timer(
|
||||
i * 0.15 + 1.5,
|
||||
ba.Call(ba.playsound, self._score_display_sound),
|
||||
)
|
||||
|
||||
ba.timer(
|
||||
i * 0.150 + delay,
|
||||
ba.WeakCall(
|
||||
self._show_team_score,
|
||||
vval - i * height,
|
||||
team,
|
||||
scored,
|
||||
i * 0.2 + 0.1,
|
||||
shift_time - (i * 0.15 + delay),
|
||||
),
|
||||
)
|
||||
i += 1
|
||||
self.show_player_scores()
|
||||
|
||||
def _show_team_name(
|
||||
self,
|
||||
pos_v: float,
|
||||
team: ba.SessionTeam,
|
||||
kill_delay: float,
|
||||
shiftdelay: float,
|
||||
) -> None:
|
||||
del kill_delay # Unused arg.
|
||||
ZoomText(
|
||||
ba.Lstr(value='${A}:', subs=[('${A}', team.name)]),
|
||||
position=(100, pos_v),
|
||||
shiftposition=(-150, pos_v),
|
||||
shiftdelay=shiftdelay,
|
||||
flash=False,
|
||||
trail=False,
|
||||
h_align='right',
|
||||
maxwidth=300,
|
||||
color=team.color,
|
||||
jitter=1.0,
|
||||
).autoretain()
|
||||
|
||||
def _show_team_old_score(
|
||||
self, pos_v: float, sessionteam: ba.SessionTeam, shiftdelay: float
|
||||
) -> None:
|
||||
ZoomText(
|
||||
str(sessionteam.customdata['score'] - 1),
|
||||
position=(150, pos_v),
|
||||
maxwidth=100,
|
||||
color=(0.6, 0.6, 0.7),
|
||||
shiftposition=(-100, pos_v),
|
||||
shiftdelay=shiftdelay,
|
||||
flash=False,
|
||||
trail=False,
|
||||
lifespan=1.0,
|
||||
h_align='left',
|
||||
jitter=1.0,
|
||||
).autoretain()
|
||||
|
||||
def _show_team_score(
|
||||
self,
|
||||
pos_v: float,
|
||||
sessionteam: ba.SessionTeam,
|
||||
scored: bool,
|
||||
kill_delay: float,
|
||||
shiftdelay: float,
|
||||
) -> None:
|
||||
del kill_delay # Unused arg.
|
||||
ZoomText(
|
||||
str(sessionteam.customdata['score']),
|
||||
position=(150, pos_v),
|
||||
maxwidth=100,
|
||||
color=(1.0, 0.9, 0.5) if scored else (0.6, 0.6, 0.7),
|
||||
shiftposition=(-100, pos_v),
|
||||
shiftdelay=shiftdelay,
|
||||
flash=scored,
|
||||
trail=scored,
|
||||
h_align='left',
|
||||
jitter=1.0,
|
||||
trailcolor=(1, 0.8, 0.0, 0),
|
||||
).autoretain()
|
||||
368
dist/ba_data/python/bastd/activity/freeforallvictory.py
vendored
Normal file
368
dist/ba_data/python/bastd/activity/freeforallvictory.py
vendored
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the final screen in free-for-all games."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
|
||||
|
||||
class FreeForAllVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
"""Score screen shown at after free-for-all rounds."""
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
super().__init__(settings=settings)
|
||||
|
||||
# Keep prev activity alive while we fade in.
|
||||
self.transition_time = 0.5
|
||||
self._cymbal_sound = ba.getsound('cymbal')
|
||||
|
||||
def on_begin(self) -> None:
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
from bastd.actor.text import Text
|
||||
from bastd.actor.image import Image
|
||||
|
||||
ba.set_analytics_screen('FreeForAll Score Screen')
|
||||
super().on_begin()
|
||||
|
||||
y_base = 100.0
|
||||
ts_h_offs = -305.0
|
||||
tdelay = 1.0
|
||||
scale = 1.2
|
||||
spacing = 37.0
|
||||
|
||||
# We include name and previous score in the sort to reduce the amount
|
||||
# of random jumping around the list we do in cases of ties.
|
||||
player_order_prev = list(self.players)
|
||||
player_order_prev.sort(
|
||||
reverse=True,
|
||||
key=lambda p: (
|
||||
p.team.sessionteam.customdata['previous_score'],
|
||||
p.getname(full=True),
|
||||
),
|
||||
)
|
||||
player_order = list(self.players)
|
||||
player_order.sort(
|
||||
reverse=True,
|
||||
key=lambda p: (
|
||||
p.team.sessionteam.customdata['score'],
|
||||
p.team.sessionteam.customdata['score'],
|
||||
p.getname(full=True),
|
||||
),
|
||||
)
|
||||
|
||||
v_offs = -74.0 + spacing * len(player_order_prev) * 0.5
|
||||
delay1 = 1.3 + 0.1
|
||||
delay2 = 2.9 + 0.1
|
||||
delay3 = 2.9 + 0.1
|
||||
order_change = player_order != player_order_prev
|
||||
|
||||
if order_change:
|
||||
delay3 += 1.5
|
||||
|
||||
ba.timer(0.3, ba.Call(ba.playsound, self._score_display_sound))
|
||||
results = self.settings_raw['results']
|
||||
assert isinstance(results, ba.GameResults)
|
||||
self.show_player_scores(
|
||||
delay=0.001, results=results, scale=1.2, x_offset=-110.0
|
||||
)
|
||||
|
||||
sound_times: set[float] = set()
|
||||
|
||||
def _scoretxt(
|
||||
text: str,
|
||||
x_offs: float,
|
||||
y_offs: float,
|
||||
highlight: bool,
|
||||
delay: float,
|
||||
extrascale: float,
|
||||
flash: bool = False,
|
||||
) -> Text:
|
||||
return Text(
|
||||
text,
|
||||
position=(
|
||||
ts_h_offs + x_offs * scale,
|
||||
y_base + (y_offs + v_offs + 2.0) * scale,
|
||||
),
|
||||
scale=scale * extrascale,
|
||||
color=(
|
||||
(1.0, 0.7, 0.3, 1.0) if highlight else (0.7, 0.7, 0.7, 0.7)
|
||||
),
|
||||
h_align=Text.HAlign.RIGHT,
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay + delay,
|
||||
flash=flash,
|
||||
).autoretain()
|
||||
|
||||
v_offs -= spacing
|
||||
slide_amt = 0.0
|
||||
transtime = 0.250
|
||||
transtime2 = 0.250
|
||||
|
||||
session = self.session
|
||||
assert isinstance(session, ba.FreeForAllSession)
|
||||
title = Text(
|
||||
ba.Lstr(
|
||||
resource='firstToSeriesText',
|
||||
subs=[('${COUNT}', str(session.get_ffa_series_length()))],
|
||||
),
|
||||
scale=1.05 * scale,
|
||||
position=(
|
||||
ts_h_offs - 0.0 * scale,
|
||||
y_base + (v_offs + 50.0) * scale,
|
||||
),
|
||||
h_align=Text.HAlign.CENTER,
|
||||
color=(0.5, 0.5, 0.5, 0.5),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
|
||||
v_offs -= 25
|
||||
v_offs_start = v_offs
|
||||
|
||||
ba.timer(
|
||||
tdelay + delay3,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
title.position_combine,
|
||||
'input0',
|
||||
{
|
||||
0.0: ts_h_offs - 0.0 * scale,
|
||||
transtime2: ts_h_offs - (0.0 + slide_amt) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
for i, player in enumerate(player_order_prev):
|
||||
v_offs_2 = v_offs_start - spacing * (player_order.index(player))
|
||||
ba.timer(
|
||||
tdelay + 0.3,
|
||||
ba.Call(ba.playsound, self._score_display_sound_small),
|
||||
)
|
||||
if order_change:
|
||||
ba.timer(
|
||||
tdelay + delay2 + 0.1,
|
||||
ba.Call(ba.playsound, self._cymbal_sound),
|
||||
)
|
||||
img = Image(
|
||||
player.get_icon(),
|
||||
position=(
|
||||
ts_h_offs - 72.0 * scale,
|
||||
y_base + (v_offs + 15.0) * scale,
|
||||
),
|
||||
scale=(30.0 * scale, 30.0 * scale),
|
||||
transition=Image.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
ba.timer(
|
||||
tdelay + delay2,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
img.position_combine,
|
||||
'input1',
|
||||
{
|
||||
0: y_base + (v_offs + 15.0) * scale,
|
||||
transtime: y_base + (v_offs_2 + 15.0) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
ba.timer(
|
||||
tdelay + delay3,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
img.position_combine,
|
||||
'input0',
|
||||
{
|
||||
0: ts_h_offs - 72.0 * scale,
|
||||
transtime2: ts_h_offs - (72.0 + slide_amt) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
txt = Text(
|
||||
ba.Lstr(value=player.getname(full=True)),
|
||||
maxwidth=130.0,
|
||||
scale=0.75 * scale,
|
||||
position=(
|
||||
ts_h_offs - 50.0 * scale,
|
||||
y_base + (v_offs + 15.0) * scale,
|
||||
),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
color=ba.safecolor(player.team.color + (1,)),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
ba.timer(
|
||||
tdelay + delay2,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
txt.position_combine,
|
||||
'input1',
|
||||
{
|
||||
0: y_base + (v_offs + 15.0) * scale,
|
||||
transtime: y_base + (v_offs_2 + 15.0) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
ba.timer(
|
||||
tdelay + delay3,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
txt.position_combine,
|
||||
'input0',
|
||||
{
|
||||
0: ts_h_offs - 50.0 * scale,
|
||||
transtime2: ts_h_offs - (50.0 + slide_amt) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
txt_num = Text(
|
||||
'#' + str(i + 1),
|
||||
scale=0.55 * scale,
|
||||
position=(
|
||||
ts_h_offs - 95.0 * scale,
|
||||
y_base + (v_offs + 8.0) * scale,
|
||||
),
|
||||
h_align=Text.HAlign.RIGHT,
|
||||
color=(0.6, 0.6, 0.6, 0.6),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
ba.timer(
|
||||
tdelay + delay3,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
txt_num.position_combine,
|
||||
'input0',
|
||||
{
|
||||
0: ts_h_offs - 95.0 * scale,
|
||||
transtime2: ts_h_offs - (95.0 + slide_amt) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
s_txt = _scoretxt(
|
||||
str(player.team.sessionteam.customdata['previous_score']),
|
||||
80,
|
||||
0,
|
||||
False,
|
||||
0,
|
||||
1.0,
|
||||
)
|
||||
ba.timer(
|
||||
tdelay + delay2,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
s_txt.position_combine,
|
||||
'input1',
|
||||
{
|
||||
0: y_base + (v_offs + 2.0) * scale,
|
||||
transtime: y_base + (v_offs_2 + 2.0) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
ba.timer(
|
||||
tdelay + delay3,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
s_txt.position_combine,
|
||||
'input0',
|
||||
{
|
||||
0: ts_h_offs + 80.0 * scale,
|
||||
transtime2: ts_h_offs + (80.0 - slide_amt) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
score_change = (
|
||||
player.team.sessionteam.customdata['score']
|
||||
- player.team.sessionteam.customdata['previous_score']
|
||||
)
|
||||
if score_change > 0:
|
||||
xval = 113
|
||||
yval = 3.0
|
||||
s_txt_2 = _scoretxt(
|
||||
'+' + str(score_change),
|
||||
xval,
|
||||
yval,
|
||||
True,
|
||||
0,
|
||||
0.7,
|
||||
flash=True,
|
||||
)
|
||||
ba.timer(
|
||||
tdelay + delay2,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
s_txt_2.position_combine,
|
||||
'input1',
|
||||
{
|
||||
0: y_base + (v_offs + yval + 2.0) * scale,
|
||||
transtime: y_base + (v_offs_2 + yval + 2.0) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
ba.timer(
|
||||
tdelay + delay3,
|
||||
ba.WeakCall(
|
||||
self._safe_animate,
|
||||
s_txt_2.position_combine,
|
||||
'input0',
|
||||
{
|
||||
0: ts_h_offs + xval * scale,
|
||||
transtime2: ts_h_offs + (xval - slide_amt) * scale,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def _safesetattr(
|
||||
node: ba.Node | None, attr: str, value: Any
|
||||
) -> None:
|
||||
if node:
|
||||
setattr(node, attr, value)
|
||||
|
||||
ba.timer(
|
||||
tdelay + delay1,
|
||||
ba.Call(_safesetattr, s_txt.node, 'color', (1, 1, 1, 1)),
|
||||
)
|
||||
for j in range(score_change):
|
||||
ba.timer(
|
||||
(tdelay + delay1 + 0.15 * j),
|
||||
ba.Call(
|
||||
_safesetattr,
|
||||
s_txt.node,
|
||||
'text',
|
||||
str(
|
||||
player.team.sessionteam.customdata[
|
||||
'previous_score'
|
||||
]
|
||||
+ j
|
||||
+ 1
|
||||
),
|
||||
),
|
||||
)
|
||||
tfin = tdelay + delay1 + 0.15 * j
|
||||
if tfin not in sound_times:
|
||||
sound_times.add(tfin)
|
||||
ba.timer(
|
||||
tfin,
|
||||
ba.Call(
|
||||
ba.playsound, self._score_display_sound_small
|
||||
),
|
||||
)
|
||||
v_offs -= spacing
|
||||
|
||||
def _safe_animate(
|
||||
self, node: ba.Node | None, attr: str, keys: dict[float, float]
|
||||
) -> None:
|
||||
"""Run an animation on a node if the node still exists."""
|
||||
if node:
|
||||
ba.animate(node, attr, keys)
|
||||
93
dist/ba_data/python/bastd/activity/multiteamjoin.py
vendored
Normal file
93
dist/ba_data/python/bastd/activity/multiteamjoin.py
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the join screen for multi-team sessions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from ba.internal import JoinActivity
|
||||
from bastd.actor.text import Text
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class MultiTeamJoinActivity(JoinActivity):
|
||||
"""Join screen for teams sessions."""
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
super().__init__(settings)
|
||||
self._next_up_text: Text | None = None
|
||||
|
||||
def on_transition_in(self) -> None:
|
||||
from bastd.actor.controlsguide import ControlsGuide
|
||||
from ba import DualTeamSession
|
||||
|
||||
super().on_transition_in()
|
||||
ControlsGuide(delay=1.0).autoretain()
|
||||
|
||||
session = self.session
|
||||
assert isinstance(session, ba.MultiTeamSession)
|
||||
|
||||
# Show info about the next up game.
|
||||
self._next_up_text = Text(
|
||||
ba.Lstr(
|
||||
value='${1} ${2}',
|
||||
subs=[
|
||||
('${1}', ba.Lstr(resource='upFirstText')),
|
||||
('${2}', session.get_next_game_description()),
|
||||
],
|
||||
),
|
||||
h_attach=Text.HAttach.CENTER,
|
||||
scale=0.7,
|
||||
v_attach=Text.VAttach.TOP,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
position=(0, -70),
|
||||
flash=False,
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
transition=Text.Transition.FADE_IN,
|
||||
transition_delay=5.0,
|
||||
)
|
||||
|
||||
# In teams mode, show our two team names.
|
||||
# FIXME: Lobby should handle this.
|
||||
if isinstance(ba.getsession(), DualTeamSession):
|
||||
team_names = [team.name for team in ba.getsession().sessionteams]
|
||||
team_colors = [
|
||||
tuple(team.color) + (0.5,)
|
||||
for team in ba.getsession().sessionteams
|
||||
]
|
||||
if len(team_names) == 2:
|
||||
for i in range(2):
|
||||
Text(
|
||||
team_names[i],
|
||||
scale=0.7,
|
||||
h_attach=Text.HAttach.CENTER,
|
||||
v_attach=Text.VAttach.TOP,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
position=(-200 + 350 * i, -100),
|
||||
color=team_colors[i],
|
||||
transition=Text.Transition.FADE_IN,
|
||||
).autoretain()
|
||||
|
||||
Text(
|
||||
ba.Lstr(
|
||||
resource='mustInviteFriendsText',
|
||||
subs=[
|
||||
('${GATHER}', ba.Lstr(resource='gatherWindow.titleText'))
|
||||
],
|
||||
),
|
||||
h_attach=Text.HAttach.CENTER,
|
||||
scale=0.8,
|
||||
host_only=True,
|
||||
v_attach=Text.VAttach.CENTER,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
position=(0, 0),
|
||||
flash=False,
|
||||
color=(0, 1, 0, 1.0),
|
||||
transition=Text.Transition.FADE_IN,
|
||||
transition_delay=2.0,
|
||||
transition_out_delay=7.0,
|
||||
).autoretain()
|
||||
262
dist/ba_data/python/bastd/activity/multiteamscore.py
vendored
Normal file
262
dist/ba_data/python/bastd/activity/multiteamscore.py
vendored
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to teams mode score screen."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from ba.internal import ScoreScreenActivity
|
||||
from bastd.actor.text import Text
|
||||
from bastd.actor.image import Image
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class MultiTeamScoreScreenActivity(ScoreScreenActivity):
|
||||
"""Base class for score screens."""
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
super().__init__(settings=settings)
|
||||
self._score_display_sound = ba.getsound('scoreHit01')
|
||||
self._score_display_sound_small = ba.getsound('scoreHit02')
|
||||
|
||||
self._show_up_next: bool = True
|
||||
|
||||
def on_begin(self) -> None:
|
||||
super().on_begin()
|
||||
session = self.session
|
||||
if self._show_up_next and isinstance(session, ba.MultiTeamSession):
|
||||
txt = ba.Lstr(
|
||||
value='${A} ${B}',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
ba.Lstr(
|
||||
resource='upNextText',
|
||||
subs=[
|
||||
('${COUNT}', str(session.get_game_number() + 1))
|
||||
],
|
||||
),
|
||||
),
|
||||
('${B}', session.get_next_game_description()),
|
||||
],
|
||||
)
|
||||
Text(
|
||||
txt,
|
||||
maxwidth=900,
|
||||
h_attach=Text.HAttach.CENTER,
|
||||
v_attach=Text.VAttach.BOTTOM,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
position=(0, 53),
|
||||
flash=False,
|
||||
color=(0.3, 0.3, 0.35, 1.0),
|
||||
transition=Text.Transition.FADE_IN,
|
||||
transition_delay=2.0,
|
||||
).autoretain()
|
||||
|
||||
def show_player_scores(
|
||||
self,
|
||||
delay: float = 2.5,
|
||||
results: ba.GameResults | None = None,
|
||||
scale: float = 1.0,
|
||||
x_offset: float = 0.0,
|
||||
y_offset: float = 0.0,
|
||||
) -> None:
|
||||
"""Show scores for individual players."""
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
ts_v_offset = 150.0 + y_offset
|
||||
ts_h_offs = 80.0 + x_offset
|
||||
tdelay = delay
|
||||
spacing = 40
|
||||
|
||||
is_free_for_all = isinstance(self.session, ba.FreeForAllSession)
|
||||
|
||||
def _get_prec_score(p_rec: ba.PlayerRecord) -> int | None:
|
||||
if is_free_for_all and results is not None:
|
||||
assert isinstance(results, ba.GameResults)
|
||||
assert p_rec.team.activityteam is not None
|
||||
val = results.get_sessionteam_score(p_rec.team)
|
||||
return val
|
||||
return p_rec.accumscore
|
||||
|
||||
def _get_prec_score_str(p_rec: ba.PlayerRecord) -> str | ba.Lstr:
|
||||
if is_free_for_all and results is not None:
|
||||
assert isinstance(results, ba.GameResults)
|
||||
assert p_rec.team.activityteam is not None
|
||||
val = results.get_sessionteam_score_str(p_rec.team)
|
||||
assert val is not None
|
||||
return val
|
||||
return str(p_rec.accumscore)
|
||||
|
||||
# stats.get_records() can return players that are no longer in
|
||||
# the game.. if we're using results we have to filter those out
|
||||
# (since they're not in results and that's where we pull their
|
||||
# scores from)
|
||||
if results is not None:
|
||||
assert isinstance(results, ba.GameResults)
|
||||
player_records = []
|
||||
assert self.stats
|
||||
valid_players = list(self.stats.get_records().items())
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def _get_player_score_set_entry(
|
||||
player: ba.SessionPlayer,
|
||||
) -> ba.PlayerRecord | None:
|
||||
for p_rec in valid_players:
|
||||
if p_rec[1].player is player:
|
||||
return p_rec[1]
|
||||
return None
|
||||
|
||||
# Results is already sorted; just convert it into a list of
|
||||
# score-set-entries.
|
||||
for winnergroup in results.winnergroups:
|
||||
for team in winnergroup.teams:
|
||||
if len(team.players) == 1:
|
||||
player_entry = _get_player_score_set_entry(
|
||||
team.players[0]
|
||||
)
|
||||
if player_entry is not None:
|
||||
player_records.append(player_entry)
|
||||
else:
|
||||
player_records = []
|
||||
player_records_scores = [
|
||||
(_get_prec_score(p), name, p)
|
||||
for name, p in list(self.stats.get_records().items())
|
||||
]
|
||||
player_records_scores.sort(reverse=True)
|
||||
|
||||
# Just want living player entries.
|
||||
player_records = [p[2] for p in player_records_scores if p[2]]
|
||||
|
||||
voffs = -140.0 + spacing * len(player_records) * 0.5
|
||||
|
||||
def _txt(
|
||||
xoffs: float,
|
||||
yoffs: float,
|
||||
text: ba.Lstr,
|
||||
h_align: Text.HAlign = Text.HAlign.RIGHT,
|
||||
extrascale: float = 1.0,
|
||||
maxwidth: float | None = 120.0,
|
||||
) -> None:
|
||||
Text(
|
||||
text,
|
||||
color=(0.5, 0.5, 0.6, 0.5),
|
||||
position=(
|
||||
ts_h_offs + xoffs * scale,
|
||||
ts_v_offset + (voffs + yoffs + 4.0) * scale,
|
||||
),
|
||||
h_align=h_align,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
scale=0.8 * scale * extrascale,
|
||||
maxwidth=maxwidth,
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
|
||||
session = self.session
|
||||
assert isinstance(session, ba.MultiTeamSession)
|
||||
tval = ba.Lstr(
|
||||
resource='gameLeadersText',
|
||||
subs=[('${COUNT}', str(session.get_game_number()))],
|
||||
)
|
||||
_txt(
|
||||
180,
|
||||
43,
|
||||
tval,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
extrascale=1.4,
|
||||
maxwidth=None,
|
||||
)
|
||||
_txt(-15, 4, ba.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
|
||||
_txt(180, 4, ba.Lstr(resource='killsText'))
|
||||
_txt(280, 4, ba.Lstr(resource='deathsText'), maxwidth=100)
|
||||
|
||||
score_label = 'Score' if results is None else results.score_label
|
||||
translated = ba.Lstr(translate=('scoreNames', score_label))
|
||||
|
||||
_txt(390, 0, translated)
|
||||
|
||||
topkillcount = 0
|
||||
topkilledcount = 99999
|
||||
top_score = (
|
||||
0 if not player_records else _get_prec_score(player_records[0])
|
||||
)
|
||||
|
||||
for prec in player_records:
|
||||
topkillcount = max(topkillcount, prec.accum_kill_count)
|
||||
topkilledcount = min(topkilledcount, prec.accum_killed_count)
|
||||
|
||||
def _scoretxt(
|
||||
text: str | ba.Lstr,
|
||||
x_offs: float,
|
||||
highlight: bool,
|
||||
delay2: float,
|
||||
maxwidth: float = 70.0,
|
||||
) -> None:
|
||||
Text(
|
||||
text,
|
||||
position=(
|
||||
ts_h_offs + x_offs * scale,
|
||||
ts_v_offset + (voffs + 15) * scale,
|
||||
),
|
||||
scale=scale,
|
||||
color=(1.0, 0.9, 0.5, 1.0)
|
||||
if highlight
|
||||
else (0.5, 0.5, 0.6, 0.5),
|
||||
h_align=Text.HAlign.RIGHT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=maxwidth,
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay + delay2,
|
||||
).autoretain()
|
||||
|
||||
for playerrec in player_records:
|
||||
tdelay += 0.05
|
||||
voffs -= spacing
|
||||
Image(
|
||||
playerrec.get_icon(),
|
||||
position=(
|
||||
ts_h_offs - 12 * scale,
|
||||
ts_v_offset + (voffs + 15.0) * scale,
|
||||
),
|
||||
scale=(30.0 * scale, 30.0 * scale),
|
||||
transition=Image.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
Text(
|
||||
ba.Lstr(value=playerrec.getname(full=True)),
|
||||
maxwidth=160,
|
||||
scale=0.75 * scale,
|
||||
position=(
|
||||
ts_h_offs + 10.0 * scale,
|
||||
ts_v_offset + (voffs + 15) * scale,
|
||||
),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
color=ba.safecolor(playerrec.team.color + (1,)),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
_scoretxt(
|
||||
str(playerrec.accum_kill_count),
|
||||
180,
|
||||
playerrec.accum_kill_count == topkillcount,
|
||||
0.1,
|
||||
)
|
||||
_scoretxt(
|
||||
str(playerrec.accum_killed_count),
|
||||
280,
|
||||
playerrec.accum_killed_count == topkilledcount,
|
||||
0.1,
|
||||
)
|
||||
_scoretxt(
|
||||
_get_prec_score_str(playerrec),
|
||||
390,
|
||||
_get_prec_score(playerrec) == top_score,
|
||||
0.2,
|
||||
)
|
||||
481
dist/ba_data/python/bastd/activity/multiteamvictory.py
vendored
Normal file
481
dist/ba_data/python/bastd/activity/multiteamvictory.py
vendored
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the final screen in multi-teams sessions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
||||
"""Final score screen for a team series."""
|
||||
|
||||
# Dont' play music by default; (we do manually after a delay).
|
||||
default_music = None
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
super().__init__(settings=settings)
|
||||
self._min_view_time = 15.0
|
||||
self._is_ffa = isinstance(self.session, ba.FreeForAllSession)
|
||||
self._allow_server_transition = True
|
||||
self._tips_text = None
|
||||
self._default_show_tips = False
|
||||
|
||||
def on_begin(self) -> None:
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
from bastd.actor.text import Text
|
||||
from bastd.actor.image import Image
|
||||
|
||||
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:
|
||||
sval = ba.Lstr(resource='pressAnyKeyButtonPlayAgainText')
|
||||
else:
|
||||
sval = ba.Lstr(resource='pressAnyButtonPlayAgainText')
|
||||
self._show_up_next = False
|
||||
self._custom_continue_message = sval
|
||||
super().on_begin()
|
||||
winning_sessionteam = self.settings_raw['winner']
|
||||
|
||||
# Pause a moment before playing victory music.
|
||||
ba.timer(0.6, ba.WeakCall(self._play_victory_music))
|
||||
ba.timer(
|
||||
4.4, ba.WeakCall(self._show_winner, self.settings_raw['winner'])
|
||||
)
|
||||
ba.timer(4.6, ba.Call(ba.playsound, self._score_display_sound))
|
||||
|
||||
# Score / Name / Player-record.
|
||||
player_entries: list[tuple[int, str, ba.PlayerRecord]] = []
|
||||
|
||||
# Note: for ffa, exclude players who haven't entered the game yet.
|
||||
if self._is_ffa:
|
||||
for _pkey, prec in self.stats.get_records().items():
|
||||
if prec.player.in_game:
|
||||
player_entries.append(
|
||||
(
|
||||
prec.player.sessionteam.customdata['score'],
|
||||
prec.getname(full=True),
|
||||
prec,
|
||||
)
|
||||
)
|
||||
player_entries.sort(reverse=True, key=lambda x: x[0])
|
||||
else:
|
||||
for _pkey, prec in self.stats.get_records().items():
|
||||
player_entries.append((prec.score, prec.name_full, prec))
|
||||
player_entries.sort(reverse=True, key=lambda x: x[0])
|
||||
|
||||
ts_height = 300.0
|
||||
ts_h_offs = -390.0
|
||||
tval = 6.4
|
||||
t_incr = 0.12
|
||||
|
||||
always_use_first_to = ba.app.lang.get_resource(
|
||||
'bestOfUseFirstToInstead'
|
||||
)
|
||||
|
||||
session = self.session
|
||||
if self._is_ffa:
|
||||
assert isinstance(session, ba.FreeForAllSession)
|
||||
txt = ba.Lstr(
|
||||
value='${A}:',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
ba.Lstr(
|
||||
resource='firstToFinalText',
|
||||
subs=[
|
||||
(
|
||||
'${COUNT}',
|
||||
str(session.get_ffa_series_length()),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
else:
|
||||
assert isinstance(session, ba.MultiTeamSession)
|
||||
|
||||
# Some languages may prefer to always show 'first to X' instead of
|
||||
# 'best of X'.
|
||||
# FIXME: This will affect all clients connected to us even if
|
||||
# they're not using this language. Should try to come up
|
||||
# with a wording that works everywhere.
|
||||
if always_use_first_to:
|
||||
txt = ba.Lstr(
|
||||
value='${A}:',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
ba.Lstr(
|
||||
resource='firstToFinalText',
|
||||
subs=[
|
||||
(
|
||||
'${COUNT}',
|
||||
str(
|
||||
session.get_series_length() / 2 + 1
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
else:
|
||||
txt = ba.Lstr(
|
||||
value='${A}:',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
ba.Lstr(
|
||||
resource='bestOfFinalText',
|
||||
subs=[
|
||||
(
|
||||
'${COUNT}',
|
||||
str(session.get_series_length()),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
Text(
|
||||
txt,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=300,
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
position=(0, 220),
|
||||
scale=1.2,
|
||||
transition=Text.Transition.IN_TOP_SLOW,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
transition_delay=t_incr * 4,
|
||||
).autoretain()
|
||||
|
||||
win_score = (session.get_series_length() - 1) // 2 + 1
|
||||
lose_score = 0
|
||||
for team in self.teams:
|
||||
if team.sessionteam.customdata['score'] != win_score:
|
||||
lose_score = team.sessionteam.customdata['score']
|
||||
|
||||
if not self._is_ffa:
|
||||
Text(
|
||||
ba.Lstr(
|
||||
resource='gamesToText',
|
||||
subs=[
|
||||
('${WINCOUNT}', str(win_score)),
|
||||
('${LOSECOUNT}', str(lose_score)),
|
||||
],
|
||||
),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
maxwidth=160,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
position=(0, -215),
|
||||
scale=1.8,
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
transition_delay=4.8 + t_incr * 4,
|
||||
).autoretain()
|
||||
|
||||
if self._is_ffa:
|
||||
v_extra = 120
|
||||
else:
|
||||
v_extra = 0
|
||||
|
||||
mvp: ba.PlayerRecord | None = None
|
||||
mvp_name: str | None = None
|
||||
|
||||
# Show game MVP.
|
||||
if not self._is_ffa:
|
||||
mvp, mvp_name = None, None
|
||||
for entry in player_entries:
|
||||
if entry[2].team == winning_sessionteam:
|
||||
mvp = entry[2]
|
||||
mvp_name = entry[1]
|
||||
break
|
||||
if mvp is not None:
|
||||
Text(
|
||||
ba.Lstr(resource='mostValuablePlayerText'),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=300,
|
||||
position=(180, ts_height / 2 + 15),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
h_align=Text.HAlign.LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
tval += 4 * t_incr
|
||||
|
||||
Image(
|
||||
mvp.get_icon(),
|
||||
position=(230, ts_height / 2 - 55 + 14 - 5),
|
||||
scale=(70, 70),
|
||||
transition=Image.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
assert mvp_name is not None
|
||||
Text(
|
||||
ba.Lstr(value=mvp_name),
|
||||
position=(280, ts_height / 2 - 55 + 15 - 5),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=170,
|
||||
scale=1.3,
|
||||
color=ba.safecolor(mvp.team.color + (1,)),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
tval += 4 * t_incr
|
||||
|
||||
# Most violent.
|
||||
most_kills = 0
|
||||
for entry in player_entries:
|
||||
if entry[2].kill_count >= most_kills:
|
||||
mvp = entry[2]
|
||||
mvp_name = entry[1]
|
||||
most_kills = entry[2].kill_count
|
||||
if mvp is not None:
|
||||
Text(
|
||||
ba.Lstr(resource='mostViolentPlayerText'),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=300,
|
||||
position=(180, ts_height / 2 - 150 + v_extra + 15),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
h_align=Text.HAlign.LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
Text(
|
||||
ba.Lstr(
|
||||
value='(${A})',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
ba.Lstr(
|
||||
resource='killsTallyText',
|
||||
subs=[('${COUNT}', str(most_kills))],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
position=(260, ts_height / 2 - 150 - 15 + v_extra),
|
||||
color=(0.3, 0.3, 0.3, 1.0),
|
||||
scale=0.6,
|
||||
h_align=Text.HAlign.LEFT,
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
tval += 4 * t_incr
|
||||
|
||||
Image(
|
||||
mvp.get_icon(),
|
||||
position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra),
|
||||
scale=(50, 50),
|
||||
transition=Image.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
assert mvp_name is not None
|
||||
Text(
|
||||
ba.Lstr(value=mvp_name),
|
||||
position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=180,
|
||||
color=ba.safecolor(mvp.team.color + (1,)),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
tval += 4 * t_incr
|
||||
|
||||
# Most killed.
|
||||
most_killed = 0
|
||||
mkp, mkp_name = None, None
|
||||
for entry in player_entries:
|
||||
if entry[2].killed_count >= most_killed:
|
||||
mkp = entry[2]
|
||||
mkp_name = entry[1]
|
||||
most_killed = entry[2].killed_count
|
||||
if mkp is not None:
|
||||
Text(
|
||||
ba.Lstr(resource='mostViolatedPlayerText'),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=300,
|
||||
position=(180, ts_height / 2 - 300 + v_extra + 15),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
h_align=Text.HAlign.LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
Text(
|
||||
ba.Lstr(
|
||||
value='(${A})',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
ba.Lstr(
|
||||
resource='deathsTallyText',
|
||||
subs=[('${COUNT}', str(most_killed))],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
position=(260, ts_height / 2 - 300 - 15 + v_extra),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
scale=0.6,
|
||||
color=(0.3, 0.3, 0.3, 1.0),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
tval += 4 * t_incr
|
||||
Image(
|
||||
mkp.get_icon(),
|
||||
position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra),
|
||||
scale=(50, 50),
|
||||
transition=Image.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
assert mkp_name is not None
|
||||
Text(
|
||||
ba.Lstr(value=mkp_name),
|
||||
position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
color=ba.safecolor(mkp.team.color + (1,)),
|
||||
maxwidth=180,
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
transition_delay=tval,
|
||||
).autoretain()
|
||||
tval += 4 * t_incr
|
||||
|
||||
# Now show individual scores.
|
||||
tdelay = tval
|
||||
Text(
|
||||
ba.Lstr(resource='finalScoresText'),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
position=(ts_h_offs, ts_height / 2),
|
||||
transition=Text.Transition.IN_RIGHT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
tdelay += 4 * t_incr
|
||||
|
||||
v_offs = 0.0
|
||||
tdelay += len(player_entries) * 8 * t_incr
|
||||
for _score, name, prec in player_entries:
|
||||
tdelay -= 4 * t_incr
|
||||
v_offs -= 40
|
||||
Text(
|
||||
str(prec.team.customdata['score'])
|
||||
if self._is_ffa
|
||||
else str(prec.score),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
position=(ts_h_offs + 230, ts_height / 2 + v_offs),
|
||||
h_align=Text.HAlign.RIGHT,
|
||||
transition=Text.Transition.IN_RIGHT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
tdelay -= 4 * t_incr
|
||||
|
||||
Image(
|
||||
prec.get_icon(),
|
||||
position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15),
|
||||
scale=(30, 30),
|
||||
transition=Image.Transition.IN_LEFT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
Text(
|
||||
ba.Lstr(value=name),
|
||||
position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
|
||||
h_align=Text.HAlign.LEFT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=180,
|
||||
color=ba.safecolor(prec.team.color + (1,)),
|
||||
transition=Text.Transition.IN_RIGHT,
|
||||
transition_delay=tdelay,
|
||||
).autoretain()
|
||||
|
||||
ba.timer(15.0, ba.WeakCall(self._show_tips))
|
||||
|
||||
def _show_tips(self) -> None:
|
||||
from bastd.actor.tipstext import TipsText
|
||||
|
||||
self._tips_text = TipsText(offs_y=70)
|
||||
|
||||
def _play_victory_music(self) -> None:
|
||||
|
||||
# Make sure we don't stomp on the next activity's music choice.
|
||||
if not self.is_transitioning_out():
|
||||
ba.setmusic(ba.MusicType.VICTORY)
|
||||
|
||||
def _show_winner(self, team: ba.SessionTeam) -> None:
|
||||
from bastd.actor.image import Image
|
||||
from bastd.actor.zoomtext import ZoomText
|
||||
|
||||
if not self._is_ffa:
|
||||
offs_v = 0.0
|
||||
ZoomText(
|
||||
team.name,
|
||||
position=(0, 97),
|
||||
color=team.color,
|
||||
scale=1.15,
|
||||
jitter=1.0,
|
||||
maxwidth=250,
|
||||
).autoretain()
|
||||
else:
|
||||
offs_v = -80.0
|
||||
if len(team.players) == 1:
|
||||
i = Image(
|
||||
team.players[0].get_icon(),
|
||||
position=(0, 143),
|
||||
scale=(100, 100),
|
||||
).autoretain()
|
||||
assert i.node
|
||||
ba.animate(i.node, 'opacity', {0.0: 0.0, 0.25: 1.0})
|
||||
ZoomText(
|
||||
ba.Lstr(
|
||||
value=team.players[0].getname(full=True, icon=False)
|
||||
),
|
||||
position=(0, 97 + offs_v),
|
||||
color=team.color,
|
||||
scale=1.15,
|
||||
jitter=1.0,
|
||||
maxwidth=250,
|
||||
).autoretain()
|
||||
|
||||
s_extra = 1.0 if self._is_ffa else 1.0
|
||||
|
||||
# Some languages say "FOO WINS" differently for teams vs players.
|
||||
if isinstance(self.session, ba.FreeForAllSession):
|
||||
wins_resource = 'seriesWinLine1PlayerText'
|
||||
else:
|
||||
wins_resource = 'seriesWinLine1TeamText'
|
||||
wins_text = ba.Lstr(resource=wins_resource)
|
||||
|
||||
# Temp - if these come up as the english default, fall-back to the
|
||||
# unified old form which is more likely to be translated.
|
||||
ZoomText(
|
||||
wins_text,
|
||||
position=(0, -10 + offs_v),
|
||||
color=team.color,
|
||||
scale=0.65 * s_extra,
|
||||
jitter=1.0,
|
||||
maxwidth=250,
|
||||
).autoretain()
|
||||
ZoomText(
|
||||
ba.Lstr(resource='seriesWinLine2Text'),
|
||||
position=(0, -110 + offs_v),
|
||||
scale=1.0 * s_extra,
|
||||
color=team.color,
|
||||
jitter=1.0,
|
||||
maxwidth=250,
|
||||
).autoretain()
|
||||
Loading…
Add table
Add a link
Reference in a new issue