Merge pull request #288 from imayushsaini/main

2 new mini games , file_sahre plugin fix
This commit is contained in:
Loup 2024-06-12 18:10:11 +05:30 committed by GitHub
commit efaf5bbf21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1204 additions and 14 deletions

View file

@ -564,6 +564,44 @@
} }
} }
}, },
"canon_fight": {
"description": "Blow up your enemy with powerfull cannon",
"external_url": "https://www.youtube.com/watch?v=7cv3ZSZeTns",
"authors": [
{
"name": "Mr.Smoothy",
"email": "",
"discord": "mr.smoothy"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "505c948",
"released_on": "09-06-2024",
"md5sum": "44adbe3bd6ec4988d65ff4ee79b48d95"
}
}
},
"laser_tracer": {
"description": "Dont touch dangerous laser light",
"external_url": "https://youtu.be/wTgwZKiykQw?si=Cr0ybDYAcKCUNFN4",
"authors": [
{
"name": "Mr.Smoothy",
"email": "",
"discord": "mr.smoothy"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "505c948",
"released_on": "09-06-2024",
"md5sum": "08a6457ebd271a7f7860a506b731d272"
}
}
},
"arms_race": { "arms_race": {
"description": "Upgrade your weapons by eliminating enemies. Win by being first one to kill while cursed", "description": "Upgrade your weapons by eliminating enemies. Win by being first one to kill while cursed",
"external_url": "", "external_url": "",

View file

@ -0,0 +1,447 @@
# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
# Released under the MIT License. See LICENSE for details.
# Created by Mr.Smoothy -
# https://discord.gg/ucyaesh
# https://bombsquad-community.web.app/home for more mods.
#
"""DeathMatch game and support classes."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs
import random
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.bomb import BombFactory
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.game.deathmatch import DeathMatchGame, Player, Team
if TYPE_CHECKING:
from typing import Any, Union, Sequence, Optional
# ba_meta export bascenev1.GameActivity
class CanonFightGame(DeathMatchGame):
"""A game type based on acquiring kills."""
name = 'Canon Fight'
description = 'Kill a set number of enemies to win.'
# Print messages when players die since it matters here.
announce_player_deaths = True
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]) -> list[babase.Setting]:
settings = [
bs.IntSetting(
'Kills to Win Per Player',
min_value=1,
default=5,
increment=1,
),
bs.IntChoiceSetting(
'Time Limit',
choices=[
('None', 0),
('1 Minute', 60),
('2 Minutes', 120),
('5 Minutes', 300),
('10 Minutes', 600),
('20 Minutes', 1200),
],
default=0,
),
bs.FloatChoiceSetting(
'Respawn Times',
choices=[
('Shorter', 0.25),
('Short', 0.5),
('Normal', 1.0),
('Long', 2.0),
('Longer', 4.0),
],
default=1.0,
),
bs.BoolSetting('Epic Mode', default=False),
]
# In teams mode, a suicide gives a point to the other team, but in
# free-for-all it subtracts from your own score. By default we clamp
# this at zero to benefit new players, but pro players might like to
# be able to go negative. (to avoid a strategy of just
# suiciding until you get a good drop)
if issubclass(sessiontype, bs.FreeForAllSession):
settings.append(
bs.BoolSetting('Allow Negative Scores', default=False))
return settings
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (issubclass(sessiontype, bs.DualTeamSession)
or issubclass(sessiontype, bs.FreeForAllSession))
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ["Step Right Up"]
def __init__(self, settings: dict):
super().__init__(settings)
self._scoreboard = Scoreboard()
self._score_to_win: Optional[int] = None
self._dingsound = bs.getsound('dingSmall')
self._epic_mode = bool(settings['Epic Mode'])
self._kills_to_win_per_player = int(
settings['Kills to Win Per Player'])
self._time_limit = float(settings['Time Limit'])
self._allow_negative_scores = bool(
settings.get('Allow Negative Scores', False))
# Base class overrides.
self.slow_motion = self._epic_mode
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
bs.MusicType.TO_THE_DEATH)
self.wtindex = 0
self.wttimer = bs.timer(5, babase.Call(self.wt_), repeat=True)
self.wthighlights = ["Created by Mr.Smoothy",
"hey smoothy youtube", "smoothy#multiverse"]
def wt_(self):
node = bs.newnode('text',
attrs={
'text': self.wthighlights[self.wtindex],
'flatness': 1.0,
'h_align': 'center',
'v_attach': 'bottom',
'scale': 0.7,
'position': (0, 20),
'color': (0.5, 0.5, 0.5)
})
self.delt = bs.timer(4, node.delete)
self.wtindex = int((self.wtindex+1) % len(self.wthighlights))
def get_instance_description(self) -> Union[str, Sequence]:
return 'Crush ${ARG1} of your enemies.', self._score_to_win
def get_instance_description_short(self) -> Union[str, Sequence]:
return 'kill ${ARG1} enemies', self._score_to_win
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
# Base kills needed to win on the size of the largest team.
self._score_to_win = (self._kills_to_win_per_player *
max(1, max(len(t.players) for t in self.teams)))
self._update_scoreboard()
self.create_canon_A()
self.create_canon_B()
self.create_wall()
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
super().handlemessage(msg)
player = msg.getplayer(Player)
self.respawn_player(player)
killer = msg.getkillerplayer(Player)
if killer is None:
return None
# Handle team-kills.
if killer.team is player.team:
# In free-for-all, killing yourself loses you a point.
if isinstance(self.session, bs.FreeForAllSession):
new_score = player.team.score - 1
if not self._allow_negative_scores:
new_score = max(0, new_score)
player.team.score = new_score
# In teams-mode it gives a point to the other team.
else:
self._dingsound.play()
for team in self.teams:
if team is not killer.team:
team.score += 1
# Killing someone on another team nets a kill.
else:
killer.team.score += 1
self._dingsound.play()
# In FFA show scores since its hard to find on the scoreboard.
if isinstance(killer.actor, PlayerSpaz) and killer.actor:
killer.actor.set_score_text(str(killer.team.score) + '/' +
str(self._score_to_win),
color=killer.team.color,
flash=True)
self._update_scoreboard()
# If someone has won, set a timer to end shortly.
# (allows the dust to clear and draws to occur if deaths are
# close enough)
assert self._score_to_win is not None
if any(team.score >= self._score_to_win for team in self.teams):
bs.timer(0.5, self.end_game)
else:
return super().handlemessage(msg)
return None
def _update_scoreboard(self) -> None:
for team in self.teams:
self._scoreboard.set_team_value(team, team.score,
self._score_to_win)
def end_game(self) -> None:
results = bs.GameResults()
self.delete_text_nodes()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)
def delete_text_nodes(self):
self.canon.delete()
self.canon_.delete()
self.canon2.delete()
self.canon_2.delete()
self.curve.delete()
self.curve2.delete()
def _handle_canon_load_A(self):
try:
bomb = bs.getcollision().opposingnode.getdelegate(Bomb, True)
# pos=bomb.position
owner = bomb.owner
type = bomb.bomb_type
source_player = bomb.get_source_player(bs.Player)
bs.getcollision().opposingnode.delete()
# bomb.delete()
self.launch_bomb_byA(owner, type, source_player, 2)
except bs.NotFoundError:
# This can happen if the flag stops touching us due to being
# deleted; that's ok.
return
def _handle_canon_load_B(self):
try:
bomb = bs.getcollision().opposingnode.getdelegate(Bomb, True)
# pos=bomb.position
owner = bomb.owner
type = bomb.bomb_type
source_player = bomb.get_source_player(bs.Player)
bs.getcollision().opposingnode.delete()
# bomb.delete()
self.launch_bomb_byB(owner, type, source_player, 2)
except bs.NotFoundError:
# This can happen if the flag stops touching us due to being
# deleted; that's ok.
return
def launch_bomb_byA(self, owner, type, source_player, count):
if count > 0:
y = random.randrange(2, 9, 2)
z = random.randrange(-4, 6)
self.fake_explosion(
(-5.708631629943848, 7.437141418457031, -4.525400638580322))
Bomb(position=(-6, 7.5, -4), bomb_type=type, owner=owner,
source_player=source_player, velocity=(19, y, z)).autoretain()
bs.timer(0.6, babase.Call(self.launch_bomb_byA,
owner, type, source_player, count-1))
else:
return
def launch_bomb_byB(self, owner, type, source_player, count):
if count > 0:
y = random.randrange(2, 9, 2)
z = random.randrange(-4, 6)
self.fake_explosion(
(5.708631629943848, 7.437141418457031, -4.525400638580322))
Bomb(position=(6, 7.5, -4), bomb_type=type, owner=owner,
source_player=source_player, velocity=(-19, y, z)).autoretain()
bs.timer(0.6, babase.Call(self.launch_bomb_byB,
owner, type, source_player, count-1))
else:
return
def fake_explosion(self, position: Sequence[float]):
explosion = bs.newnode('explosion',
attrs={'position': position,
'radius': 1, 'big': False})
bs.timer(0.4, explosion.delete)
sounds = ['explosion0'+str(n) for n in range(1, 6)]
sound = random.choice(sounds)
bs.getsound(sound).play()
def create_canon_A(self):
shared = SharedObjects.get()
canon_load_mat = bs.Material()
factory = BombFactory.get()
canon_load_mat.add_actions(
actions=(
('modify_part_collision', 'collide', False),
('modify_part_collision', 'physical', False)
))
canon_load_mat.add_actions(
conditions=('they_have_material', factory.bomb_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', True),
('call', 'at_connect', babase.Call(self._handle_canon_load_A))
),
)
self.ud_1_r = bs.newnode('region', attrs={'position': (-8.908631629943848, 7.337141418457031, -
4.525400638580322), 'scale': (2, 1, 1), 'type': 'box', 'materials': [canon_load_mat]})
self.node = bs.newnode('shield',
delegate=self,
attrs={
'position': (-8.308631629943848, 7.337141418457031, -4.525400638580322),
'color': (0.3, 0.2, 2.8),
'radius': 1.3
})
self.canon = bs.newnode('text',
attrs={
'text': '___________',
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (0.3, 0.3, 0.8),
'scale': 0.019,
'h_align': 'left',
'position': (-8.388631629943848, 7.837141418457031, -4.525400638580322)
})
self.canon_ = bs.newnode('text',
attrs={
'text': '_________',
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (0.3, 0.3, 0.8),
'scale': 0.019,
'h_align': 'left',
'position': (-7.888631629943848, 7.237141418457031, -4.525400638580322)
})
self.curve = bs.newnode('text',
attrs={
'text': '/\n',
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (0.3, 0.3, 0.8),
'scale': 0.019,
'h_align': 'left',
'position': (-8.788631629943848, 7.237141418457031, -4.525400638580322)
})
def create_canon_B(self):
shared = SharedObjects.get()
canon_load_mat = bs.Material()
factory = BombFactory.get()
canon_load_mat.add_actions(
actions=(
('modify_part_collision', 'collide', False),
('modify_part_collision', 'physical', False)
))
canon_load_mat.add_actions(
conditions=('they_have_material', factory.bomb_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', True),
('call', 'at_connect', babase.Call(self._handle_canon_load_B))
),
)
self.ud_1_r2 = bs.newnode('region', attrs={'position': (
8.908631629943848+0.81, 7.327141418457031, -4.525400638580322), 'scale': (2, 1, 1), 'type': 'box', 'materials': [canon_load_mat]})
self.node2 = bs.newnode('shield',
delegate=self,
attrs={
'position': (8.308631629943848+0.81, 7.327141418457031, -4.525400638580322),
'color': (2.3, 0.2, 0.3),
'radius': 1.3
})
self.canon2 = bs.newnode('text',
attrs={
'text': '___________',
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (0.8, 0.3, 0.3),
'scale': 0.019,
'h_align': 'right',
'position': (8.388631629943848+0.81, 7.837141418457031, -4.525400638580322)
})
self.canon_2 = bs.newnode('text',
attrs={
'text': '_________',
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (0.8, 0.3, 0.3),
'scale': 0.019,
'h_align': 'right',
'position': (7.888631629943848+0.81, 7.237141418457031, -4.525400638580322)
})
self.curve2 = bs.newnode('text',
attrs={
'text': '\\',
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (0.8, 0.3, 0.3),
'scale': 0.019,
'h_align': 'right',
'position': (8.788631629943848+0.81, 7.237141418457031, -4.525400638580322)
})
def create_wall(self):
shared = SharedObjects.get()
factory = BombFactory.get()
mat = bs.Material()
mat.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', True)
))
mat.add_actions(
conditions=(
('they_have_material', factory.bomb_material)),
actions=(
('modify_part_collision', 'collide', False)
))
self.wall = bs.newnode('region', attrs={'position': (
0.61877517104148865, 4.312626838684082, -8.68477725982666), 'scale': (3, 7, 27), 'type': 'box', 'materials': [mat]})

View file

@ -0,0 +1,689 @@
# Released under the MIT License. See LICENSE for details.
# https://youtu.be/wTgwZKiykQw?si=Cr0ybDYAcKCUNFN4
# https://discord.gg/ucyaesh
# https://bombsquad-community.web.app/home
# by: Mr.Smoothy
"""Elimination mini-game."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Optional, Union
import random
class Icon(bs.Actor):
"""Creates in in-game icon on screen."""
def __init__(self,
player: Player,
position: tuple[float, float],
scale: float,
show_lives: bool = True,
show_death: bool = True,
name_scale: float = 1.0,
name_maxwidth: float = 115.0,
flatness: float = 1.0,
shadow: float = 1.0):
super().__init__()
self._player = player
self._show_lives = show_lives
self._show_death = show_death
self._name_scale = name_scale
self._outline_tex = bs.gettexture('characterIconMask')
icon = player.get_icon()
self.node = bs.newnode('image',
delegate=self,
attrs={
'texture': icon['texture'],
'tint_texture': icon['tint_texture'],
'tint_color': icon['tint_color'],
'vr_depth': 400,
'tint2_color': icon['tint2_color'],
'mask_texture': self._outline_tex,
'opacity': 1.0,
'absolute_scale': True,
'attach': 'bottomCenter'
})
self._name_text = bs.newnode(
'text',
owner=self.node,
attrs={
'text': babase.Lstr(value=player.getname()),
'color': babase.safecolor(player.team.color),
'h_align': 'center',
'v_align': 'center',
'vr_depth': 410,
'maxwidth': name_maxwidth,
'shadow': shadow,
'flatness': flatness,
'h_attach': 'center',
'v_attach': 'bottom'
})
if self._show_lives:
self._lives_text = bs.newnode('text',
owner=self.node,
attrs={
'text': 'x0',
'color': (1, 1, 0.5),
'h_align': 'left',
'vr_depth': 430,
'shadow': 1.0,
'flatness': 1.0,
'h_attach': 'center',
'v_attach': 'bottom'
})
self.set_position_and_scale(position, scale)
def set_position_and_scale(self, position: tuple[float, float],
scale: float) -> None:
"""(Re)position the icon."""
assert self.node
self.node.position = position
self.node.scale = [70.0 * scale]
self._name_text.position = (position[0], position[1] + scale * 52.0)
self._name_text.scale = 1.0 * scale * self._name_scale
if self._show_lives:
self._lives_text.position = (position[0] + scale * 10.0,
position[1] - scale * 43.0)
self._lives_text.scale = 1.0 * scale
def update_for_lives(self) -> None:
"""Update for the target player's current lives."""
if self._player:
lives = self._player.lives
else:
lives = 0
if self._show_lives:
if lives > 0:
self._lives_text.text = 'x' + str(lives - 1)
else:
self._lives_text.text = ''
if lives == 0:
self._name_text.opacity = 0.2
assert self.node
self.node.color = (0.7, 0.3, 0.3)
self.node.opacity = 0.2
def handle_player_spawned(self) -> None:
"""Our player spawned; hooray!"""
if not self.node:
return
self.node.opacity = 1.0
self.update_for_lives()
def handle_player_died(self) -> None:
"""Well poo; our player died."""
if not self.node:
return
if self._show_death:
bs.animate(
self.node, 'opacity', {
0.00: 1.0,
0.05: 0.0,
0.10: 1.0,
0.15: 0.0,
0.20: 1.0,
0.25: 0.0,
0.30: 1.0,
0.35: 0.0,
0.40: 1.0,
0.45: 0.0,
0.50: 1.0,
0.55: 0.2
})
lives = self._player.lives
if lives == 0:
bs.timer(0.6, self.update_for_lives)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
self.node.delete()
return None
return super().handlemessage(msg)
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self) -> None:
self.lives = 0
self.icons: list[Icon] = []
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
self.survival_seconds: Optional[int] = None
self.spawn_order: list[Player] = []
# ba_meta export bascenev1.GameActivity
class LasorTracerGame(bs.TeamGameActivity[Player, Team]):
"""Game type where last player(s) left alive win."""
name = 'Laser Tracer'
description = 'Last remaining alive wins.'
scoreconfig = bs.ScoreConfig(label='Survived',
scoretype=bs.ScoreType.SECONDS,
none_is_winner=True)
# Show messages when players die since it's meaningful here.
announce_player_deaths = True
allow_mid_activity_joins = False
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]) -> list[babase.Setting]:
settings = [
bs.IntSetting(
'Lives Per Player',
default=1,
min_value=1,
max_value=10,
increment=1,
),
bs.IntChoiceSetting(
'Time Limit',
choices=[
('None', 0),
('1 Minute', 60),
('2 Minutes', 120),
('5 Minutes', 300),
('10 Minutes', 600),
('20 Minutes', 1200),
],
default=0,
),
bs.FloatChoiceSetting(
'Respawn Times',
choices=[
('Shorter', 0.25),
('Short', 0.5),
('Normal', 1.0),
('Long', 2.0),
('Longer', 4.0),
],
default=1.0,
),
bs.BoolSetting('Epic Mode', default=False),
]
if issubclass(sessiontype, bs.DualTeamSession):
settings.append(bs.BoolSetting('Solo Mode', default=False))
settings.append(
bs.BoolSetting('Balance Total Lives', default=False))
return settings
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (issubclass(sessiontype, bs.DualTeamSession)
or issubclass(sessiontype, bs.FreeForAllSession))
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ["Courtyard"]
def __init__(self, settings: dict):
super().__init__(settings)
shared = SharedObjects.get()
self._scoreboard = Scoreboard()
self._start_time: Optional[float] = None
self._vs_text: Optional[bs.Actor] = None
self._round_end_timer: Optional[bs.Timer] = None
self._epic_mode = bool(settings['Epic Mode'])
self._lives_per_player = 1
self._time_limit = float(settings['Time Limit'])
self._balance_total_lives = bool(
settings.get('Balance Total Lives', False))
self._solo_mode = bool(settings.get('Solo Mode', False))
# Base class overrides:
self.slow_motion = self._epic_mode
self.default_music = (bs.MusicType.EPIC
if self._epic_mode else bs.MusicType.SURVIVAL)
self.laser_material = bs.Material()
self.laser_material.add_actions(
conditions=('they_have_material',
shared.player_material),
actions=(('modify_part_collision', 'collide', True),
('message', 'their_node', 'at_connect', bs.DieMessage()))
)
def get_instance_description(self) -> Union[str, Sequence]:
return 'Last team standing wins.' if isinstance(
self.session, bs.DualTeamSession) else 'Last one standing wins.'
def get_instance_description_short(self) -> Union[str, Sequence]:
return 'last team standing wins' if isinstance(
self.session, bs.DualTeamSession) else 'last one standing wins'
def on_player_join(self, player: Player) -> None:
player.lives = self._lives_per_player
if self._solo_mode:
player.team.spawn_order.append(player)
self._update_solo_mode()
else:
# Create our icon and spawn.
# player.icons = [Icon(player, position=(0, 50), scale=0.8)]
if player.lives > 0:
self.spawn_player(player)
# Don't waste time doing this until begin.
if self.has_begun():
self._update_icons()
def on_begin(self) -> None:
super().on_begin()
self._start_time = bs.time()
self.setup_standard_time_limit(self._time_limit)
# self.setup_standard_powerup_drops()
self.add_wall()
self.create_laser()
if self._solo_mode:
self._vs_text = bs.NodeActor(
bs.newnode('text',
attrs={
'position': (0, 105),
'h_attach': 'center',
'h_align': 'center',
'maxwidth': 200,
'shadow': 0.5,
'vr_depth': 390,
'scale': 0.6,
'v_attach': 'bottom',
'color': (0.8, 0.8, 0.3, 1.0),
'text': babase.Lstr(resource='vsText')
}))
# If balance-team-lives is on, add lives to the smaller team until
# total lives match.
if (isinstance(self.session, bs.DualTeamSession)
and self._balance_total_lives and self.teams[0].players
and self.teams[1].players):
if self._get_total_team_lives(
self.teams[0]) < self._get_total_team_lives(self.teams[1]):
lesser_team = self.teams[0]
greater_team = self.teams[1]
else:
lesser_team = self.teams[1]
greater_team = self.teams[0]
add_index = 0
while (self._get_total_team_lives(lesser_team) <
self._get_total_team_lives(greater_team)):
lesser_team.players[add_index].lives += 1
add_index = (add_index + 1) % len(lesser_team.players)
self._update_icons()
# We could check game-over conditions at explicit trigger points,
# but lets just do the simple thing and poll it.
bs.timer(1.0, self._update, repeat=True)
def _update_solo_mode(self) -> None:
# For both teams, find the first player on the spawn order list with
# lives remaining and spawn them if they're not alive.
for team in self.teams:
# Prune dead players from the spawn order.
team.spawn_order = [p for p in team.spawn_order if p]
for player in team.spawn_order:
assert isinstance(player, Player)
if player.lives > 0:
if not player.is_alive():
self.spawn_player(player)
break
def _update_icons(self) -> None:
return
# lets do nothing ;Eat 5 Star
# pylint: disable=too-many-branches
# In free-for-all mode, everyone is just lined up along the bottom.
if isinstance(self.session, bs.FreeForAllSession):
count = len(self.teams)
x_offs = 85
xval = x_offs * (count - 1) * -0.5
for team in self.teams:
if len(team.players) == 1:
player = team.players[0]
for icon in player.icons:
icon.set_position_and_scale((xval, 30), 0.7)
icon.update_for_lives()
xval += x_offs
# In teams mode we split up teams.
else:
if self._solo_mode:
# First off, clear out all icons.
for player in self.players:
player.icons = []
# Now for each team, cycle through our available players
# adding icons.
for team in self.teams:
if team.id == 0:
xval = -60
x_offs = -78
else:
xval = 60
x_offs = 78
is_first = True
test_lives = 1
while True:
players_with_lives = [
p for p in team.spawn_order
if p and p.lives >= test_lives
]
if not players_with_lives:
break
for player in players_with_lives:
player.icons.append(
Icon(player,
position=(xval, (40 if is_first else 25)),
scale=1.0 if is_first else 0.5,
name_maxwidth=130 if is_first else 75,
name_scale=0.8 if is_first else 1.0,
flatness=0.0 if is_first else 1.0,
shadow=0.5 if is_first else 1.0,
show_death=is_first,
show_lives=False))
xval += x_offs * (0.8 if is_first else 0.56)
is_first = False
test_lives += 1
# Non-solo mode.
else:
for team in self.teams:
if team.id == 0:
xval = -50
x_offs = -85
else:
xval = 50
x_offs = 85
for player in team.players:
for icon in player.icons:
icon.set_position_and_scale((xval, 30), 0.7)
icon.update_for_lives()
xval += x_offs
def _get_spawn_point(self, player: Player) -> Optional[babase.Vec3]:
del player # Unused.
# In solo-mode, if there's an existing live player on the map, spawn at
# whichever spot is farthest from them (keeps the action spread out).
if self._solo_mode:
living_player = None
living_player_pos = None
for team in self.teams:
for tplayer in team.players:
if tplayer.is_alive():
assert tplayer.node
ppos = tplayer.node.position
living_player = tplayer
living_player_pos = ppos
break
if living_player:
assert living_player_pos is not None
player_pos = babase.Vec3(living_player_pos)
points: list[tuple[float, babase.Vec3]] = []
for team in self.teams:
start_pos = babase.Vec3(
self.map.get_start_position(team.id))
points.append(
((start_pos - player_pos).length(), start_pos))
# Hmm.. we need to sorting vectors too?
points.sort(key=lambda x: x[0])
return points[-1][1]
return None
def spawn_player(self, player: Player) -> bs.Actor:
actor = self.spawn_player_spaz(player, self._get_spawn_point(player))
actor.connect_controls_to_player(enable_punch=False,
enable_bomb=False,
enable_pickup=False)
if not self._solo_mode:
bs.timer(0.3, babase.Call(self._print_lives, player))
# If we have any icons, update their state.
for icon in player.icons:
icon.handle_player_spawned()
return actor
def _print_lives(self, player: Player) -> None:
from bascenev1lib.actor import popuptext
# We get called in a timer so it's possible our player has left/etc.
if not player or not player.is_alive() or not player.node:
return
popuptext.PopupText('x' + str(player.lives - 1),
color=(1, 1, 0, 1),
offset=(0, -0.8, 0),
random_offset=0.0,
scale=1.8,
position=player.node.position).autoretain()
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
player.icons = []
# Remove us from spawn-order.
if self._solo_mode:
if player in player.team.spawn_order:
player.team.spawn_order.remove(player)
# Update icons in a moment since our team will be gone from the
# list then.
bs.timer(0, self._update_icons)
# If the player to leave was the last in spawn order and had
# their final turn currently in-progress, mark the survival time
# for their team.
if self._get_total_team_lives(player.team) == 0:
assert self._start_time is not None
player.team.survival_seconds = int(bs.time() - self._start_time)
def _get_total_team_lives(self, team: Team) -> int:
return sum(player.lives for player in team.players)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
super().handlemessage(msg)
player: Player = msg.getplayer(Player)
player.lives -= 1
if player.lives < 0:
babase.print_error(
"Got lives < 0 in Elim; this shouldn't happen. solo:" +
str(self._solo_mode))
player.lives = 0
# If we have any icons, update their state.
for icon in player.icons:
icon.handle_player_died()
# Play big death sound on our last death
# or for every one in solo mode.
if self._solo_mode or player.lives == 0:
SpazFactory.get().single_player_death_sound.play()
# If we hit zero lives, we're dead (and our team might be too).
if player.lives == 0:
# If the whole team is now dead, mark their survival time.
if self._get_total_team_lives(player.team) == 0:
assert self._start_time is not None
player.team.survival_seconds = int(bs.time() -
self._start_time)
else:
# Otherwise, in regular mode, respawn.
if not self._solo_mode:
self.respawn_player(player)
# In solo, put ourself at the back of the spawn order.
if self._solo_mode:
player.team.spawn_order.remove(player)
player.team.spawn_order.append(player)
def _update(self) -> None:
if self._solo_mode:
# For both teams, find the first player on the spawn order
# list with lives remaining and spawn them if they're not alive.
for team in self.teams:
# Prune dead players from the spawn order.
team.spawn_order = [p for p in team.spawn_order if p]
for player in team.spawn_order:
assert isinstance(player, Player)
if player.lives > 0:
if not player.is_alive():
self.spawn_player(player)
self._update_icons()
break
# If we're down to 1 or fewer living teams, start a timer to end
# the game (allows the dust to settle and draws to occur if deaths
# are close enough).
if len(self._get_living_teams()) < 2:
self._round_end_timer = bs.Timer(0.5, self.end_game)
def _get_living_teams(self) -> list[Team]:
return [
team for team in self.teams
if len(team.players) > 0 and any(player.lives > 0
for player in team.players)
]
def end_game(self) -> None:
if self.has_ended():
return
results = bs.GameResults()
self._vs_text = None # Kill our 'vs' if its there.
for team in self.teams:
results.set_team_score(team, team.survival_seconds)
self.end(results=results)
def add_wall(self):
# FIXME: Chop this into vr and non-vr chunks.
shared = SharedObjects.get()
pwm = bs.Material()
cwwm = bs.Material()
pwm.add_actions(
actions=('modify_part_collision', 'friction', 0.0))
# anything that needs to hit the wall should apply this.
pwm.add_actions(
conditions=('they_have_material',
shared.player_material),
actions=('modify_part_collision', 'collide', True))
cmesh = bs.getcollisionmesh('courtyardPlayerWall')
self.player_wall = bs.newnode(
'terrain',
attrs={
'collision_mesh': cmesh,
'affect_bg_dynamics': False,
'materials': [pwm]
})
def create_laser(self) -> None:
bs.timer(6, babase.Call(self.LRlaser, True))
bs.timer(7, babase.Call(self.UDlaser, True))
bs.timer(30, babase.Call(self.create_laser))
def LRlaser(self, left):
ud_1_r = bs.newnode('region', attrs={'position': (-5, 2.6, 0), 'scale': (
0.1, 0.6, 15), 'type': 'box', 'materials': [self.laser_material]})
shields = []
x = -6
for i in range(0, 30):
x = x+0.4
node = bs.newnode('shield', owner=ud_1_r, attrs={
'color': (1, 0, 0), 'radius': 0.28})
mnode = bs.newnode('math',
owner=ud_1_r,
attrs={
'input1': (0, 0.0, x),
'operation': 'add'
})
ud_1_r.connectattr('position', mnode, 'input2')
mnode.connectattr('output', node, 'position')
_rcombine = bs.newnode('combine',
owner=ud_1_r,
attrs={
'input1': 2.6,
'input2': -2,
'size': 3
})
if left:
x1 = -10
x2 = 10
else:
x1 = 10
x2 = -10
bs.animate(_rcombine, 'input0', {
0: x1,
20: x2
})
_rcombine.connectattr('output', ud_1_r, 'position')
bs.timer(20, babase.Call(ud_1_r.delete))
t = random.randrange(7, 13)
bs.timer(t, babase.Call(self.LRlaser, random.randrange(0, 2)))
def UDlaser(self, up):
ud_2_r = bs.newnode('region', attrs={'position': (-3, 2.6, -6), 'scale': (
20, 0.6, 0.1), 'type': 'box', 'materials': [self.laser_material]})
shields = []
x = -6
for i in range(0, 40):
x = x+0.4
node = bs.newnode('shield', owner=ud_2_r, attrs={
'color': (1, 0, 0), 'radius': 0.28})
mnode = bs.newnode('math',
owner=ud_2_r,
attrs={
'input1': (x, 0.0, 0),
'operation': 'add'
})
ud_2_r.connectattr('position', mnode, 'input2')
mnode.connectattr('output', node, 'position')
_rcombine = bs.newnode('combine',
owner=ud_2_r,
attrs={
'input0': -2,
'input1': 2.6,
'size': 3
})
if up:
x1 = -9
x2 = 6
else:
x1 = 6
x2 = -9
bs.animate(_rcombine, 'input2', {
0: x1,
17: x2
})
_rcombine.connectattr('output', ud_2_r, 'position')
bs.timer(17, babase.Call(ud_2_r.delete))
t = random.randrange(6, 13)
bs.timer(t, babase.Call(self.UDlaser, random.randrange(0, 2)))

View file

@ -676,6 +676,12 @@
} }
], ],
"versions": { "versions": {
"1.0.2": {
"api_version": 8,
"commit_sha": "505c948",
"released_on": "09-06-2024",
"md5sum": "ae32962255c357b29bd9c46c0551a19c"
},
"1.0.1": { "1.0.1": {
"api_version": 8, "api_version": 8,
"commit_sha": "b089293", "commit_sha": "b089293",

View file

@ -1,6 +1,6 @@
# ba_meta require api 8 # ba_meta require api 8
''' '''
File Share Mod for BombSquad 1.7.23 and above. File Share Mod for BombSquad 1.7.30 and above.
https://youtu.be/qtGsFU4cgic https://youtu.be/qtGsFU4cgic
https://discord.gg/ucyaesh https://discord.gg/ucyaesh
by : Mr.Smoothy by : Mr.Smoothy
@ -19,7 +19,7 @@ import _baplus
import _babase import _babase
import babase import babase
from bauiv1lib.fileselector import FileSelectorWindow from bauiv1lib.fileselector import FileSelectorWindow
from bauiv1lib.promocode import PromoCodeWindow from bauiv1lib.sendinfo import SendInfoWindow
from bauiv1lib.confirm import ConfirmWindow from bauiv1lib.confirm import ConfirmWindow
import bauiv1 as bui import bauiv1 as bui
import os import os
@ -55,14 +55,16 @@ class UploadConfirmation(ConfirmWindow):
origin_widget: bui.Widget | None = None, origin_widget: bui.Widget | None = None,
): ):
super().__init__(text=text, action=action, origin_widget=origin_widget, ok_text=ok_text) super().__init__(text=text, action=action,
origin_widget=origin_widget, ok_text=ok_text)
self.status = status self.status = status
self.file_path = file_path self.file_path = file_path
def _ok(self) -> None: def _ok(self) -> None:
if self.status == "init": if self.status == "init":
self._cancel() self._cancel()
UploadConfirmation("", "uploading", text="Uploading file wait !", ok_text="Wait") UploadConfirmation(
"", "uploading", text="Uploading file wait !", ok_text="Wait")
self._upload_file() self._upload_file()
elif self.status == "uploading": elif self.status == "uploading":
@ -83,10 +85,10 @@ class UploadConfirmation(ConfirmWindow):
ShowURLWindow(url) ShowURLWindow(url)
class InputWindow(PromoCodeWindow): class InputWindow(SendInfoWindow):
def __init__( def __init__(
self, modal: bool = True, origin_widget: bui.Widget | None = None, path=None): self, modal: bool = True, origin_widget: bui.Widget | None = None, path=None):
super().__init__(modal=modal, origin_widget=origin_widget) super().__init__(modal=modal, legacy_code_mode=True, origin_widget=origin_widget)
bui.textwidget(edit=self._text_field, max_chars=300) bui.textwidget(edit=self._text_field, max_chars=300)
self._path = path self._path = path
self.message_widget = bui.textwidget( self.message_widget = bui.textwidget(
@ -101,12 +103,15 @@ class InputWindow(PromoCodeWindow):
def _do_enter(self): def _do_enter(self):
url = bui.textwidget(query=self._text_field) url = bui.textwidget(query=self._text_field)
if self._path and self._path != "/bombsquad": if self._path and self._path != "/bombsquad":
bui.textwidget(edit=self.message_widget, text="downloading.... wait...") bui.textwidget(edit=self.message_widget,
text="downloading.... wait...")
bui.screenmessage("Downloading started") bui.screenmessage("Downloading started")
thread = Thread(target=handle_download, args=(url, self._path, self.on_download,)) thread = Thread(target=handle_download, args=(
url, self._path, self.on_download,))
thread.start() thread.start()
else: else:
bui.textwidget(edit=self.message_widget, text="First select folder were to save file.") bui.textwidget(edit=self.message_widget,
text="First select folder were to save file.")
self.close() self.close()
def on_download(self, output_path): def on_download(self, output_path):
@ -306,7 +311,8 @@ def handle_upload(file, callback, root_widget):
_babase.pushcall(Call(callback, json.loads(response.read().decode( _babase.pushcall(Call(callback, json.loads(response.read().decode(
'utf-8'))["link"], root_widget), from_other_thread=True) 'utf-8'))["link"], root_widget), from_other_thread=True)
else: else:
bui.screenmessage(f"Failed to Upload file. Status code: {response.getcode()}") bui.screenmessage(
f"Failed to Upload file. Status code: {response.getcode()}")
except urllib.error.URLError as e: except urllib.error.URLError as e:
bui.screenmessage(f"Error occurred: {e}") bui.screenmessage(f"Error occurred: {e}")
@ -318,22 +324,26 @@ def handle_download(url, path, callback):
if response.getcode() == 200: if response.getcode() == 200:
# Read the filename from the Content-Disposition header # Read the filename from the Content-Disposition header
filename = None filename = None
content_disposition = response.headers.get('Content-Disposition', '') content_disposition = response.headers.get(
'Content-Disposition', '')
match = re.search(r'filename\*?=(.+)', content_disposition) match = re.search(r'filename\*?=(.+)', content_disposition)
if match: if match:
filename = urllib.parse.unquote(match.group(1), encoding='utf-8') filename = urllib.parse.unquote(
match.group(1), encoding='utf-8')
filename = filename.replace("UTF-8''", '') filename = filename.replace("UTF-8''", '')
output_path = os.path.join(path, filename) output_path = os.path.join(path, filename)
with open(output_path, 'wb') as file: with open(output_path, 'wb') as file:
file.write(response.read()) file.write(response.read())
_babase.pushcall(Call(callback, output_path), from_other_thread=True) _babase.pushcall(Call(callback, output_path),
from_other_thread=True)
print(f"File downloaded and saved to: {output_path}") print(f"File downloaded and saved to: {output_path}")
else: else:
print(f"Failed to download file. Status code: {response.getcode()}") print(
f"Failed to download file. Status code: {response.getcode()}")
except urllib.error.URLError as e: except urllib.error.URLError as e:
# bui.screenmessage(f'Error occured {e}') # bui.screenmessage(f'Error occured {e}')
print(f"Error occurred: {e}") print(f"Error occurred: {e}")