Merge pull request #228 from brostosjoined/main

Request from players
This commit is contained in:
rikkolovescats 2024-02-04 14:30:33 +05:30 committed by GitHub
commit cbf57d43fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 10175 additions and 8 deletions

View file

@ -254,7 +254,7 @@
{
"name": "Rikko",
"email": "rikkolovescats@proton.me",
"discord": "Rikko#7383"
"discord": "rikkolovescats"
}
],
"versions": {
@ -360,7 +360,7 @@
{
"name": "TheMikirog",
"email": "",
"discord": "TheMikirog#1984"
"discord": "themikirog"
}
],
"versions": {
@ -601,7 +601,7 @@
{
"name": "TheMikirog",
"email": "",
"discord": "TheMikirog#1984"
"discord": "themikirog"
},
{
"name": "JoseAng3l",
@ -913,9 +913,9 @@
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "47a1ca8",
"released_on": "22-08-2023",
"md5sum": "398cf911195c4904b281ed05767e25f4"
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "4a9cdcd798454e5034a5ea9ce58fe586"
}
}
},
@ -967,6 +967,386 @@
"md5sum": "1cbe5b3e85b5dfcee1eb322f33568fd4"
}
}
},
"avalanche": {
"description": "Dodge the falling ice bombs",
"external_url": "",
"authors": [
{
"name": "",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "a07a171c29417056bb69ed1cf0f2864b"
}
}
},
"hyper_race": {
"description": "Race and avoid the obstacles",
"external_url": "",
"authors": [
{
"name": "JoseAng3l",
"email": "",
"discord": "joseang3l"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "8b423bdae256bd411489528b550b8bd9"
}
}
},
"meteor_shower_deluxe": {
"description": "Meteor shower on all maps support",
"external_url": "",
"authors": [
{
"name": "EraOSBeta",
"email": "",
"discord": "3ra0"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "7e86bbc8e3ebb26a66602068950adfbf"
}
}
},
"ofuuu_attack": {
"description": "Dodge the falling bombs.",
"external_url": "",
"authors": [
{
"name": "Riyukii",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "982c72f2d2cb3d50280f50b022e7865f"
}
}
},
"snow_ball_fight": {
"description": "Throw snoballs and dominate",
"external_url": "https://youtu.be/uXyb_meBjGI?si=D_N_OXZT5BFh8R5C",
"authors": [
{
"name": "JoseAng3l",
"email": "",
"discord": "joseang3l"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "ef6bd7cd0404674f65e8f8d4da3ab8c8"
}
}
},
"egg_game": {
"description": "Throw Egg as far u can",
"external_url": "https://youtu.be/82vLp9ceCcw?si=OSC5Hu3Ns7PevlwP",
"authors": [
{
"name": "Mr.Smoothy",
"email": "",
"discord": "mr.smoothy"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "388e58a",
"released_on": "01-02-2024",
"md5sum": "8fc1f9c984f9e77b0125cbeaba5c13bf"
}
}
},
"you_vs_bombsquad": {
"description": "You against bombsquad solo or with friends",
"external_url": "",
"authors": [
{
"name": "JoseAng3l",
"email": "",
"discord": "joseang3l"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "451daeabbd628ec70fa31b80a0999f35"
}
}
},
"onslaught_football": {
"description": "Onslaught but in football map",
"external_url": "",
"authors": [
{
"name": "IDK",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "e6267fec4dc7f874b1f8065863aae1cb"
}
}
},
"lame_fight": {
"description": "Save World With Super Powers",
"external_url": "",
"authors": [
{
"name": "Blitz",
"email": "",
"discord": "itsmeblitz"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "eb0e5a8658e82b3aac6b04b68c1c0f60"
}
}
},
"infinite_ninjas": {
"description": "How long can you survive from Ninjas??",
"external_url": "",
"authors": [
{
"name": "IDK",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "15e303e02e3da4636fd002c43a579180"
}
}
},
"gravity_falls": {
"description": "Trip to the moon",
"external_url": "",
"authors": [
{
"name": "IDK",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "6d0b06b283ef702f41837f09e457d3b8"
}
}
},
"bot_chase": {
"description": "Try to survive from bots!",
"external_url": "",
"authors": [
{
"name": "! JETZ",
"email": "",
"discord": "! JETZ#5313"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "b0519f41146eb2f8b9c2d6c747376a9b"
}
}
},
"down_into_the_abyss": {
"description": "Survive as long as you can but dont miss a step",
"external_url": "",
"authors": [
{
"name": "IDK",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "d3c488671dd35c488e22002ddeb80aef"
}
}
},
"better_deathmatch": {
"description": "A very-customisable DeathMatch mini-game",
"external_url": "",
"authors": [
{
"name": "Freaku",
"email": "",
"discord": "freakyyyy"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "0b607db2dbe3ab40aa05bd4bfd5b4afa"
}
}
},
"better_elimination": {
"description": "A very-customisable Elimination mini-game",
"external_url": "",
"authors": [
{
"name": "Freaku",
"email": "",
"discord": "freakyyyy"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "18bbb7f6ddc4206c7039ad3b5f5facae"
}
}
},
"bot_shower": {
"description": "Survive from the bots.",
"external_url": "",
"authors": [
{
"name": "! JETZ",
"email": "",
"discord": "! JETZ#5313"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "1259e09ccd9b10ae82e4a6623572a4d2"
}
}
},
"explodo_run": {
"description": "Cursed meteor shower of crazy Captain Jack trying take your soul",
"external_url": "",
"authors": [
{
"name": "Blitz",
"email": "",
"discord": "itsmeblitz"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "ec6286e",
"released_on": "01-02-2024",
"md5sum": "e3cd927316b9c3bcac714b4dc8d0d73c"
}
}
},
"extinction": {
"description": "Survive the Extinction.",
"external_url": "",
"authors": [
{
"name": "IDK",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "9fb61df79a50d010864964a7cb1de76e"
}
}
},
"fat_pigs": {
"description": "Eliminate other Mels' while dodging falling stickies.",
"external_url": "",
"authors": [
{
"name": "Zacker Tz",
"email": "",
"discord": "zacker_tz"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "ec6286e",
"released_on": "01-02-2024",
"md5sum": "fe0d82c322ce01d8956af3131e135ad2"
}
}
},
"flag_day": {
"description": "Pick up flags to receive a prize.But beware...",
"external_url": "https://youtu.be/ANDzdBicjA4?si=h8S_TPUAxSaG7nls",
"authors": [
{
"name": "MattZ45986",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "ec6286e",
"released_on": "01-02-2024",
"md5sum": "e11fbf742e6556939ac8d986e2dd430b"
}
}
}
}
}

View file

@ -0,0 +1,143 @@
# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
"""Avalancha mini-game."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import random
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.onscreentimer import OnScreenTimer
from bascenev1lib.game.meteorshower import *
from bascenev1lib.actor.spazbot import *
from bascenev1lib.actor.spaz import PunchHitMessage
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Optional, List, Dict, Type, Type
## MoreMinigames.py support ##
randomPic = ["lakeFrigidPreview", "hockeyStadiumPreview"]
def ba_get_api_version():
return 8
def ba_get_levels():
return [
bs._level.Level(
"Icy Emits",
gametype=IcyEmitsGame,
settings={},
preview_texture_name=random.choice(randomPic),
)
]
## MoreMinigames.py support ##
class PascalBot(BrawlerBot):
color = (0, 0, 3)
highlight = (0.2, 0.2, 1)
character = "Pascal"
bouncy = True
punchiness = 0.7
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.FreezeMessage):
return
else:
super().handlemessage(msg)
# ba_meta export bascenev1.GameActivity
class AvalanchaGame(MeteorShowerGame):
"""Minigame involving dodging falling bombs."""
name = "Avalanche"
description = "Dodge the ice-bombs."
available_settings = [
bs.BoolSetting("Epic Mode", default=False),
bs.IntSetting("Difficulty", default=1, min_value=1, max_value=3, increment=1),
]
scoreconfig = bs.ScoreConfig(
label="Survived", scoretype=bs.ScoreType.MILLISECONDS, version="B"
)
announce_player_deaths = True
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return ["Tip Top"]
def __init__(self, settings: dict):
super().__init__(settings)
self._epic_mode = settings.get("Epic Mode", False)
self._last_player_death_time: Optional[float] = None
self._meteor_time = 2.0
if settings["Difficulty"] == 1:
self._min_delay = 0.4
elif settings["Difficulty"] == 2:
self._min_delay = 0.3
else:
self._min_delay = 0.1
self._timer: Optional[OnScreenTimer] = None
self._bots = SpazBotSet()
self.default_music = (
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SURVIVAL
)
if self._epic_mode:
self.slow_motion = True
def on_transition_in(self) -> None:
super().on_transition_in()
gnode = bs.getactivity().globalsnode
gnode.tint = (0.5, 0.5, 1)
act = bs.getactivity().map
shared = SharedObjects.get()
mat = bs.Material()
mat.add_actions(actions=("modify_part_collision", "friction", 0.18))
act.node.color = act.bottom.color = (1, 1, 1.2)
act.node.reflection = act.bottom.reflection = "soft"
act.node.materials = [shared.footing_material, mat]
def _set_meteor_timer(self) -> None:
bs.timer(
(1.0 + 0.2 * random.random()) * self._meteor_time, self._drop_bomb_cluster
)
def _drop_bomb_cluster(self) -> None:
defs = self.map.defs
delay = 0.0
for _i in range(random.randrange(1, 3)):
pos = defs.points["flag_default"]
pos = (pos[0], pos[1] + 0.4, pos[2])
dropdir = -1.0 if pos[0] > 0 else 1.0
vel = (random.randrange(-4, 4), 7.0, random.randrange(0, 4))
bs.timer(delay, babase.Call(self._drop_bomb, pos, vel))
delay += 0.1
self._set_meteor_timer()
def _drop_bomb(self, position: Sequence[float], velocity: Sequence[float]) -> None:
Bomb(position=position, velocity=velocity, bomb_type="ice").autoretain()
def _decrement_meteor_time(self) -> None:
if self._meteor_time < self._min_delay:
return
self._meteor_time = max(0.01, self._meteor_time * 0.9)
if random.choice([0, 0, 1]) == 1:
pos = self.map.defs.points["flag_default"]
self._bots.spawn_bot(PascalBot, pos=pos, spawn_time=2)

View file

@ -0,0 +1,269 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# BetterDeathMatch
# Made by your friend: @[Just] Freak#4999
"""Defines a very-customisable DeathMatch mini-game"""
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Tuple, Union, Sequence, Optional
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export bascenev1.GameActivity
class BetterDeathMatchGame(bs.TeamGameActivity[Player, Team]):
"""A game type based on acquiring kills."""
name = 'Btrr Death Match'
description = 'Kill a set number of enemies to win.\nbyFREAK'
# 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),
## Add settings ##
bs.BoolSetting('Enable Gloves', False),
bs.BoolSetting('Enable Powerups', True),
bs.BoolSetting('Night Mode', False),
bs.BoolSetting('Icy Floor', False),
bs.BoolSetting('One Punch Kill', False),
bs.BoolSetting('Spawn with Shield', False),
bs.BoolSetting('Punching Only', False),
## Add settings ##
]
# 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 bs.app.classic.getmaps('melee')
def __init__(self, settings: dict):
super().__init__(settings)
self._scoreboard = Scoreboard()
self._score_to_win: Optional[int] = None
self._dingsound = bui.getsound('dingSmall')
## Take applied settings ##
self._boxing_gloves = bool(settings['Enable Gloves'])
self._enable_powerups = bool(settings['Enable Powerups'])
self._night_mode = bool(settings['Night Mode'])
self._icy_floor = bool(settings['Icy Floor'])
self._one_punch_kill = bool(settings['One Punch Kill'])
self._shield_ = bool(settings['Spawn with Shield'])
self._only_punch = bool(settings['Punching Only'])
## Take applied settings ##
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)
def get_instance_description(self) -> Union[str, Sequence]:
return 'Crush ${ARG1} of your enemies. byFREAK', self._score_to_win
def get_instance_description_short(self) -> Union[str, Sequence]:
return 'kill ${ARG1} enemies. byFREAK', self._score_to_win
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
## Run settings related: IcyFloor ##
def on_transition_in(self) -> None:
super().on_transition_in()
activity = bs.getactivity()
if self._icy_floor:
activity.map.is_hockey = True
else:
return
## Run settings related: IcyFloor ##
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
## Run settings related: NightMode,Powerups ##
if self._night_mode:
bs.getactivity().globalsnode.tint = (0.5, 0.7, 1)
else:
pass
# -# Tried return here, pfft. Took me 30mins to figure out why pwps spawning only on NightMode
# -# Now its fixed :)
if self._enable_powerups:
self.setup_standard_powerup_drops()
else:
pass
## Run settings related: NightMode,Powerups ##
# 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()
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
## Run settings related: Spaz ##
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(player)
if self._boxing_gloves:
spaz.equip_boxing_gloves()
if self._one_punch_kill:
spaz._punch_power_scale = 15
if self._shield_:
spaz.equip_shields()
if self._only_punch:
spaz.connect_controls_to_player(enable_bomb=False, enable_pickup=False)
return spaz
## Run settings related: Spaz ##
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()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)

View file

@ -0,0 +1,659 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# BetterElimination
# Made by your friend: @[Just] Freak#4999
# Huge Thx to Nippy for "Live Team Balance"
"""Defines a very-customisable Elimination mini-game"""
# ba_meta require api 8
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
if TYPE_CHECKING:
from typing import (Any, Tuple, Dict, Type, List, Sequence, Optional,
Union)
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 BetterEliminationGame(bs.TeamGameActivity[Player, Team]):
"""Game type where last player(s) left alive win."""
name = 'Bttr Elimination'
description = 'Last remaining alive wins.\nbyFREAK'
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
@classmethod
def get_available_settings(
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
settings = [
bs.IntSetting(
'Life\'s 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),
## Add settings ##
bs.BoolSetting('Live Team Balance (by Nippy#2677)', True),
bs.BoolSetting('Enable Gloves', False),
bs.BoolSetting('Enable Powerups', True),
bs.BoolSetting('Night Mode', False),
bs.BoolSetting('Icy Floor', False),
bs.BoolSetting('One Punch Kill', False),
bs.BoolSetting('Spawn with Shield', False),
bs.BoolSetting('Punching Only', False),
## Add settings ##
]
if issubclass(sessiontype, bs.DualTeamSession):
settings.append(bs.BoolSetting('Solo Mode', default=False))
settings.append(
bs.BoolSetting('Balance Total Life\'s (on spawn only)', 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 bs.app.classic.getmaps('melee')
def __init__(self, settings: dict):
super().__init__(settings)
self._scoreboard = Scoreboard()
self._start_time: Optional[float] = None
self._vs_text: Optional[bs.Actor] = None
self._round_end_timer: Optional[bs.Timer] = None
## Take applied settings ##
self._live_team_balance = bool(settings['Live Team Balance (by Nippy#2677)'])
self._boxing_gloves = bool(settings['Enable Gloves'])
self._enable_powerups = bool(settings['Enable Powerups'])
self._night_mode = bool(settings['Night Mode'])
self._icy_floor = bool(settings['Icy Floor'])
self._one_punch_kill = bool(settings['One Punch Kill'])
self._shield_ = bool(settings['Spawn with Shield'])
self._only_punch = bool(settings['Punching Only'])
## Take applied settings ##
self._epic_mode = bool(settings['Epic Mode'])
self._lives_per_player = int(settings['Life\'s Per Player'])
self._time_limit = float(settings['Time Limit'])
self._balance_total_lives = bool(
settings.get('Balance Total Life\'s (on spawn only)', 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)
def get_instance_description(self) -> Union[str, Sequence]:
return 'Last team standing wins. byFREAK' 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. byFREAK' if isinstance(
self.session, bs.DualTeamSession) else 'last one standing wins'
def on_player_join(self, player: Player) -> None:
# No longer allowing mid-game joiners here; too easy to exploit.
if self.has_begun():
# Make sure their team has survival seconds set if they're all dead
# (otherwise blocked new ffa players are considered 'still alive'
# in score tallying).
if (self._get_total_team_lives(player.team) == 0
and player.team.survival_seconds is None):
player.team.survival_seconds = 0
bui.screenmessage(
babase.Lstr(resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(0, 1, 0),
)
return
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()
## Run settings related: IcyFloor ##
def on_transition_in(self) -> None:
super().on_transition_in()
activity = bs.getactivity()
if self._icy_floor:
activity.map.is_hockey = True
else:
return
## Run settings related: IcyFloor ##
def on_begin(self) -> None:
super().on_begin()
self._start_time = bs.time()
self.setup_standard_time_limit(self._time_limit)
## Run settings related: NightMode,Powerups ##
if self._night_mode:
bs.getactivity().globalsnode.tint = (0.5, 0.7, 1)
else:
pass
# -# Tried return here, pfft. Took me 30mins to figure out why pwps spawning only on NightMode
# -# Now its fixed :)
if self._enable_powerups:
self.setup_standard_powerup_drops()
else:
pass
## Run settings related: NightMode,Powerups ##
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:
# 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))
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()
## Run settings related: Spaz ##
if self._boxing_gloves:
actor.equip_boxing_gloves()
if self._one_punch_kill:
actor._punch_power_scale = 15
if self._shield_:
actor.equip_shields()
if self._only_punch:
actor.connect_controls_to_player(enable_bomb=False, enable_pickup=False)
return actor
## Run settings related: Spaz ##
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:
# Nippy#2677
team_count = 1 # Just initiating
if player.lives > 0 and self._live_team_balance:
team_mem = []
for teamer in player.team.players:
if player != teamer:
team_mem.append(teamer) # Got Dead players Team
live = player.lives
team_count = len(team_mem)
# Extending Player List for Sorted Players
for i in range(int((live if live % 2 == 0 else live+1)/2)):
team_mem.extend(team_mem)
if team_count > 0:
for i in range(live):
team_mem[i].lives += 1
if team_count <= 0: # Draw if Player Leaves
self.end_game()
# Nippy#2677
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)

View file

@ -0,0 +1,223 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import random
from bascenev1lib.actor.spazbot import BrawlerBot, SpazBotSet, SpazBot, SpazBotDiedMessage
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.spaz import Spaz
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, List, Type, Optional
def ba_get_api_version():
return 8
def ba_get_levels():
return [bs._level.Level(
'Bot Chase', gametype=BotChaseGame,
settings={},
preview_texture_name='footballStadiumPreview')]
class Player(bs.Player['Team']):
"""Our player type for this game"""
def __init__(self) -> None:
super().__init__()
self.death_time: Optional[float] = None
class MrSpazBot(SpazBot):
"""Our bot type for this game"""
character = 'Spaz'
run = True
charge_dist_min = 10.0
charge_dist_max = 9999.0
charge_speed_min = 1.0
charge_speed_max = 1.0
throw_dist_min = 9999
throw_dist_max = 9999
class Team(bs.Team[Player]):
"""Our team type for this minigame"""
# ba_meta export bascenev1.GameActivity
class BotChaseGame(bs.TeamGameActivity[Player, Team]):
"""Our goal is to survive from spawning bots"""
name = 'Bot Chase'
description = 'Try to survive from bots!'
available_settings = [
bs.BoolSetting(
'Epic Mode',
default=False
),
]
announce_player_deaths = True
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return ['Football Stadium']
@classmethod
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
# Coop session unused
return (issubclass(sessiontype, bs.FreeForAllSession) or issubclass(sessiontype, bs.DualTeamSession) or issubclass(sessiontype, bs.CoopSession))
def __init__(self, settings: dict):
super().__init__(settings)
self._bots = SpazBotSet()
self._epic_mode = bool(settings['Epic Mode'])
self._timer: Optional[OnScreenTimer] = None
self._last_player_death_time: Optional[float] = None
if self._epic_mode:
self.slow_motion = True
self.default_music = (bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FORWARD_MARCH)
def on_player_join(self, player: Player) -> None:
if self.has_begun():
bs.broadcastmessage(
babase.Lstr(resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(0, 1, 0),
)
assert self._timer is not None
player.death_time = self._timer.getstarttime()
return
self.spawn_player(player)
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
self._check_end_game()
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(player)
spaz.connect_controls_to_player(enable_punch=True,
enable_bomb=True,
enable_pickup=True)
spaz.bomb_count = 3
spaz.bomb_type = 'normal'
# cerdo gordo
spaz.node.color_mask_texture = bs.gettexture('melColorMask')
spaz.node.color_texture = bs.gettexture('melColor')
spaz.node.head_mesh = bs.getmesh('melHead')
spaz.node.hand_mesh = bs.getmesh('melHand')
spaz.node.torso_mesh = bs.getmesh('melTorso')
spaz.node.pelvis_mesh = bs.getmesh('kronkPelvis')
spaz.node.upper_arm_mesh = bs.getmesh('melUpperArm')
spaz.node.forearm_mesh = bs.getmesh('melForeArm')
spaz.node.upper_leg_mesh = bs.getmesh('melUpperLeg')
spaz.node.lower_leg_mesh = bs.getmesh('melLowerLeg')
spaz.node.toes_mesh = bs.getmesh('melToes')
spaz.node.style = 'mel'
# Sounds cerdo gordo
mel_sounds = [bs.getsound('mel01'), bs.getsound('mel02'), bs.getsound('mel03'), bs.getsound('mel04'), bs.getsound('mel05'),
bs.getsound('mel06'), bs.getsound('mel07'), bs.getsound('mel08'), bs.getsound('mel09'), bs.getsound('mel10')]
spaz.node.jump_sounds = mel_sounds
spaz.node.attack_sounds = mel_sounds
spaz.node.impact_sounds = mel_sounds
spaz.node.pickup_sounds = mel_sounds
spaz.node.death_sounds = [bs.getsound('melDeath01')]
spaz.node.fall_sounds = [bs.getsound('melFall01')]
spaz.play_big_death_sound = True
return spaz
def on_begin(self) -> None:
super().on_begin()
self._bots.spawn_bot(MrSpazBot, pos=(random.choice(
[1, -1, 2, -2]), 1.34, random.choice([1, -1, 2, -2])), spawn_time=2.0)
self._bots.spawn_bot(MrSpazBot, pos=(random.choice(
[1, -1, 2, -2]), 1.34, random.choice([1, -1, 2, -2])), spawn_time=2.0)
self._timer = OnScreenTimer()
self._timer.start()
bs.timer(10.0, self._spawn_this_bot, repeat=True)
bs.timer(5.0, self._check_end_game)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg)
curtime = bs.time()
msg.getplayer(Player).death_time = curtime
if isinstance(self.session, bs.CoopSession):
babase.pushcall(self._check_end_game)
self._last_player_death_time = curtime
else:
bs.timer(1.0, self._check_end_game)
elif isinstance(msg, SpazBotDiedMessage):
self._spawn_this_bot()
else:
return super().handlemessage(msg)
return None
def _spawn_this_bot(self) -> None:
self._bots.spawn_bot(MrSpazBot, pos=(random.choice(
[1, -1, 2, -2]), 1.34, random.choice([1, -1, 2, -2])), spawn_time=2.0)
def _check_end_game(self) -> None:
living_team_count = 0
for team in self.teams:
for player in team.players:
if player.is_alive():
living_team_count += 1
break
if isinstance(self.session, bs.CoopSession):
if living_team_count <= 0:
self.end_game()
else:
if living_team_count <= 1:
self.end_game()
def end_game(self) -> None:
cur_time = bs.time()
assert self._timer is not None
start_time = self._timer.getstarttime()
for team in self.teams:
for player in team.players:
survived = False
if player.death_time is None:
survived = True
player.death_time = cur_time + 1
score = int(player.death_time - self._timer.getstarttime())
if survived:
score += 50
self.stats.player_scored(player, score, screenmessage=False)
self._timer.stop(endtime=self._last_player_death_time)
results = bs.GameResults()
for team in self.teams:
longest_life = 0.0
for player in team.players:
assert player.death_time is not None
longest_life = max(longest_life,
player.death_time - start_time)
results.set_team_score(team, int(1000.0 * longest_life))
self.end(results=results)

View file

@ -0,0 +1,196 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import random
from bascenev1lib.actor.onscreentimer import OnScreenTimer
from bascenev1lib.actor.spazbot import (
SpazBot, SpazBotSet,
BomberBot, BrawlerBot, BouncyBot,
ChargerBot, StickyBot, TriggerBot,
ExplodeyBot)
if TYPE_CHECKING:
from typing import Any, List, Type, Optional
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self) -> None:
super().__init__()
self.death_time: Optional[float] = None
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class BotShowerGame(bs.TeamGameActivity[Player, Team]):
"""A babase.MeteorShowerGame but replaced with bots."""
name = 'Bot Shower'
description = 'Survive from the bots.'
available_settings = [
bs.BoolSetting('Spaz', default=True),
bs.BoolSetting('Zoe', default=True),
bs.BoolSetting('Kronk', default=True),
bs.BoolSetting('Snake Shadow', default=True),
bs.BoolSetting('Mel', default=True),
bs.BoolSetting('Jack Morgan', default=True),
bs.BoolSetting('Easter Bunny', default=True),
bs.BoolSetting('Epic Mode', default=False),
]
announce_player_deaths = True
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return ['Football Stadium', 'Hockey Stadium']
def __init__(self, settings: dict) -> None:
super().__init__(settings)
self._epic_mode = settings['Epic Mode']
self._last_player_death_time: Optional[float] = None
self._timer: Optional[OnScreenTimer] = None
self._bots: Optional[SpazBotSet] = None
self._bot_type: List[SpazBot] = []
if bool(settings['Spaz']) == True:
self._bot_type.append(BomberBot)
else:
if BomberBot in self._bot_type:
self._bot_type.remove(BomberBot)
if bool(settings['Zoe']) == True:
self._bot_type.append(TriggerBot)
else:
if TriggerBot in self._bot_type:
self._bot_type.remove(TriggerBot)
if bool(settings['Kronk']) == True:
self._bot_type.append(BrawlerBot)
else:
if BrawlerBot in self._bot_type:
self._bot_type.remove(BrawlerBot)
if bool(settings['Snake Shadow']) == True:
self._bot_type.append(ChargerBot)
else:
if ChargerBot in self._bot_type:
self._bot_type.remove(ChargerBot)
if bool(settings['Jack Morgan']) == True:
self._bot_type.append(ExplodeyBot)
else:
if ExplodeyBot in self._bot_type:
self._bot_type.remove(ExplodeyBot)
if bool(settings['Easter Bunny']) == True:
self._bot_type.append(BouncyBot)
else:
if BouncyBot in self._bot_type:
self._bot_type.remove(BouncyBot)
self.slow_motion = self._epic_mode
self.default_music = (bs.MusicType.EPIC
if self._epic_mode else bs.MusicType.SURVIVAL)
def on_begin(self) -> None:
super().on_begin()
self._bots = SpazBotSet()
self._timer = OnScreenTimer()
self._timer.start()
if self._epic_mode:
bs.timer(1.0, self._start_spawning_bots)
else:
bs.timer(5.0, self._start_spawning_bots)
bs.timer(5.0, self._check_end_game)
def spawn_player(self, player: Player) -> None:
spaz = self.spawn_player_spaz(player)
spaz.connect_controls_to_player(
enable_punch=False,
enable_bomb=False,
enable_pickup=False)
return spaz
def on_player_join(self, player: Player) -> None:
if self.has_begun():
bui.screenmessage(
babase.Lstr(resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(1, 1, 0),
)
assert self._timer is not None
player.death_time = self._timer.getstarttime()
return
self.spawn_player(player)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
curtime = bs.time()
msg.getplayer(Player).death_time = curtime
bs.timer(1.0, self._check_end_game)
else:
super().handlemessage(msg)
def _start_spawning_bots(self) -> None:
bs.timer(1.2, self._spawn_bot, repeat=True)
bs.timer(2.2, self._spawn_bot, repeat=True)
def _spawn_bot(self) -> None:
assert self._bots is not None
self._bots.spawn_bot(random.choice(self._bot_type), pos=(
random.uniform(-11, 11), (9.8 if self.map.getname() == 'Football Stadium' else 5.0), random.uniform(-5, 5)))
def _check_end_game(self) -> None:
living_team_count = 0
for team in self.teams:
for player in team.players:
if player.is_alive():
living_team_count += 1
break
if living_team_count <= 1:
self.end_game()
def end_game(self) -> None:
cur_time = bs.time()
assert self._timer is not None
start_time = self._timer.getstarttime()
for team in self.teams:
for player in team.players:
survived = False
if player.death_time is None:
survived = True
player.death_time = cur_time + 1
score = int(player.death_time - self._timer.getstarttime())
if survived:
score += 50
self.stats.player_scored(player, score, screenmessage=False)
self._timer.stop(endtime=self._last_player_death_time)
results = bs.GameResults()
for team in self.teams:
longest_life = 0.0
for player in team.players:
assert player.death_time is not None
longest_life = max(longest_life,
player.death_time - start_time)
results.set_team_score(team, int(1000.0 * longest_life))
self.end(results=results)

View file

@ -0,0 +1,790 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# 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
import _babase
import random
from bascenev1._map import register_map
from bascenev1lib.actor.spaz import PickupMessage
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.spazbot import SpazBotSet, ChargerBotPro, TriggerBotPro
from bascenev1lib.actor.bomb import Blast
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, Sequence
lang = bs.app.lang.language
if lang == 'Spanish':
name = 'Abajo en el Abismo'
description = 'Sobrevive tanto como puedas'
help = 'El mapa es 3D, ¡ten cuidado!'
author = 'Autor: Deva'
github = 'GitHub: spdv123'
blog = 'Blog: superdeva.info'
peaceTime = 'Tiempo de Paz'
npcDensity = 'Densidad de Enemigos'
hint_use_punch = '¡Ahora puedes golpear a los enemigos!'
elif lang == 'Chinese':
name = '无尽深渊'
description = '在无穷尽的坠落中存活更长时间'
help = ''
author = '作者: Deva'
github = 'GitHub: spdv123'
blog = '博客: superdeva.info'
peaceTime = '和平时间'
npcDensity = 'NPC密度'
hint_use_punch = u'现在可以使用拳头痛扁你的敌人了'
else:
name = 'Down Into The Abyss'
description = 'Survive as long as you can'
help = 'The map is 3D, be careful!'
author = 'Author: Deva'
github = 'GitHub: spdv123'
blog = 'Blog: superdeva.info'
peaceTime = 'Peace Time'
npcDensity = 'NPC Density'
hint_use_punch = 'You can punch your enemies now!'
class AbyssMap(bs.Map):
from bascenev1lib.mapdata import happy_thoughts as defs
# Add the y-dimension space for players
defs.boxes['map_bounds'] = (-0.8748348681, 9.212941713, -9.729538885) \
+ (0.0, 0.0, 0.0) \
+ (36.09666006, 26.19950145, 20.89541168)
name = 'Abyss Unhappy'
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['abyss']
@classmethod
def get_preview_texture_name(cls) -> str:
return 'alwaysLandPreview'
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
'mesh': bs.getmesh('alwaysLandLevel'),
'bottom_mesh': bs.getmesh('alwaysLandLevelBottom'),
'bgmesh': bs.getmesh('alwaysLandBG'),
'collision_mesh': bs.getcollisionmesh('alwaysLandLevelCollide'),
'tex': bs.gettexture('alwaysLandLevelColor'),
'bgtex': bs.gettexture('alwaysLandBGColor'),
'vr_fill_mound_mesh': bs.getmesh('alwaysLandVRFillMound'),
'vr_fill_mound_tex': bs.gettexture('vrFillMound')
}
return data
@classmethod
def get_music_type(cls) -> bs.MusicType:
return bs.MusicType.FLYING
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, -3.7, 2.5))
self.background = bs.newnode(
'terrain',
attrs={
'mesh': self.preloaddata['bgmesh'],
'lighting': False,
'background': True,
'color_texture': self.preloaddata['bgtex']
})
bs.newnode('terrain',
attrs={
'mesh': self.preloaddata['vr_fill_mound_mesh'],
'lighting': False,
'vr_only': True,
'color': (0.2, 0.25, 0.2),
'background': True,
'color_texture': self.preloaddata['vr_fill_mound_tex']
})
gnode = bs.getactivity().globalsnode
gnode.happy_thoughts_mode = True
gnode.shadow_offset = (0.0, 8.0, 5.0)
gnode.tint = (1.3, 1.23, 1.0)
gnode.ambient_color = (1.3, 1.23, 1.0)
gnode.vignette_outer = (0.64, 0.59, 0.69)
gnode.vignette_inner = (0.95, 0.95, 0.93)
gnode.vr_near_clip = 1.0
self.is_flying = True
register_map(AbyssMap)
class SpazTouchFoothold:
pass
class BombToDieMessage:
pass
class Foothold(bs.Actor):
def __init__(self,
position: Sequence[float] = (0.0, 1.0, 0.0),
power: str = 'random',
size: float = 6.0,
breakable: bool = True,
moving: bool = False):
super().__init__()
shared = SharedObjects.get()
powerup = PowerupBoxFactory.get()
fmesh = bs.getmesh('landMine')
fmeshs = bs.getmesh('powerupSimple')
self.died = False
self.breakable = breakable
self.moving = moving # move right and left
self.lrSig = 1 # left or right signal
self.lrSpeedPlus = random.uniform(1 / 2.0, 1 / 0.7)
self._npcBots = SpazBotSet()
self.foothold_material = bs.Material()
self.impact_sound = bui.getsound('impactMedium')
self.foothold_material.add_actions(
conditions=(('they_dont_have_material', shared.player_material),
'and',
('they_have_material', shared.object_material),
'or',
('they_have_material', shared.footing_material)),
actions=(('modify_node_collision', 'collide', True),
))
self.foothold_material.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(('modify_part_collision', 'physical', True),
('modify_part_collision', 'stiffness', 0.05),
('message', 'our_node', 'at_connect', SpazTouchFoothold()),
))
self.foothold_material.add_actions(
conditions=('they_have_material', self.foothold_material),
actions=('modify_node_collision', 'collide', False),
)
tex = {
'punch': powerup.tex_punch,
'sticky_bombs': powerup.tex_sticky_bombs,
'ice_bombs': powerup.tex_ice_bombs,
'impact_bombs': powerup.tex_impact_bombs,
'health': powerup.tex_health,
'curse': powerup.tex_curse,
'shield': powerup.tex_shield,
'land_mines': powerup.tex_land_mines,
'tnt': bs.gettexture('tnt'),
}.get(power, bs.gettexture('tnt'))
powerupdist = {
powerup.tex_bomb: 3,
powerup.tex_ice_bombs: 2,
powerup.tex_punch: 3,
powerup.tex_impact_bombs: 3,
powerup.tex_land_mines: 3,
powerup.tex_sticky_bombs: 4,
powerup.tex_shield: 4,
powerup.tex_health: 3,
powerup.tex_curse: 1,
bs.gettexture('tnt'): 2
}
self.randtex = []
for keyTex in powerupdist:
for i in range(powerupdist[keyTex]):
self.randtex.append(keyTex)
if power == 'random':
random.seed()
tex = random.choice(self.randtex)
self.tex = tex
self.powerup_type = {
powerup.tex_punch: 'punch',
powerup.tex_bomb: 'triple_bombs',
powerup.tex_ice_bombs: 'ice_bombs',
powerup.tex_impact_bombs: 'impact_bombs',
powerup.tex_land_mines: 'land_mines',
powerup.tex_sticky_bombs: 'sticky_bombs',
powerup.tex_shield: 'shield',
powerup.tex_health: 'health',
powerup.tex_curse: 'curse',
bs.gettexture('tnt'): 'tnt'
}.get(self.tex, '')
self._spawn_pos = (position[0], position[1], position[2])
self.node = bs.newnode(
'prop',
delegate=self,
attrs={
'body': 'landMine',
'position': self._spawn_pos,
'mesh': fmesh,
'light_mesh': fmeshs,
'shadow_size': 0.5,
'velocity': (0, 0, 0),
'density': 90000000000,
'sticky': False,
'body_scale': size,
'mesh_scale': size,
'color_texture': tex,
'reflection': 'powerup',
'is_area_of_interest': True,
'gravity_scale': 0.0,
'reflection_scale': [0],
'materials': [self.foothold_material,
shared.object_material,
shared.footing_material]
})
self.touchedSpazs = set()
self.keep_vel()
def keep_vel(self) -> None:
if self.node and not self.died:
speed = bs.getactivity().cur_speed
if self.moving:
if abs(self.node.position[0]) > 10:
self.lrSig *= -1
self.node.velocity = (
self.lrSig * speed * self.lrSpeedPlus, speed, 0)
bs.timer(0.1, bs.WeakCall(self.keep_vel))
else:
self.node.velocity = (0, speed, 0)
# self.node.extraacceleration = (0, self.speed, 0)
bs.timer(0.1, bs.WeakCall(self.keep_vel))
def tnt_explode(self) -> None:
pos = self.node.position
Blast(position=pos,
blast_radius=6.0,
blast_type='tnt',
source_player=None).autoretain()
def spawn_npc(self) -> None:
if not self.breakable:
return
if self._npcBots.have_living_bots():
return
if random.randint(0, 3) >= bs.getactivity().npc_density:
return
pos = self.node.position
pos = (pos[0], pos[1] + 1, pos[2])
self._npcBots.spawn_bot(
bot_type=random.choice([ChargerBotPro, TriggerBotPro]),
pos=pos,
spawn_time=10)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:
self.node.delete()
self.died = True
elif isinstance(msg, bs.OutOfBoundsMessage):
self.handlemessage(bs.DieMessage())
elif isinstance(msg, BombToDieMessage):
if self.powerup_type == 'tnt':
self.tnt_explode()
self.handlemessage(bs.DieMessage())
elif isinstance(msg, bs.HitMessage):
ispunched = (msg.srcnode and msg.srcnode.getnodetype() == 'spaz')
if not ispunched:
if self.breakable:
self.handlemessage(BombToDieMessage())
elif isinstance(msg, SpazTouchFoothold):
node = bs.getcollision().opposingnode
if node is not None and node:
try:
spaz = node.getdelegate(object)
if not isinstance(spaz, AbyssPlayerSpaz):
return
if spaz in self.touchedSpazs:
return
self.touchedSpazs.add(spaz)
self.spawn_npc()
spaz.fix_2D_position()
if self.powerup_type not in ['', 'tnt']:
node.handlemessage(
bs.PowerupMessage(self.powerup_type))
except Exception as e:
print(e)
pass
class AbyssPlayerSpaz(PlayerSpaz):
def __init__(self,
player: bs.Player,
color: Sequence[float] = (1.0, 1.0, 1.0),
highlight: Sequence[float] = (0.5, 0.5, 0.5),
character: str = 'Spaz',
powerups_expire: bool = True):
super().__init__(player=player,
color=color,
highlight=highlight,
character=character,
powerups_expire=powerups_expire)
self.node.fly = False
self.node.hockey = True
self.hitpoints_max = self.hitpoints = 1500 # more HP to handle drop
bs.timer(bs.getactivity().peace_time,
bs.WeakCall(self.safe_connect_controls_to_player))
def safe_connect_controls_to_player(self) -> None:
try:
self.connect_controls_to_player()
except:
pass
def on_move_up_down(self, value: float) -> None:
"""
Called to set the up/down joystick amount on this spaz;
used for player or AI connections.
value will be between -32768 to 32767
WARNING: deprecated; use on_move instead.
"""
if not self.node:
return
if self.node.run > 0.1:
self.node.move_up_down = value
else:
self.node.move_up_down = value / 3.
def on_move_left_right(self, value: float) -> None:
"""
Called to set the left/right joystick amount on this spaz;
used for player or AI connections.
value will be between -32768 to 32767
WARNING: deprecated; use on_move instead.
"""
if not self.node:
return
if self.node.run > 0.1:
self.node.move_left_right = value
else:
self.node.move_left_right = value / 1.5
def fix_2D_position(self) -> None:
self.node.fly = True
bs.timer(0.02, bs.WeakCall(self.disable_fly))
def disable_fly(self) -> None:
if self.node:
self.node.fly = False
def curse(self) -> None:
"""
Give this poor spaz a curse;
he will explode in 5 seconds.
"""
if not self._cursed:
factory = SpazFactory.get()
self._cursed = True
# Add the curse material.
for attr in ['materials', 'roller_materials']:
materials = getattr(self.node, attr)
if factory.curse_material not in materials:
setattr(self.node, attr,
materials + (factory.curse_material, ))
# None specifies no time limit
assert self.node
if self.curse_time == -1:
self.node.curse_death_time = -1
else:
# Note: curse-death-time takes milliseconds.
tval = bs.time()
assert isinstance(tval, (float, int))
self.node.curse_death_time = bs.time() + 15
bs.timer(15, bs.WeakCall(self.curse_explode))
def handlemessage(self, msg: Any) -> Any:
dontUp = False
if isinstance(msg, PickupMessage):
dontUp = True
collision = bs.getcollision()
opposingnode = collision.opposingnode
opposingbody = collision.opposingbody
if opposingnode is None or not opposingnode:
return True
opposingdelegate = opposingnode.getdelegate(object)
# Don't pick up the foothold
if isinstance(opposingdelegate, Foothold):
return True
# dont allow picking up of invincible dudes
try:
if opposingnode.invincible:
return True
except Exception:
pass
# if we're grabbing the pelvis of a non-shattered spaz,
# we wanna grab the torso instead
if (opposingnode.getnodetype() == 'spaz'
and not opposingnode.shattered and opposingbody == 4):
opposingbody = 1
# Special case - if we're holding a flag, don't replace it
# (hmm - should make this customizable or more low level).
held = self.node.hold_node
if held and held.getnodetype() == 'flag':
return True
# Note: hold_body needs to be set before hold_node.
self.node.hold_body = opposingbody
self.node.hold_node = opposingnode
if not dontUp:
PlayerSpaz.handlemessage(self, msg)
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self) -> None:
super().__init__()
self.death_time: float | None = None
self.notIn: bool = None
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class AbyssGame(bs.TeamGameActivity[Player, Team]):
name = name
description = description
scoreconfig = bs.ScoreConfig(label='Survived',
scoretype=bs.ScoreType.MILLISECONDS,
version='B')
# Print messages when players die (since its meaningful in this game).
announce_player_deaths = True
# We're currently hard-coded for one map.
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ['Abyss Unhappy']
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]) -> list[babase.Setting]:
settings = [
bs.FloatChoiceSetting(
peaceTime,
choices=[
('None', 0.0),
('Shorter', 2.5),
('Short', 5.0),
('Normal', 10.0),
('Long', 15.0),
('Longer', 20.0),
],
default=10.0,
),
bs.FloatChoiceSetting(
npcDensity,
choices=[
('0%', 0),
('25%', 1),
('50%', 2),
('75%', 3),
('100%', 4),
],
default=2,
),
bs.BoolSetting('Epic Mode', default=False),
]
return settings
# We support teams, free-for-all, and co-op sessions.
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (issubclass(sessiontype, bs.DualTeamSession)
or issubclass(sessiontype, bs.FreeForAllSession)
or issubclass(sessiontype, bs.CoopSession))
def __init__(self, settings: dict):
super().__init__(settings)
self._epic_mode = settings.get('Epic Mode', False)
self._last_player_death_time: float | None = None
self._timer: OnScreenTimer | None = None
self.fix_y = -5.614479365
self.start_z = 0
self.init_position = (0, self.start_z, self.fix_y)
self.team_init_positions = [(-5, self.start_z, self.fix_y),
(5, self.start_z, self.fix_y)]
self.cur_speed = 2.5
# TODO: The variable below should be set in settings
self.peace_time = float(settings[peaceTime])
self.npc_density = float(settings[npcDensity])
# Some base class overrides:
self.default_music = (bs.MusicType.EPIC
if self._epic_mode else bs.MusicType.SURVIVAL)
if self._epic_mode:
self.slow_motion = True
self._game_credit = bs.NodeActor(
bs.newnode(
'text',
attrs={
'v_attach': 'bottom',
'h_align': 'center',
'vr_depth': 0,
'color': (0.0, 0.7, 1.0),
'shadow': 1.0 if True else 0.5,
'flatness': 1.0 if True else 0.5,
'position': (0, 0),
'scale': 0.8,
'text': ' | '.join([author, github, blog])
}))
def get_instance_description(self) -> str | Sequence:
return description
def get_instance_description_short(self) -> str | Sequence:
return self.get_instance_description() + '\n' + help
def on_player_join(self, player: Player) -> None:
if self.has_begun():
player.notIn = True
bs.broadcastmessage(babase.Lstr(
resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(0, 1, 0))
self.spawn_player(player)
def on_begin(self) -> None:
super().on_begin()
self._timer = OnScreenTimer()
self._timer.start()
self.level_cnt = 1
if self.teams_or_ffa() == 'teams':
ip0 = self.team_init_positions[0]
ip1 = self.team_init_positions[1]
Foothold(
(ip0[0], ip0[1] - 2, ip0[2]),
power='shield', breakable=False).autoretain()
Foothold(
(ip1[0], ip1[1] - 2, ip1[2]),
power='shield', breakable=False).autoretain()
else:
ip = self.init_position
Foothold(
(ip[0], ip[1] - 2, ip[2]),
power='shield', breakable=False).autoretain()
bs.timer(int(5.0 / self.cur_speed),
bs.WeakCall(self.add_foothold), repeat=True)
# Repeat check game end
bs.timer(1.0, self._check_end_game, repeat=True)
bs.timer(self.peace_time + 0.1,
bs.WeakCall(self.tip_hint, hint_use_punch))
bs.timer(6.0, bs.WeakCall(self.faster_speed), repeat=True)
def tip_hint(self, text: str) -> None:
bs.broadcastmessage(text, color=(0.2, 0.2, 1))
def faster_speed(self) -> None:
self.cur_speed *= 1.15
def add_foothold(self) -> None:
ip = self.init_position
ip_1 = (ip[0] - 7, ip[1], ip[2])
ip_2 = (ip[0] + 7, ip[1], ip[2])
ru = random.uniform
self.level_cnt += 1
if self.level_cnt % 3:
Foothold((
ip_1[0] + ru(-5, 5),
ip[1] - 2,
ip[2] + ru(-0.0, 0.0))).autoretain()
Foothold((
ip_2[0] + ru(-5, 5),
ip[1] - 2,
ip[2] + ru(-0.0, 0.0))).autoretain()
else:
Foothold((
ip[0] + ru(-8, 8),
ip[1] - 2,
ip[2]), moving=True).autoretain()
def teams_or_ffa(self) -> None:
if isinstance(self.session, bs.DualTeamSession):
return 'teams'
return 'ffa'
def spawn_player_spaz(self,
player: Player,
position: Sequence[float] = (0, 0, 0),
angle: float | None = None) -> PlayerSpaz:
# pylint: disable=too-many-locals
# pylint: disable=cyclic-import
from babase import _math
from bascenev1._gameutils import animate
position = self.init_position
if self.teams_or_ffa() == 'teams':
position = self.team_init_positions[player.team.id % 2]
angle = None
name = player.getname()
color = player.color
highlight = player.highlight
light_color = _math.normalized_color(color)
display_color = _babase.safecolor(color, target_intensity=0.75)
spaz = AbyssPlayerSpaz(color=color,
highlight=highlight,
character=player.character,
player=player)
player.actor = spaz
assert spaz.node
spaz.node.name = name
spaz.node.name_color = display_color
spaz.connect_controls_to_player(enable_punch=False,
enable_bomb=True,
enable_pickup=False)
# Move to the stand position and add a flash of light.
spaz.handlemessage(
bs.StandMessage(
position,
angle if angle is not None else random.uniform(0, 360)))
self._spawn_sound.play(1, position=spaz.node.position)
light = bs.newnode('light', attrs={'color': light_color})
spaz.node.connectattr('position', light, 'position')
animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
bs.timer(0.5, light.delete)
return spaz
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
super().handlemessage(msg)
curtime = bs.time()
# Record the player's moment of death.
# assert isinstance(msg.spaz.player
msg.getplayer(Player).death_time = curtime
# In co-op mode, end the game the instant everyone dies
# (more accurate looking).
# In teams/ffa, allow a one-second fudge-factor so we can
# get more draws if players die basically at the same time.
if isinstance(self.session, bs.CoopSession):
# Teams will still show up if we check now.. check in
# the next cycle.
babase.pushcall(self._check_end_game)
# Also record this for a final setting of the clock.
self._last_player_death_time = curtime
else:
bs.timer(1.0, self._check_end_game)
else:
# Default handler:
return super().handlemessage(msg)
return None
def _check_end_game(self) -> None:
living_team_count = 0
for team in self.teams:
for player in team.players:
if player.is_alive():
living_team_count += 1
break
# In co-op, we go till everyone is dead.. otherwise we go
# until one team remains.
if isinstance(self.session, bs.CoopSession):
if living_team_count <= 0:
self.end_game()
else:
if living_team_count <= 0:
self.end_game()
def end_game(self) -> None:
cur_time = bs.time()
assert self._timer is not None
start_time = self._timer.getstarttime()
# Mark death-time as now for any still-living players
# and award players points for how long they lasted.
# (these per-player scores are only meaningful in team-games)
for team in self.teams:
for player in team.players:
survived = False
if player.notIn:
player.death_time = 0
# Throw an extra fudge factor in so teams that
# didn't die come out ahead of teams that did.
if player.death_time is None:
survived = True
player.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.death_time - self._timer.getstarttime())
if survived:
score += 50 # A bit extra for survivors.
self.stats.player_scored(player, score, screenmessage=False)
# Stop updating our time text, and set the final time to match
# exactly when our last guy died.
self._timer.stop(endtime=self._last_player_death_time)
# Ok now calc game results: set a score for each team and then tell
# the game to end.
results = bs.GameResults()
# Remember that 'free-for-all' mode is simply a special form
# of 'teams' mode where each player gets their own team, so we can
# just always deal in teams and have all cases covered.
for team in self.teams:
# Set the team score to the max time survived by any player on
# that team.
longest_life = 0.0
for player in team.players:
assert player.death_time is not None
longest_life = max(longest_life,
player.death_time - start_time)
# Submit the score value in milliseconds.
results.set_team_score(team, int(1000.0 * longest_life))
self.end(results=results)

View file

@ -0,0 +1,493 @@
# Ported by brostos to api 8
# Tool used to make porting easier.(https://github.com/bombsquad-community/baport)
# Released under the MIT License. See LICENSE for details.
"""Egg game and support classes."""
# The Egg Game - throw egg as far as you can
# created in BCS (Bombsquad Consultancy Service) - opensource bombsquad mods for all
# discord.gg/ucyaesh join now and give your contribution
# The Egg game by mr.smoothy
# 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.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.flag import Flag
import math
import random
if TYPE_CHECKING:
from typing import Any, Sequence, Dict, Type, List, Optional, Union
class PuckDiedMessage:
"""Inform something that a puck has died."""
def __init__(self, puck: Puck):
self.puck = puck
class Puck(bs.Actor):
"""A lovely giant hockey puck."""
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0)):
super().__init__()
shared = SharedObjects.get()
activity = self.getactivity()
# Spawn just above the provided point.
self._spawn_pos = (position[0], position[1] + 1.0, position[2])
self.last_players_to_touch = None
self.scored = False
self.egg_mesh = bs.getmesh('egg')
self.egg_tex_1 = bs.gettexture('eggTex1')
self.egg_tex_2 = bs.gettexture('eggTex2')
self.egg_tex_3 = bs.gettexture('eggTex3')
self.eggtx = [self.egg_tex_1, self.egg_tex_2, self.egg_tex_3]
regg = random.randrange(0, 3)
assert activity is not None
assert isinstance(activity, EggGame)
pmats = [shared.object_material, activity.puck_material]
self.node = bs.newnode('prop',
delegate=self,
attrs={
'mesh': self.egg_mesh,
'color_texture': self.eggtx[regg],
'body': 'capsule',
'reflection': 'soft',
'reflection_scale': [0.2],
'shadow_size': 0.5,
'body_scale': 0.7,
'is_area_of_interest': True,
'position': self._spawn_pos,
'materials': pmats
})
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 0.7, 0.26: 0.6})
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
assert self.node
self.node.delete()
activity = self._activity()
if activity and not msg.immediate:
activity.handlemessage(PuckDiedMessage(self))
# If we go out of bounds, move back to where we started.
elif isinstance(msg, bs.OutOfBoundsMessage):
assert self.node
self.node.position = self._spawn_pos
elif isinstance(msg, bs.HitMessage):
assert self.node
assert msg.force_direction is not None
self.node.handlemessage(
'impulse', msg.pos[0], msg.pos[1], msg.pos[2], msg.velocity[0],
msg.velocity[1], msg.velocity[2], 1.0 * msg.magnitude,
1.0 * msg.velocity_magnitude, msg.radius, 0,
msg.force_direction[0], msg.force_direction[1],
msg.force_direction[2])
# If this hit came from a player, log them as the last to touch us.
s_player = msg.get_source_player(Player)
if s_player is not None:
activity = self._activity()
if activity:
if s_player in activity.players:
self.last_players_to_touch = s_player
else:
super().handlemessage(msg)
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export bascenev1.GameActivity
class EggGame(bs.TeamGameActivity[Player, Team]):
"""Egg game."""
name = 'Epic Egg Game'
description = 'Score some goals.'
available_settings = [
bs.IntSetting(
'Score to Win',
min_value=1,
default=1,
increment=1,
),
bs.IntChoiceSetting(
'Time Limit',
choices=[
('None', 0),
('40 Seconds', 40),
('1 Minute', 60),
('2 Minutes', 120),
('5 Minutes', 300),
('10 Minutes', 600),
('20 Minutes', 1200),
],
default=0,
),
bs.FloatChoiceSetting(
'Respawn Times',
choices=[
('Shorter', 0.1),
('Short', 0.5),
('Normal', 1.0),
('Long', 2.0),
('Longer', 4.0),
],
default=1.0,
),
]
default_music = bs.MusicType.HOCKEY
@classmethod
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return bs.app.classic.getmaps('football')
def __init__(self, settings: dict):
super().__init__(settings)
shared = SharedObjects.get()
self.slow_motion = True
self._scoreboard = Scoreboard()
self._cheer_sound = bui.getsound('cheer')
self._chant_sound = bui.getsound('crowdChant')
self._foghorn_sound = bui.getsound('foghorn')
self._swipsound = bui.getsound('swip')
self._whistle_sound = bui.getsound('refWhistle')
self.puck_mesh = bs.getmesh('bomb')
self.puck_tex = bs.gettexture('landMine')
self.puck_scored_tex = bs.gettexture('landMineLit')
self._puck_sound = bui.getsound('metalHit')
self.puck_material = bs.Material()
self._fake_wall_material = bs.Material()
self.HIGHEST = 0
self._fake_wall_material.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', True)
))
self.puck_material.add_actions(actions=(('modify_part_collision',
'friction', 0.5)))
self.puck_material.add_actions(conditions=('they_have_material',
shared.pickup_material),
actions=('modify_part_collision',
'collide', True))
self.puck_material.add_actions(
conditions=(
('we_are_younger_than', 100),
'and',
('they_have_material', shared.object_material),
),
actions=('modify_node_collision', 'collide', False),
)
# self.puck_material.add_actions(conditions=('they_have_material',
# shared.footing_material),
# actions=('impact_sound',
# self._puck_sound, 0.2, 5))
# Keep track of which player last touched the puck
self.puck_material.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(('call', 'at_connect',
self._handle_puck_player_collide), ))
# We want the puck to kill powerups; not get stopped by them
self.puck_material.add_actions(
conditions=('they_have_material',
PowerupBoxFactory.get().powerup_material),
actions=(('modify_part_collision', 'physical', False),
('message', 'their_node', 'at_connect', bs.DieMessage())))
# self.puck_material.add_actions(
# conditions=('they_have_material',shared.footing_material)
# actions=(('modify_part_collision', 'collide',
# True), ('modify_part_collision', 'physical', True),
# ('call', 'at_connect', self._handle_egg_collision))
# )
self._score_region_material = bs.Material()
self._score_region_material.add_actions(
conditions=('they_have_material', self.puck_material),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('call', 'at_connect', self._handle_score)))
self.main_ground_material = bs.Material()
self.main_ground_material.add_actions(
conditions=('they_have_material', self.puck_material),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('call', 'at_connect', self._handle_egg_collision)))
self._puck_spawn_pos: Optional[Sequence[float]] = None
self._score_regions: Optional[List[bs.NodeActor]] = None
self._puck: Optional[Puck] = None
self._pucks = []
self._score_to_win = int(settings['Score to Win'])
self._time_limit = float(settings['Time Limit'])
def get_instance_description(self) -> Union[str, Sequence]:
return "Throw Egg as far u can"
def get_instance_description_short(self) -> Union[str, Sequence]:
return "Throw Egg as far u can"
def on_begin(self) -> None:
super().on_begin()
if self._time_limit == 0.0:
self._time_limit = 60
self.setup_standard_time_limit(self._time_limit)
# self.setup_standard_powerup_drops()
self._puck_spawn_pos = self.map.get_flag_position(None)
self._spawn_puck()
self._spawn_puck()
self._spawn_puck()
self._spawn_puck()
self._spawn_puck()
# Set up the two score regions.
defs = self.map.defs
self._score_regions = []
pos = (11.88630542755127, 0.3009839951992035, 1.33331298828125)
# mat=bs.Material()
# mat.add_actions(
# actions=( ('modify_part_collision','physical',True),
# ('modify_part_collision','collide',True))
# )
# self._score_regions.append(
# bs.NodeActor(
# bs.newnode('region',
# attrs={
# 'position': pos,
# 'scale': (2,3,5),
# 'type': 'box',
# 'materials': [self._score_region_material]
# })))
# pos=(-11.88630542755127, 0.3009839951992035, 1.33331298828125)
# self._score_regions.append(
# bs.NodeActor(
# bs.newnode('region',
# attrs={
# 'position': pos,
# 'scale': (2,3,5),
# 'type': 'box',
# 'materials': [self._score_region_material]
# })))
self._score_regions.append(
bs.NodeActor(
bs.newnode('region',
attrs={
'position': (-9.21, defs.boxes['goal2'][0:3][1], defs.boxes['goal2'][0:3][2]),
'scale': defs.boxes['goal2'][6:9],
'type': 'box',
'materials': (self._fake_wall_material, )
})))
pos = (0, 0.1, -5)
self.main_ground = bs.newnode('region', attrs={'position': pos, 'scale': (
25, 0.001, 22), 'type': 'box', 'materials': [self.main_ground_material]})
self._update_scoreboard()
self._chant_sound.play()
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
def _handle_puck_player_collide(self) -> None:
collision = bs.getcollision()
try:
puck = collision.sourcenode.getdelegate(Puck, True)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(
Player, True)
except bs.NotFoundError:
return
puck.last_players_to_touch = player
def _kill_puck(self) -> None:
self._puck = None
def _handle_egg_collision(self) -> None:
no = bs.getcollision().opposingnode
pos = no.position
egg = no.getdelegate(Puck)
source_player = egg.last_players_to_touch
if source_player == None or pos[0] < -8 or not source_player.node.exists():
return
try:
col = source_player.team.color
self.flagg = Flag(pos, touchable=False, color=col).autoretain()
self.flagg.is_area_of_interest = True
player_pos = source_player.node.position
distance = math.sqrt(pow(player_pos[0]-pos[0], 2) + pow(player_pos[2]-pos[2], 2))
dis_mark = bs.newnode('text',
attrs={
'text': str(round(distance, 2))+"m",
'in_world': True,
'scale': 0.02,
'h_align': 'center',
'position': (pos[0], 1.6, pos[2]),
'color': col
})
bs.animate(dis_mark, 'scale', {
0.0: 0, 0.5: 0.01
})
if distance > self.HIGHEST:
self.HIGHEST = distance
self.stats.player_scored(
source_player,
10,
big_message=False)
no.delete()
bs.timer(2, self._spawn_puck)
source_player.team.score = int(distance)
except ():
pass
def spawn_player(self, player: Player) -> bs.Actor:
zoo = random.randrange(-4, 5)
pos = (-11.204887390136719, 0.2998693287372589, zoo)
spaz = self.spawn_player_spaz(
player, position=pos, angle=90)
assert spaz.node
# Prevent controlling of characters before the start of the race.
return spaz
def _handle_score(self) -> None:
"""A point has been scored."""
assert self._puck is not None
assert self._score_regions is not None
# Our puck might stick around for a second or two
# we don't want it to be able to score again.
if self._puck.scored:
return
region = bs.getcollision().sourcenode
index = 0
for index in range(len(self._score_regions)):
if region == self._score_regions[index].node:
break
for team in self.teams:
if team.id == index:
scoring_team = team
team.score += 1
# Tell all players to celebrate.
for player in team.players:
if player.actor:
player.actor.handlemessage(bs.CelebrateMessage(2.0))
# If we've got the player from the scoring team that last
# touched us, give them points.
if (scoring_team.id in self._puck.last_players_to_touch
and self._puck.last_players_to_touch[scoring_team.id]):
self.stats.player_scored(
self._puck.last_players_to_touch[scoring_team.id],
20,
big_message=True)
# End game if we won.
if team.score >= self._score_to_win:
self.end_game()
self._foghorn_sound.play()
self._cheer_sound.play()
# self._puck.scored = True
# Change puck texture to something cool
# self._puck.node.color_texture = self.puck_scored_tex
# Kill the puck (it'll respawn itself shortly).
bs.timer(1.0, self._kill_puck)
# light = bs.newnode('light',
# attrs={
# 'position': bs.getcollision().position,
# 'height_attenuated': False,
# 'color': (1, 0, 0)
# })
# bs.animate(light, 'intensity', {0: 0, 0.5: 1, 1.0: 0}, loop=True)
# bs.timer(1.0, light.delete)
bs.cameraflash(duration=10.0)
self._update_scoreboard()
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)
def _update_scoreboard(self) -> None:
winscore = self._score_to_win
# for team in self.teams:
# self._scoreboard.set_team_value(team, team.score, winscore)
def handlemessage(self, msg: Any) -> Any:
# Respawn dead players if they're still in the game.
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior...
super().handlemessage(msg)
self.respawn_player(msg.getplayer(Player))
# Respawn dead pucks.
elif isinstance(msg, PuckDiedMessage):
if not self.has_ended():
bs.timer(3.0, self._spawn_puck)
else:
super().handlemessage(msg)
def _flash_puck_spawn(self) -> None:
# light = bs.newnode('light',
# attrs={
# 'position': self._puck_spawn_pos,
# 'height_attenuated': False,
# 'color': (1, 0, 0)
# })
# bs.animate(light, 'intensity', {0.0: 0, 0.25: 1, 0.5: 0}, loop=True)
# bs.timer(1.0, light.delete)
pass
def _spawn_puck(self) -> None:
# self._swipsound.play()
# self._whistle_sound.play()
self._flash_puck_spawn()
assert self._puck_spawn_pos is not None
zoo = random.randrange(-5, 6)
pos = (-11.204887390136719, 0.2998693287372589, zoo)
self._pucks.append(Puck(position=pos))

View file

@ -0,0 +1,144 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import random
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.spazbot import SpazBotSet, ExplodeyBot, SpazBotDiedMessage
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, Type, Dict, List, Optional
def ba_get_api_version():
return 8
def ba_get_levels():
return [bs._level.Level(
'Explodo Run',
gametype=ExplodoRunGame,
settings={},
preview_texture_name='rampagePreview'), bs._level.Level(
'Epic Explodo Run',
gametype=ExplodoRunGame,
settings={'Epic Mode': True},
preview_texture_name='rampagePreview')]
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class ExplodoRunGame(bs.TeamGameActivity[Player, Team]):
name = "Explodo Run"
description = "Run For Your Life :))"
available_settings = [bs.BoolSetting('Epic Mode', default=False)]
scoreconfig = bs.ScoreConfig(label='Time',
scoretype=bs.ScoreType.MILLISECONDS,
lower_is_better=False)
@classmethod
def get_preview_texture_name(cls) -> str:
return 'rampagePreview'
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ['Rampage']
def __init__(self, settings: dict):
settings['map'] = "Rampage"
self._epic_mode = settings.get('Epic Mode', False)
if self._epic_mode:
self.slow_motion = True
super().__init__(settings)
self._timer: Optional[OnScreenTimer] = None
self._winsound = bs.getsound('score')
self._won = False
self._bots = SpazBotSet()
self.wave = 1
self.default_music = bs.MusicType.TO_THE_DEATH
def on_begin(self) -> None:
super().on_begin()
self._timer = OnScreenTimer()
bs.timer(2.5, self._timer.start)
# Bots Hehe
bs.timer(2.5, self.street)
def street(self):
for a in range(self.wave):
p1 = random.choice([-5, -2.5, 0, 2.5, 5])
p3 = random.choice([-4.5, -4.14, -5, -3])
time = random.choice([1, 1.5, 2.5, 2])
self._bots.spawn_bot(ExplodeyBot, pos=(p1, 5.5, p3), spawn_time=time)
self.wave += 1
def botrespawn(self):
if not self._bots.have_living_bots():
self.street()
def handlemessage(self, msg: Any) -> Any:
# A player has died.
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard behavior.
self._won = True
self.end_game()
# A spaz-bot has died.
elif isinstance(msg, SpazBotDiedMessage):
# Unfortunately the bot-set will always tell us there are living
# bots if we ask here (the currently-dying bot isn't officially
# marked dead yet) ..so lets push a call into the event loop to
# check once this guy has finished dying.
babase.pushcall(self.botrespawn)
# Let the base class handle anything we don't.
else:
return super().handlemessage(msg)
return None
# When this is called, we should fill out results and end the game
# *regardless* of whether is has been won. (this may be called due
# to a tournament ending or other external reason).
def end_game(self) -> None:
# Stop our on-screen timer so players can see what they got.
assert self._timer is not None
self._timer.stop()
results = bs.GameResults()
# If we won, set our score to the elapsed time in milliseconds.
# (there should just be 1 team here since this is co-op).
# ..if we didn't win, leave scores as default (None) which means
# we lost.
if self._won:
elapsed_time_ms = int((bs.time() - self._timer.starttime) * 1000.0)
bs.cameraflash()
self._winsound.play()
for team in self.teams:
for player in team.players:
if player.actor:
player.actor.handlemessage(bs.CelebrateMessage())
results.set_team_score(team, elapsed_time_ms)
# Ends the activity.
self.end(results)

View file

@ -0,0 +1,257 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
"""For 1.7.33"""
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import random
from bascenev1lib.actor.bomb import BombFactory, Blast, ImpactMessage
from bascenev1lib.actor.onscreentimer import OnScreenTimer
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Optional, Type
def ba_get_api_version():
return 8
def ba_get_levels():
return [bs._level.Level(
'Extinction',
gametype=NewMeteorShowerGame,
settings={'Epic Mode': False},
preview_texture_name='footballStadiumPreview'),
bs._level.Level(
'Epic Extinction',
gametype=NewMeteorShowerGame,
settings={'Epic Mode': True},
preview_texture_name='footballStadiumPreview')]
class Meteor(bs.Actor):
"""A giant meteor instead of bombs."""
def __init__(self,
pos: Sequence[float] = (0.0, 1.0, 0.0),
velocity: Sequence[float] = (0.0, 0.0, 0.0)):
super().__init__()
shared = SharedObjects.get()
factory = BombFactory.get()
materials = (shared.object_material,
factory.impact_blast_material)
self.pos = (pos[0], pos[1], pos[2])
self.velocity = (velocity[0], velocity[1], velocity[2])
self.node = bs.newnode(
'prop',
delegate=self,
attrs={
'position': self.pos,
'velocity': self.velocity,
'mesh': factory.sticky_bomb_mesh,
'color_texture': factory.tnt_tex,
'mesh_scale': 3.0,
'body_scale': 2.99,
'body': 'sphere',
'shadow_size': 0.5,
'reflection': 'soft',
'reflection_scale': [0.45],
'materials': materials
})
def explode(self) -> None:
Blast(position=self.node.position,
velocity=self.node.velocity,
blast_type='tnt',
blast_radius=2.0)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:
self.node.delete()
elif isinstance(msg, ImpactMessage):
self.explode()
self.handlemessage(bs.DieMessage())
else:
super().handlemessage(msg)
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self):
super().__init__()
self.death_time: Optional[float] = None
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class NewMeteorShowerGame(bs.TeamGameActivity[Player, Team]):
"""Minigame by Jetz."""
name = 'Extinction'
description = 'Survive the Extinction.'
available_settings = [
bs.BoolSetting('Epic Mode', default=False)]
announce_player_deaths = True
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return ['Football Stadium']
@classmethod
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
return (issubclass(sessiontype, bs.FreeForAllSession)
or issubclass(sessiontype, bs.DualTeamSession))
def __init__(self, settings: dict):
super().__init__(settings)
self._epic_mode = bool(settings['Epic Mode'])
self._last_player_death_time: Optiobal[float] = None
self._meteor_time = 2.0
self._timer: Optional[OnScreenTimer] = None
self.default_music = (bs.MusicType.EPIC
if self._epic_mode else bs.MusicType.SURVIVAL)
if self._epic_mode:
self.slow_motion = True
def on_begin(self) -> None:
super().on_begin()
delay = 5.0 if len(self.players) > 2 else 2.5
if self._epic_mode:
delay *= 0.25
bs.timer(delay, self._decrement_meteor_time, repeat=True)
delay = 3.0
if self._epic_mode:
delay *= 0.25
bs.timer(delay, self._set_meteor_timer)
self._timer = OnScreenTimer()
self._timer.start()
self._check_end_game()
def on_player_join(self, player: Player) -> None:
if self.has_begun():
bs.broadcastmessage(
babase.Lstr(resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(0, 1, 0),
)
assert self._timer is not None
player.death_time = self._timer.getstarttime()
return
self.spawn_player(player)
def spawn_player(self, player: Player) -> None:
spaz = self.spawn_player_spaz(player)
spaz.connect_controls_to_player(enable_punch=False,
enable_pickup=False,
enable_bomb=False,
enable_jump=False)
spaz.play_big_death_sound = True
return spaz
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
self._check_end_game()
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
curtime = bs.time()
msg.getplayer(Player).death_time = curtime
bs.timer(1.0, self._check_end_game)
else:
return super().handlemessage(msg)
def _spawn_meteors(self) -> None:
pos = (random.randint(-6, 7), 12,
random.uniform(-2, 1))
velocity = (random.randint(-11, 11), 0,
random.uniform(-5, 5))
Meteor(pos=pos, velocity=velocity).autoretain()
def _spawn_meteors_cluster(self) -> None:
delay = 0.0
for _i in range(random.randrange(1, 3)):
bs.timer(delay, self._spawn_meteors)
delay += 1
self._set_meteor_timer()
def _decrement_meteor_time(self) -> None:
self._meteor_time = max(0.01, self._meteor_time * 0.9)
def _set_meteor_timer(self) -> None:
bs.timer((1.0 + 0.2 * random.random()) * self._meteor_time,
self._spawn_meteors_cluster)
def _check_end_game(self) -> None:
living_team_count = 0
for team in self.teams:
for player in team.players:
if player.is_alive():
living_team_count += 1
break
if isinstance(self.session, bs.CoopSession):
if living_team_count <= 0:
self.end_game()
else:
if living_team_count <= 1:
self.end_game()
def end_game(self) -> None:
cur_time = bs.time()
assert self._timer is not None
start_time = self._timer.getstarttime()
for team in self.teams:
for player in team.players:
survived = False
if player.death_time is None:
survived = True
player.death_time = cur_time + 1
score = int(player.death_time - self._timer.getstarttime())
if survived:
score += 50
self.stats.player_scored(player, score, screenmessage=False)
self._timer.stop(endtime=self._last_player_death_time)
results = bs.GameResults()
for team in self.teams:
longest_life = 0.0
for player in team.players:
assert player.death_time is not None
longest_life = max(longest_life,
player.death_time - start_time)
results.set_team_score(team, int(1000.0 * longest_life))
self.end(results=results)

View file

@ -0,0 +1,339 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
# - - - - - - - - - - - - - - - - - - - - -
# - Fat-Pigs! by Zacker Tz || Zacker#5505 -
# - Version 0.01 :v -
# - - - - - - - - - - - - - - - - - - - - -
from __future__ import annotations
from typing import TYPE_CHECKING
import random
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.onscreentimer import OnScreenTimer
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
if TYPE_CHECKING:
from typing import Any, Union, Sequence, Optional
# - - - - - - - Mini - Settings - - - - - - - - - - - - - - - - #
zkBombs_limit = 3 # Number of bombs you can use | Default = 3
zkPunch = False # Enable/Disable punchs | Default = False
zkPickup = False # Enable/Disable pickup | Default = False
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export bascenev1.GameActivity
class FatPigs(bs.TeamGameActivity[Player, Team]):
"""A game type based on acquiring kills."""
name = 'Fat-Pigs!'
description = 'Survive...'
# 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=0.25,
),
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 ['Courtyard', 'Rampage', 'Monkey Face', 'Lake Frigid', 'Step Right Up']
def __init__(self, settings: dict):
super().__init__(settings)
self._scoreboard = Scoreboard()
self._meteor_time = 2.0
self._score_to_win: Optional[int] = None
self._dingsound = bs.getsound('dingSmall')
self._epic_mode = bool(settings['Epic Mode'])
# self._text_credit = bool(settings['Credits'])
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)
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()
# Ambiente
gnode = bs.getactivity().globalsnode
gnode.tint = (0.8, 1.2, 0.8)
gnode.ambient_color = (0.7, 1.0, 0.6)
gnode.vignette_outer = (0.4, 0.6, 0.4) # C
# gnode.vignette_inner = (0.9, 0.9, 0.9)
# 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()
delay = 5.0 if len(self.players) > 2 else 2.5
if self._epic_mode:
delay *= 0.25
bs.timer(delay, self._decrement_meteor_time, repeat=False)
# Kick off the first wave in a few seconds.
delay = 3.0
if self._epic_mode:
delay *= 0.25
bs.timer(delay, self._set_meteor_timer)
# self._timer = OnScreenTimer()
# self._timer.start()
# Check for immediate end (if we've only got 1 player, etc).
bs.timer(5.0, self._check_end_game)
t = bs.newnode('text',
attrs={'text': "Minigame by Zacker Tz",
'scale': 0.7,
'position': (0.001, 625),
'shadow': 0.5,
'opacity': 0.7,
'flatness': 1.2,
'color': (0.6, 1, 0.6),
'h_align': 'center',
'v_attach': 'bottom'})
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(player)
# Let's reconnect this player's controls to this
# spaz but *without* the ability to attack or pick stuff up.
spaz.connect_controls_to_player(enable_punch=zkPunch,
enable_bomb=True,
enable_pickup=zkPickup)
spaz.bomb_count = zkBombs_limit
spaz._max_bomb_count = zkBombs_limit
spaz.bomb_type_default = 'sticky'
spaz.bomb_type = 'sticky'
# cerdo gordo
spaz.node.color_mask_texture = bs.gettexture('melColorMask')
spaz.node.color_texture = bs.gettexture('melColor')
spaz.node.head_mesh = bs.getmesh('melHead')
spaz.node.hand_mesh = bs.getmesh('melHand')
spaz.node.torso_mesh = bs.getmesh('melTorso')
spaz.node.pelvis_mesh = bs.getmesh('kronkPelvis')
spaz.node.upper_arm_mesh = bs.getmesh('melUpperArm')
spaz.node.forearm_mesh = bs.getmesh('melForeArm')
spaz.node.upper_leg_mesh = bs.getmesh('melUpperLeg')
spaz.node.lower_leg_mesh = bs.getmesh('melLowerLeg')
spaz.node.toes_mesh = bs.getmesh('melToes')
spaz.node.style = 'mel'
# Sounds cerdo gordo
mel_sounds = [bs.getsound('mel01'), bs.getsound('mel02'), bs.getsound('mel03'), bs.getsound('mel04'), bs.getsound('mel05'),
bs.getsound('mel06'), bs.getsound('mel07'), bs.getsound('mel08'), bs.getsound('mel09'), bs.getsound('mel10')]
spaz.node.jump_sounds = mel_sounds
spaz.node.attack_sounds = mel_sounds
spaz.node.impact_sounds = mel_sounds
spaz.node.pickup_sounds = mel_sounds
spaz.node.death_sounds = [bs.getsound('melDeath01')]
spaz.node.fall_sounds = [bs.getsound('melFall01')]
def _set_meteor_timer(self) -> None:
bs.timer((1.0 + 0.2 * random.random()) * self._meteor_time,
self._drop_bomb_cluster)
def _drop_bomb_cluster(self) -> None:
# Random note: code like this is a handy way to plot out extents
# and debug things.
loc_test = False
if loc_test:
bs.newnode('locator', attrs={'position': (8, 6, -5.5)})
bs.newnode('locator', attrs={'position': (8, 6, -2.3)})
bs.newnode('locator', attrs={'position': (-7.3, 6, -5.5)})
bs.newnode('locator', attrs={'position': (-7.3, 6, -2.3)})
# Drop several bombs in series.
delay = 0.0
for _i in range(random.randrange(1, 3)):
# Drop them somewhere within our bounds with velocity pointing
# toward the opposite side.
pos = (-7.3 + 15.3 * random.random(), 11,
-5.5 + 2.1 * random.random())
dropdir = (-1.0 if pos[0] > 0 else 1.0)
vel = ((-5.0 + random.random() * 30.0) * dropdir, -4.0, 0)
bs.timer(delay, babase.Call(self._drop_bomb, pos, vel))
delay += 0.1
self._set_meteor_timer()
def _drop_bomb(self, position: Sequence[float],
velocity: Sequence[float]) -> None:
Bomb(position=position, velocity=velocity, bomb_type='sticky').autoretain()
def _decrement_meteor_time(self) -> None:
self._meteor_time = max(0.01, self._meteor_time * 0.9)
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 _check_end_game(self) -> None:
living_team_count = 0
for team in self.teams:
for player in team.players:
if player.is_alive():
living_team_count += 1
break
# In co-op, we go till everyone is dead.. otherwise we go
# until one team remains.
if isinstance(self.session, bs.CoopSession):
if living_team_count <= 0:
self.end_game()
else:
if living_team_count <= 1:
self.end_game()
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()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)

View file

@ -0,0 +1,615 @@
# Ported by brostos to api 8
# 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
import _babase
import json
import math
import random
from bascenev1lib.game.elimination import Icon
from bascenev1lib.actor.bomb import Bomb, Blast
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.powerupbox import PowerupBox
from bascenev1lib.actor.flag import Flag, FlagPickedUpMessage
from bascenev1lib.actor.spazbot import SpazBotSet, BrawlerBotLite, SpazBotDiedMessage
if TYPE_CHECKING:
from typing import Any, Sequence
lang = bs.app.lang.language
if lang == 'Spanish':
name = 'Día de la Bandera'
description = ('Recoge las banderas para recibir un premio.\n'
'Pero ten cuidado...')
slow_motion_deaths = 'Muertes en Cámara Lenta'
credits = 'Creado por MattZ45986 en Github | Actualizado por byANG3L and brostos'
you_were = 'Estas'
cursed_text = 'MALDITO'
run = 'CORRE'
climb_top = 'Escala a la cima'
bomb_rain = '¡LLUVIA DE BOMBAS!'
lame_guys = 'Chicos Ligeros'
jackpot = '¡PREMIO MAYOR!'
diedtxt = '¡'
diedtxt2 = ' ha sido eliminado!'
else:
name = 'Flag Day'
description = 'Pick up flags to receive a prize.\nBut beware...'
slow_motion_deaths = 'Slow Motion Deaths'
credits = 'Created by MattZ45986 on Github | Updated by byANG3L and brostos'
you_were = 'You were'
cursed_text = 'CURSED'
run = 'RUN'
climb_top = 'Climb to the top'
bomb_rain = 'BOMB RAIN!'
lame_guys = 'Lame Guys'
jackpot = '!JACKPOT!'
diedtxt = ''
diedtxt2 = ' died!'
class Icon(Icon):
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,
dead: bool = False,
):
super().__init__(player, position, scale, show_lives, show_death,
name_scale, name_maxwidth, flatness, shadow)
if dead:
self._name_text.opacity = 0.2
self.node.color = (0.7, 0.3, 0.3)
self.node.opacity = 0.2
class FlagBearer(PlayerSpaz):
def handlemessage(self, msg: Any) -> Any:
super().handlemessage(msg)
if isinstance(msg, bs.PowerupMessage):
activity = self.activity
player = self.getplayer(Player)
if not player.is_alive():
return
if activity.last_prize == 'curse':
player.team.score += 25
activity._update_scoreboard()
elif activity.last_prize == 'land_mines':
player.team.score += 15
activity._update_scoreboard()
self.connect_controls_to_player()
elif activity.last_prize == 'climb':
player.team.score += 50
activity._update_scoreboard()
if msg.poweruptype == 'health':
activity.round_timer = None
bs.timer(0.2, activity.setup_next_round)
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self) -> None:
self.dead = False
self.icons: list[Icon] = []
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export bascenev1.GameActivity
class FlagDayGame(bs.TeamGameActivity[Player, Team]):
"""A game type based on acquiring kills."""
name = name
description = description
# Print messages when players die since it matters 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.BoolSetting(slow_motion_deaths, default=True),
bs.BoolSetting('Epic Mode', default=False),
]
return settings
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (
issubclass(sessiontype, bs.CoopSession)
or 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)
self.credits()
self._scoreboard = Scoreboard()
self._dingsound = bui.getsound('dingSmall')
self._epic_mode = bool(settings['Epic Mode'])
self._slow_motion_deaths = bool(settings[slow_motion_deaths])
self.current_player: Player | None = None
self.prize_recipient: Player | None = None
self.bomb_survivor: Player | None = None
self.bad_guy_cost: int = 0
self.player_index: int = 0
self.bombs: list = []
self.queue_line: list = []
self._bots: SpazBotSet | None = None
self.light: bs.Node | None = None
self.last_prize = 'none'
self._flag: Flag | None = None
self._flag2: Flag | None = None
self._flag3: Flag | None = None
self._flag4: Flag | None = None
self._flag5: Flag | None = None
self._flag6: Flag | None = None
self._flag7: Flag | None = None
self._flag8: Flag | None = None
self.set = False
self.round_timer: bs.Timer | None = None
self.give_points_timer: bs.Timer | None = None
self._jackpot_sound = bui.getsound('achievement')
self._round_sound = bui.getsound('powerup01')
self._dingsound = bui.getsound('dingSmall')
# 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
)
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
def on_player_leave(self, player: Player) -> None:
if player is self.current_player:
self.setup_next_round()
self._check_end_game()
super().on_player_leave(player)
self.queue_line.remove(player)
self._update_icons()
def on_begin(self) -> None:
super().on_begin()
for player in self.players:
if player.actor:
player.actor.handlemessage(bs.DieMessage())
player.actor.node.delete()
self.queue_line.append(player)
self.spawn_player_spaz(
self.queue_line[self.player_index % len(self.queue_line)],
(0.0, 3.0, -2.0))
self.current_player = self.queue_line[0]
# Declare a set of bots (enemies) that we will use later
self._bots = SpazBotSet()
self.reset_flags()
self._update_icons()
self._update_scoreboard()
def credits(self) -> None:
bs.newnode(
'text',
attrs={
'v_attach': 'bottom',
'h_align': 'center',
'vr_depth': 0,
'color': (0, 0.2, 0),
'shadow': 1.0,
'flatness': 1.0,
'position': (0, 0),
'scale': 0.8,
'text': credits
})
def _update_icons(self) -> None:
# pylint: disable=too-many-branches
for player in self.queue_line:
player.icons = []
if player == self.current_player:
xval = 0
x_offs = -78
player.icons.append(
Icon(player,
position=(xval, 65),
scale=1.0,
name_maxwidth=130,
name_scale=0.8,
flatness=0.0,
shadow=0.5,
show_death=True,
show_lives=False))
elif player.dead:
xval = 65
x_offs = 78
player.icons.append(
Icon(player,
position=(xval, 50),
scale=0.5,
name_maxwidth=75,
name_scale=1.0,
flatness=1.0,
shadow=1.0,
show_death=False,
show_lives=False,
dead=True))
xval += x_offs * 0.56
else:
xval = -65
x_offs = 78
player.icons.append(
Icon(player,
position=(xval, 50),
scale=0.5,
name_maxwidth=75,
name_scale=1.0,
flatness=1.0,
shadow=1.0,
show_death=False,
show_lives=False))
xval -= x_offs * 0.56
def give_prize(self, prize: int) -> None:
if prize == 1:
# Curse him aka make him blow up in 5 seconds
# give them a nice message
bs.broadcastmessage(you_were, color=(0.1, 0.1, 0.1))
bs.broadcastmessage(cursed_text, color=(1.0, 0.0, 0.0))
self.make_health_box((0.0, 0.0, 0.0))
self.last_prize = 'curse'
self.prize_recipient.actor.curse()
# bs.timer(5.5, self.setup_next_round)
if prize == 2:
self.setup_rof()
bs.broadcastmessage(run, color=(1.0, 0.2, 0.1))
self.last_prize = 'ring_of_fire'
if prize == 3:
self.last_prize = 'climb'
self.light = bs.newnode(
'locator',
attrs={
'shape': 'circle',
'position': (0.0, 3.0, -9.0),
'color': (1.0, 1.0, 1.0),
'opacity': 1.0,
'draw_beauty': True,
'additive': True
})
bs.broadcastmessage(climb_top, color=(0.5, 0.5, 0.5))
bs.timer(3.0, babase.Call(self.make_health_box, (0.0, 6.0, -9.0)))
self.round_timer = bs.Timer(10.0, self.setup_next_round)
if prize == 4:
self.last_prize = 'land_mines'
self.make_health_box((6.0, 5.0, -2.0))
self.make_land_mines()
self.prize_recipient.actor.connect_controls_to_player(
enable_bomb=False)
self.prize_recipient.actor.node.handlemessage(
bs.StandMessage(position=(-6.0, 3.0, -2.0)))
self.round_timer = bs.Timer(7.0, self.setup_next_round)
if prize == 5:
# Make it rain bombs
self.bomb_survivor = self.prize_recipient
bs.broadcastmessage(bomb_rain, color=(1.0, 0.5, 0.16))
# Set positions for the bombs to drop
for bzz in range(-5, 6):
for azz in range(-5, 2):
# for each position make a bomb drop there
self.make_bomb(bzz, azz)
self.give_points_timer = bs.Timer(3.3, self.give_points)
self.last_prize = 'bombrain'
if prize == 6:
self.setup_br()
self.bomb_survivor = self.prize_recipient
self.give_points_timer = bs.Timer(7.0, self.give_points)
self.last_prize = 'bombroad'
if prize == 7:
# makes killing a bad guy worth ten points
self.bad_guy_cost = 2
bs.broadcastmessage(lame_guys, color=(1.0, 0.5, 0.16))
# makes a set of nine positions
for a in range(-1, 2):
for b in range(-3, 0):
# and spawns one in each position
self._bots.spawn_bot(BrawlerBotLite, pos=(a, 2.5, b))
# and we give our player boxing gloves and a shield
self._player.equip_boxing_gloves()
self._player.equip_shields()
self.last_prize = 'lameguys'
if prize == 8:
self._jackpot_sound.play()
bs.broadcastmessage(jackpot, color=(1.0, 0.0, 0.0))
bs.broadcastmessage(jackpot, color=(0.0, 1.0, 0.0))
bs.broadcastmessage(jackpot, color=(0.0, 0.0, 1.0))
team = self.prize_recipient.team
# GIVE THEM A WHOPPING 50 POINTS!!!
team.score += 50
# and update the scores
self._update_scoreboard()
self.last_prize = 'jackpot'
bs.timer(2.0, self.setup_next_round)
def setup_next_round(self) -> None:
if self._slow_motion_deaths:
bs.getactivity().globalsnode.slow_motion = False
if self.set:
return
if self.light:
self.light.delete()
for bomb in self.bombs:
bomb.handlemessage(bs.DieMessage())
self.kill_flags()
self._bots.clear()
self.reset_flags()
self.current_player.actor.handlemessage(
bs.DieMessage(how='game'))
self.current_player.actor.node.delete()
c = 0
self.player_index += 1
self.player_index %= len(self.queue_line)
if len(self.queue_line) > 0:
while self.queue_line[self.player_index].dead:
if c > len(self.queue_line):
return
self.player_index += 1
self.player_index %= len(self.queue_line)
c += 1
self.spawn_player_spaz(
self.queue_line[self.player_index], (0.0, 3.0, -2.0))
self.current_player = self.queue_line[self.player_index]
self.last_prize = 'none'
self._update_icons()
def check_bots(self) -> None:
if not self._bots.have_living_bots():
self.setup_next_round()
def make_land_mines(self) -> None:
self.bombs = []
for i in range(-11, 7):
self.bombs.append(Bomb(
position=(0.0, 6.0, i/2.0),
bomb_type='land_mine',
blast_radius=2.0))
self.bombs[i+10].arm()
def give_points(self) -> None:
if self.bomb_survivor is not None and self.bomb_survivor.is_alive():
self.bomb_survivor.team.score += 20
self._update_scoreboard()
self.round_timer = bs.Timer(1.0, self.setup_next_round)
def make_health_box(self, position: Sequence[float]) -> None:
if position == (0.0, 3.0, 0.0):
position = (random.randint(-6, 6), 6, random.randint(-6, 4))
elif position == (0, 0, 0):
position = random.choice(
((-7, 6, -5), (7, 6, -5), (-7, 6, 1), (7, 6, 1)))
self.health_box = PowerupBox(
position=position, poweruptype='health').autoretain()
# called in prize #5
def make_bomb(self, xpos: float, zpos: float) -> None:
# makes a bomb at the given position then auto-retains it aka:
# makes sure it doesn't disappear because there is no reference to it
self.bombs.append(Bomb(position=(xpos, 12, zpos)))
def setup_br(self) -> None:
self.make_bomb_row(6)
self.prize_recipient.actor.handlemessage(
bs.StandMessage(position=(6.0, 3.0, -2.0)))
def make_bomb_row(self, num: int) -> None:
if not self.prize_recipient.is_alive():
return
if num == 0:
self.round_timer = bs.Timer(1.0, self.setup_next_round)
return
for i in range(-11, 7):
self.bombs.append(
Bomb(position=(-3, 3, i/2.0),
velocity=(12, 0.0, 0.0),
bomb_type='normal',
blast_radius=1.2))
bs.timer(1.0, babase.Call(self.make_bomb_row, num-1))
def setup_rof(self) -> None:
self.make_blast_ring(10)
self.prize_recipient.actor.handlemessage(
bs.StandMessage(position=(0.0, 3.0, -2.0)))
def make_blast_ring(self, length: float) -> None:
if not self.prize_recipient.is_alive():
return
if length == 0:
self.setup_next_round()
self.prize_recipient.team.score += 50
self._update_scoreboard()
return
for angle in range(0, 360, 45):
angle += random.randint(0, 45)
angle %= 360
x = length * math.cos(math.radians(angle))
z = length * math.sin(math.radians(angle))
blast = Blast(position=(x, 2.2, z-2), blast_radius=3.5)
bs.timer(0.75, babase.Call(self.make_blast_ring, length-1))
# a method to remake the flags
def reset_flags(self) -> None:
# remake the flags
self._flag = Flag(
position=(0.0, 3.0, 1.0), touchable=True, color=(0.0, 0.0, 1.0))
self._flag2 = Flag(
position=(0.0, 3.0, -5.0), touchable=True, color=(1.0, 0.0, 0.0))
self._flag3 = Flag(
position=(3.0, 3.0, -2.0), touchable=True, color=(0.0, 1.0, 0.0))
self._flag4 = Flag(
position=(-3.0, 3.0, -2.0), touchable=True, color=(1.0, 1.0, 1.0))
self._flag5 = Flag(
position=(1.8, 3.0, 0.2), touchable=True, color=(0.0, 1.0, 1.0))
self._flag6 = Flag(
position=(-1.8, 3.0, 0.2), touchable=True, color=(1.0, 0.0, 1.0))
self._flag7 = Flag(
position=(1.8, 3.0, -3.8), touchable=True, color=(1.0, 1.0, 0.0))
self._flag8 = Flag(
position=(-1.8, 3.0, -3.8), touchable=True, color=(0.0, 0.0, 0.0))
# a method to kill the flags
def kill_flags(self) -> None:
# destroy all the flags by erasing all references to them,
# indicated by None similar to null
self._flag.node.delete()
self._flag2.node.delete()
self._flag3.node.delete()
self._flag4.node.delete()
self._flag5.node.delete() # 132, 210 ,12
self._flag6.node.delete()
self._flag7.node.delete()
self._flag8.node.delete()
def _check_end_game(self) -> None:
for player in self.queue_line:
if not player.dead:
return
self.end_game()
def spawn_player_spaz(
self,
player: PlayerT,
position: Sequence[float] = (0, 0, 0),
angle: float | None = None,
) -> PlayerSpaz:
from babase import _math
from bascenev1._gameutils import animate
from bascenev1._coopsession import CoopSession
angle = None
name = player.getname()
color = player.color
highlight = player.highlight
light_color = _math.normalized_color(color)
display_color = babase.safecolor(color, target_intensity=0.75)
spaz = FlagBearer(color=color,
highlight=highlight,
character=player.character,
player=player)
player.actor = spaz
assert spaz.node
spaz.node.name = name
spaz.node.name_color = display_color
spaz.connect_controls_to_player()
# Move to the stand position and add a flash of light.
spaz.handlemessage(
bs.StandMessage(
position,
angle if angle is not None else random.uniform(0, 360)))
self._spawn_sound.play(1, position=spaz.node.position)
light = bs.newnode('light', attrs={'color': light_color})
spaz.node.connectattr('position', light, 'position')
animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
bs.timer(0.5, light.delete)
return spaz
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# give them a nice farewell
if bs.time() < 0.5:
return
if msg.how == 'game':
return
player = msg.getplayer(Player)
bs.broadcastmessage(
diedtxt + str(player.getname()) + diedtxt2, color=player.color)
player.dead = True
if player is self.current_player:
self.round_timer = None
self.give_points_timer = None
if not msg.how is bs.DeathType.FALL:
if self._slow_motion_deaths:
bs.getactivity().globalsnode.slow_motion = True
time = 0.5
else:
time = 0.01
# check to see if we can end the game
self._check_end_game()
bs.timer(time, self.setup_next_round)
elif isinstance(msg, FlagPickedUpMessage):
msg.flag.last_player_to_hold = msg.node.getdelegate(
FlagBearer, True
).getplayer(Player, True)
self._player = msg.node.getdelegate(
FlagBearer, True
)
self.prize_recipient = msg.node.getdelegate(
FlagBearer, True
).getplayer(Player, True)
self.kill_flags()
self.give_prize(random.randint(1, 8))
self._round_sound.play()
self.current_player = self.prize_recipient
elif isinstance(msg, SpazBotDiedMessage):
# find out which team the last person to hold a flag was on
team = self.prize_recipient.team
# give them their points
team.score += self.bad_guy_cost
self._dingsound.play(0.5)
# update the scores
for team in self.teams:
self._scoreboard.set_team_value(team, team.score)
bs.timer(0.3, self.check_bots)
return None
def _update_scoreboard(self) -> None:
for player in self.queue_line:
if not player.dead:
if player.team.score > 0:
self._dingsound.play()
self._scoreboard.set_team_value(player.team, player.team.score)
def end_game(self) -> None:
if self.set:
return
self.set = True
results = bs.GameResults()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)

View file

@ -0,0 +1,35 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# Made by MattZ45986 on GitHub
# Ported by: Freaku / @[Just] Freak#4999
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.game.elimination import EliminationGame
# ba_meta require api 8
# ba_meta export bascenev1.GameActivity
class GFGame(EliminationGame):
name = 'Gravity Falls'
def spawn_player(self, player):
actor = self.spawn_player_spaz(player, (0, 5, 0))
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()
bs.timer(1, babase.Call(self.raise_player, player))
return actor
def raise_player(self, player):
if player.is_alive():
try:
player.actor.node.handlemessage(
"impulse", player.actor.node.position[0], player.actor.node.position[1]+.5, player.actor.node.position[2], 0, 5, 0, 3, 10, 0, 0, 0, 5, 0)
except:
pass
bs.timer(0.05, babase.Call(self.raise_player, player))

File diff suppressed because it is too large Load diff

View file

@ -55,11 +55,11 @@ else:
def ba_get_api_version():
return 6
return 8
def ba_get_levels():
return [babase._level.Level(
return [bs._level.Level(
name,
gametype=Infection,
settings={},

View file

@ -0,0 +1,139 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
# Copy pasted from ExplodoRun by Blitz,just edited Bots and map 😝
from __future__ import annotations
import random
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.spazbot import SpazBotSet, ChargerBot, SpazBotDiedMessage
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, Type, Dict, List, Optional
## MoreMinigames.py support ##
def ba_get_api_version():
return 8
def ba_get_levels():
return [bs._level.Level(
'Infinite Ninjas', gametype=InfiniteNinjasGame,
settings={},
preview_texture_name='footballStadiumPreview'),
bs._level.Level(
'Epic Infinite Ninjas', gametype=InfiniteNinjasGame,
settings={'Epic Mode': True},
preview_texture_name='footballStadiumPreview')]
## MoreMinigames.py support ##
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class InfiniteNinjasGame(bs.TeamGameActivity[Player, Team]):
name = "Infinite Ninjas"
description = "How long can you survive from Ninjas??"
available_settings = [bs.BoolSetting('Epic Mode', default=False)]
scoreconfig = bs.ScoreConfig(label='Time',
scoretype=bs.ScoreType.MILLISECONDS,
lower_is_better=False)
default_music = bs.MusicType.TO_THE_DEATH
def __init__(self, settings: dict):
settings['map'] = "Football Stadium"
self._epic_mode = settings.get('Epic Mode', False)
if self._epic_mode:
self.slow_motion = True
super().__init__(settings)
self._timer: Optional[OnScreenTimer] = None
self._winsound = bs.getsound('score')
self._won = False
self._bots = SpazBotSet()
self.wave = 1
def on_begin(self) -> None:
super().on_begin()
self._timer = OnScreenTimer()
bs.timer(2.5, self._timer.start)
# Bots Hehe
bs.timer(2.5, self.street)
def street(self):
for a in range(self.wave):
p1 = random.choice([-5, -2.5, 0, 2.5, 5])
p3 = random.choice([-4.5, -4.14, -5, -3])
time = random.choice([1, 1.5, 2.5, 2])
self._bots.spawn_bot(ChargerBot, pos=(p1, 0.4, p3), spawn_time=time)
self.wave += 1
def botrespawn(self):
if not self._bots.have_living_bots():
self.street()
def handlemessage(self, msg: Any) -> Any:
# A player has died.
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard behavior.
self._won = True
self.end_game()
# A spaz-bot has died.
elif isinstance(msg, SpazBotDiedMessage):
# Unfortunately the bot-set will always tell us there are living
# bots if we ask here (the currently-dying bot isn't officially
# marked dead yet) ..so lets push a call into the event loop to
# check once this guy has finished dying.
babase.pushcall(self.botrespawn)
# Let the base class handle anything we don't.
else:
return super().handlemessage(msg)
return None
# When this is called, we should fill out results and end the game
# *regardless* of whether is has been won. (this may be called due
# to a tournament ending or other external reason).
def end_game(self) -> None:
# Stop our on-screen timer so players can see what they got.
assert self._timer is not None
self._timer.stop()
results = bs.GameResults()
# If we won, set our score to the elapsed time in milliseconds.
# (there should just be 1 team here since this is co-op).
# ..if we didn't win, leave scores as default (None) which means
# we lost.
if self._won:
elapsed_time_ms = int((bs.time() - self._timer.starttime) * 1000.0)
bs.cameraflash()
self._winsound.play()
for team in self.teams:
for player in team.players:
if player.actor:
player.actor.handlemessage(bs.CelebrateMessage())
results.set_team_score(team, elapsed_time_ms)
# Ends the activity.
self.end(results)

View file

@ -0,0 +1,170 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import random
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.spazbot import SpazBotSet, ChargerBot, BrawlerBotProShielded, TriggerBotProShielded, ExplodeyBot, BomberBotProShielded, SpazBotDiedMessage
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, Type, Dict, List, Optional
def ba_get_api_version():
return 6
def ba_get_levels():
return [babase._level.Level(
'Lame Fight',
gametype=LameFightGame,
settings={},
preview_texture_name='courtyardPreview')]
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class LameFightGame(bs.TeamGameActivity[Player, Team]):
name = "Lame Fight"
description = "Save World With Super Powers"
slow_motion = True
scoreconfig = bs.ScoreConfig(label='Time',
scoretype=bs.ScoreType.MILLISECONDS,
lower_is_better=True)
default_music = bs.MusicType.TO_THE_DEATH
def __init__(self, settings: dict):
settings['map'] = "Courtyard"
super().__init__(settings)
self._timer: Optional[OnScreenTimer] = None
self._winsound = bs.getsound('score')
self._won = False
self._bots = SpazBotSet()
def on_begin(self) -> None:
super().on_begin()
self._timer = OnScreenTimer()
bs.timer(4.0, self._timer.start)
# Bots Hehe
bs.timer(1.0, lambda: self._bots.spawn_bot(ChargerBot, pos=(3, 3, -2), spawn_time=3.0))
bs.timer(1.0, lambda: self._bots.spawn_bot(ChargerBot, pos=(-3, 3, -2), spawn_time=3.0))
bs.timer(1.0, lambda: self._bots.spawn_bot(ChargerBot, pos=(5, 3, -2), spawn_time=3.0))
bs.timer(1.0, lambda: self._bots.spawn_bot(ChargerBot, pos=(-5, 3, -2), spawn_time=3.0))
bs.timer(1.0, lambda: self._bots.spawn_bot(ChargerBot, pos=(0, 3, 1), spawn_time=3.0))
bs.timer(1.0, lambda: self._bots.spawn_bot(ChargerBot, pos=(0, 3, -5), spawn_time=3.0))
bs.timer(9.0, lambda: self._bots.spawn_bot(
BomberBotProShielded, pos=(-7, 5, -7.5), spawn_time=3.0))
bs.timer(9.0, lambda: self._bots.spawn_bot(
BomberBotProShielded, pos=(7, 5, -7.5), spawn_time=3.0))
bs.timer(9.0, lambda: self._bots.spawn_bot(
BomberBotProShielded, pos=(7, 5, 1.5), spawn_time=3.0))
bs.timer(9.0, lambda: self._bots.spawn_bot(
BomberBotProShielded, pos=(-7, 5, 1.5), spawn_time=3.0))
bs.timer(12.0, lambda: self._bots.spawn_bot(
TriggerBotProShielded, pos=(-1, 7, -8), spawn_time=3.0))
bs.timer(12.0, lambda: self._bots.spawn_bot(
TriggerBotProShielded, pos=(1, 7, -8), spawn_time=3.0))
bs.timer(15.0, lambda: self._bots.spawn_bot(ExplodeyBot, pos=(0, 3, -5), spawn_time=3.0))
bs.timer(20.0, lambda: self._bots.spawn_bot(ExplodeyBot, pos=(0, 3, 1), spawn_time=3.0))
bs.timer(20.0, lambda: self._bots.spawn_bot(ExplodeyBot, pos=(-5, 3, -2), spawn_time=3.0))
bs.timer(20.0, lambda: self._bots.spawn_bot(ExplodeyBot, pos=(5, 3, -2), spawn_time=3.0))
bs.timer(30, self.street)
def street(self):
bs.broadcastmessage("Lame Guys Are Here!", color=(1, 0, 0))
for a in range(-1, 2):
for b in range(-3, 0):
self._bots.spawn_bot(BrawlerBotProShielded, pos=(a, 3, b), spawn_time=3.0)
def spawn_player(self, player: Player) -> bs.Actor:
spawn_center = (0, 3, -2)
pos = (spawn_center[0] + random.uniform(-1.5, 1.5), spawn_center[1],
spawn_center[2] + random.uniform(-1.5, 1.5))
spaz = self.spawn_player_spaz(player, position=pos)
p = ["Bigger Blast", "Stronger Punch", "Shield", "Speed"]
Power = random.choice(p)
spaz.bomb_type = random.choice(
["normal", "sticky", "ice", "impact", "normal", "ice", "sticky"])
bs.broadcastmessage(f"Now You Have {Power}")
if Power == p[0]:
spaz.bomb_count = 3
spaz.blast_radius = 2.5
if Power == p[1]:
spaz._punch_cooldown = 350
spaz._punch_power_scale = 2.0
if Power == p[2]:
spaz.equip_shields()
if Power == p[3]:
spaz.node.hockey = True
return spaz
def _check_if_won(self) -> None:
if not self._bots.have_living_bots():
self._won = True
self.end_game()
def handlemessage(self, msg: Any) -> Any:
# A player has died.
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard behavior.
self.respawn_player(msg.getplayer(Player))
# A spaz-bot has died.
elif isinstance(msg, SpazBotDiedMessage):
# Unfortunately the bot-set will always tell us there are living
# bots if we ask here (the currently-dying bot isn't officially
# marked dead yet) ..so lets push a call into the event loop to
# check once this guy has finished dying.
babase.pushcall(self._check_if_won)
# Let the base class handle anything we don't.
else:
return super().handlemessage(msg)
return None
# When this is called, we should fill out results and end the game
# *regardless* of whether is has been won. (this may be called due
# to a tournament ending or other external reason).
def end_game(self) -> None:
# Stop our on-screen timer so players can see what they got.
assert self._timer is not None
self._timer.stop()
results = bs.GameResults()
# If we won, set our score to the elapsed time in milliseconds.
# (there should just be 1 team here since this is co-op).
# ..if we didn't win, leave scores as default (None) which means
# we lost.
if self._won:
elapsed_time_ms = int((bs.time() - self._timer.starttime) * 1000.0)
bs.cameraflash()
self._winsound.play()
for team in self.teams:
for player in team.players:
if player.actor:
player.actor.handlemessage(bs.CelebrateMessage())
results.set_team_score(team, elapsed_time_ms)
# Ends the activity.
self.end(results)

View file

@ -0,0 +1,67 @@
# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
"""
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <[1](https://fsf.org/)>
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
This license is designed to ensure cooperation with the community in the case of network server software. It is a free, copyleft license for software and other kinds of works. The license guarantees your freedom to share and change all versions of a program, to make sure it remains free software for all its users.
The license identifier refers to the choice to use code under AGPL-3.0-or-later (i.e., AGPL-3.0 or some later version), as distinguished from use of code under AGPL-3.0-only. The license notice states which of these applies the code in the file.
"""
import random
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.game.meteorshower import MeteorShowerGame
from bascenev1lib.actor.bomb import Bomb
class NewMeteorShowerGame(MeteorShowerGame):
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return bs.app.classic.getmaps("melee")
def _drop_bomb_cluster(self) -> None:
# Drop several bombs in series.
delay = 0.0
bounds = list(self._map.get_def_bound_box("map_bounds"))
for _i in range(random.randrange(1, 3)):
# Drop them somewhere within our bounds with velocity pointing
# toward the opposite side.
pos = (
random.uniform(bounds[0], bounds[3]),
bounds[4],
random.uniform(bounds[2], bounds[5]),
)
dropdirx = -1 if pos[0] > 0 else 1
dropdirz = -1 if pos[2] > 0 else 1
forcex = (
bounds[0] - bounds[3]
if bounds[0] - bounds[3] > 0
else -(bounds[0] - bounds[3])
)
forcez = (
bounds[2] - bounds[5]
if bounds[2] - bounds[5] > 0
else -(bounds[2] - bounds[5])
)
vel = (
(-5 + random.random() * forcex) * dropdirx,
random.uniform(-3.066, -4.12),
(-5 + random.random() * forcez) * dropdirz,
)
bs.timer(delay, babase.Call(self._drop_bomb, pos, vel))
delay += 0.1
self._set_meteor_timer()
# ba_meta export plugin
class byEra0S(babase.Plugin):
MeteorShowerGame.get_supported_maps = NewMeteorShowerGame.get_supported_maps
MeteorShowerGame._drop_bomb_cluster = NewMeteorShowerGame._drop_bomb_cluster

View file

@ -0,0 +1,382 @@
# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
# 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
import random
from bascenev1lib.actor.bomb import BombFactory, Bomb
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, Sequence, Optional, List, Dict, Type, Type
class _GotTouched():
pass
class UFO(bs.Actor):
def __init__(self, pos: float = (0, 0, 0)):
super().__init__()
shared = SharedObjects.get()
self.r: Optional[int] = 0
self.dis: Optional[List] = []
self.target: float = (0.0, 0.0, 0.0)
self.regs: List[bs.NodeActor] = []
self.node = bs.newnode('prop',
delegate=self,
attrs={'body': 'landMine',
'position': pos,
'mesh': bs.getmesh('landMine'),
'mesh_scale': 1.5,
'body_scale': 0.01,
'shadow_size': 0.000001,
'gravity_scale': 0.0,
'color_texture': bs.gettexture("achievementCrossHair"),
'materials': [shared.object_material]})
self.ufo_collide = None
def create_target(self):
if not self.node.exists():
return
self.dis = []
shared = SharedObjects.get()
try:
def pass_():
self.regs.clear()
bs.timer(3875*0.001, self.move)
try:
bs.timer(3277*0.001, lambda: Bomb(velocity=(0, 0, 0), position=(
self.target[0], self.node.position[1]-0.43999, self.target[2]), bomb_type='impact').autoretain().arm())
except:
pass
key = bs.Material()
key.add_actions(
conditions=('they_have_material', shared.object_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('call', 'at_connect', pass_()),
))
except:
pass
self.regs.append(bs.NodeActor(bs.newnode('region',
attrs={
'position': self.target,
'scale': (0.04, 22, 0.04),
'type': 'sphere',
'materials': [key]})))
def move(self):
if not self.node.exists():
return
try:
self.create_target()
for j in bs.getnodes():
n = j.getdelegate(object)
if j.getnodetype() == 'prop' and isinstance(n, TileFloor):
if n.node.exists():
self.dis.append(n.node)
self.r = random.randint(0, len(self.dis)-1)
self.target = (self.dis[self.r].position[0],
self.node.position[1], self.dis[self.r].position[2])
bs.animate_array(self.node, 'position', 3, {
0: self.node.position,
3.0: self.target})
except:
pass
def handlemessage(self, msg):
if isinstance(msg, bs.DieMessage):
self.node.delete()
elif isinstance(msg, bs.OutOfBoundsMessage):
self.handlemessage(bs.DieMessage())
else:
super().handlemessage(msg)
class TileFloor(bs.Actor):
def __init__(self,
pos: float = (0, 0, 0)):
super().__init__()
get_mat = SharedObjects.get()
self.pos = pos
self.scale = 1.5
self.mat, self.mat2, self.test = bs.Material(), bs.Material(), bs.Material()
self.mat.add_actions(conditions=('we_are_older_than', 1),
actions=(('modify_part_collision', 'collide', False)))
self.mat2.add_actions(conditions=('we_are_older_than', 1),
actions=(('modify_part_collision', 'collide', True)))
self.test.add_actions(
conditions=('they_have_material', BombFactory.get().bomb_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', _GotTouched())))
self.node = bs.newnode('prop',
delegate=self,
attrs={'body': 'puck',
'position': self.pos,
'mesh': bs.getmesh('buttonSquareOpaque'),
'mesh_scale': self.scale*1.16,
'body_scale': self.scale,
'shadow_size': 0.0002,
'gravity_scale': 0.0,
'color_texture': bs.gettexture("tnt"),
'is_area_of_interest': True,
'materials': [self.mat, self.test]})
self.node_support = bs.newnode('region',
attrs={
'position': self.pos,
'scale': (self.scale*0.8918, 0.1, self.scale*0.8918),
'type': 'box',
'materials': [get_mat.footing_material, self.mat2]
})
def handlemessage(self, msg):
if isinstance(msg, bs.DieMessage):
self.node.delete()
self.node_support.delete()
elif isinstance(msg, _GotTouched):
def do(): self.handlemessage(bs.DieMessage())
bs.timer(0.1, do)
else:
super().handlemessage(msg)
class defs():
points = boxes = {}
boxes['area_of_interest_bounds'] = (-1.3440, 1.185751251, 3.7326226188) + (
0.0, 0.0, 0.0) + (29.8180273, 15.57249038, 22.93859993)
boxes['map_bounds'] = (0.0, 2.585751251, 0.4326226188) + (0.0, 0.0,
0.0) + (29.09506485, 15.81173179, 33.76723155)
class DummyMapForGame(bs.Map):
defs, name = defs(), 'Tile Lands'
@classmethod
def get_play_types(cls) -> List[str]:
return []
@classmethod
def get_preview_texture_name(cls) -> str:
return 'achievementCrossHair'
@classmethod
def on_preload(cls) -> Any:
data: Dict[str, Any] = {'bg_1': bs.gettexture('rampageBGColor'), 'bg_2': bs.gettexture(
'rampageBGColor2'), 'bg_mesh_1': bs.getmesh('rampageBG'), 'bg_mesh_2': bs.getmesh('rampageBG2'), }
return data
def __init__(self) -> None:
super().__init__()
self.bg1 = bs.newnode('terrain', attrs={
'mesh': self.preloaddata['bg_mesh_1'], 'lighting': False, 'background': True, 'color_texture': self.preloaddata['bg_2']})
self.bg2 = bs.newnode('terrain', attrs={
'mesh': self.preloaddata['bg_mesh_2'], 'lighting': False, 'background': True, 'color_texture': self.preloaddata['bg_2']})
a = bs.getactivity().globalsnode
a.tint, a.ambient_color, a.vignette_outer, a.vignette_inner = (
1.2, 1.1, 0.97), (1.3, 1.2, 1.03), (0.62, 0.64, 0.69), (0.97, 0.95, 0.93)
class DummyMapForGame2(bs.Map):
defs, name = defs(), 'Tile Lands Night'
@classmethod
def get_play_types(cls) -> List[str]:
return []
@classmethod
def get_preview_texture_name(cls) -> str:
return 'achievementCrossHair'
@classmethod
def on_preload(cls) -> Any:
data: Dict[str, Any] = {'bg_1': bs.gettexture('menuBG'), 'bg_2': bs.gettexture(
'menuBG'), 'bg_mesh_1': bs.getmesh('thePadBG'), 'bg_mesh_2': bs.getmesh('thePadBG'), }
return data
def __init__(self) -> None:
super().__init__()
self.bg1 = bs.newnode('terrain', attrs={
'mesh': self.preloaddata['bg_mesh_1'], 'lighting': False, 'background': True, 'color_texture': self.preloaddata['bg_2']})
self.bg2 = bs.newnode('terrain', attrs={
'mesh': self.preloaddata['bg_mesh_2'], 'lighting': False, 'background': True, 'color_texture': self.preloaddata['bg_2']})
a = bs.getactivity().globalsnode
a.tint, a.ambient_color, a.vignette_outer, a.vignette_inner = (
0.5, 0.7, 1.27), (2.5, 2.5, 2.5), (0.62, 0.64, 0.69), (0.97, 0.95, 0.93)
bs._map.register_map(DummyMapForGame)
bs._map.register_map(DummyMapForGame2)
class Player(bs.Player['Team']):
"""Our player type for this game."""
def __init__(self) -> None:
super().__init__()
self.death_time: Optional[float] = None
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class UFOAttackGame(bs.TeamGameActivity[Player, Team]):
name = 'UFO Attack'
description = 'Dodge the falling bombs.'
available_settings = [
bs.BoolSetting('Epic Mode', default=False),
bs.BoolSetting('Enable Run', default=True),
bs.BoolSetting('Enable Jump', default=True),
bs.BoolSetting('Display Map Area Dimension', default=False),
bs.IntSetting('No. of Rows' + u'', max_value=13, min_value=1, default=8, increment=1),
bs.IntSetting('No. of Columns' + u'', max_value=12, min_value=1, default=6, increment=1)
]
scoreconfig = bs.ScoreConfig(label='Survived',
scoretype=bs.ScoreType.SECONDS,
version='B')
# Print messages when players die (since its meaningful in this game).
announce_player_deaths = True
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return ['Tile Lands', 'Tile Lands Night']
@classmethod
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
return (issubclass(sessiontype, bs.DualTeamSession)
or issubclass(sessiontype, bs.FreeForAllSession))
def __init__(self, settings: dict):
super().__init__(settings)
self.col = int(settings['No. of Columns' + u''])
self.row = int(settings['No. of Rows' + u''])
self.bool1 = bool(settings['Enable Run'])
self.bool2 = bool(settings['Enable Jump'])
self._epic_mode = settings.get('Epic Mode', False)
self._last_player_death_time: Optional[float] = None
self._timer: Optional[OnScreenTimer] = None
self.default_music = (bs.MusicType.EPIC
if self._epic_mode else bs.MusicType.SURVIVAL)
if bool(settings["Display Map Area Dimension"]):
self.game_name = "UFO Attack " + "(" + str(self.col) + "x" + str(self.row) + ")"
else:
self.game_name = "UFO Attack"
if self._epic_mode:
self.slow_motion = True
def get_instance_display_string(self) -> babase.Lstr:
return self.game_name
def on_begin(self) -> None:
super().on_begin()
self._timer = OnScreenTimer()
self._timer.start()
# bs.timer(5.0, self._check_end_game)
for r in range(self.col):
for j in range(self.row):
tile = TileFloor(pos=(-6.204283+(j*1.399), 3.425666,
-1.3538+(r*1.399))).autoretain()
self.ufo = UFO(pos=(-5.00410667, 6.616383286, -2.503472)).autoretain()
bs.timer(7000*0.001, lambda: self.ufo.move())
for t in self.players:
self.spawn_player(t)
def on_player_join(self, player: Player) -> None:
if self.has_begun():
bs.broadcastmessage(
babase.Lstr(resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(0, 1, 0),
)
assert self._timer is not None
player.death_time = self._timer.getstarttime()
return
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
self._check_end_game()
def spawn_player(self, player: Player) -> bs.Actor:
dis = []
for a in bs.getnodes():
g = a.getdelegate(object)
if a.getnodetype() == 'prop' and isinstance(g, TileFloor):
dis.append(g.node)
r = random.randint(0, len(dis)-1)
spaz = self.spawn_player_spaz(player, position=(
dis[r].position[0], dis[r].position[1]+1.005958, dis[r].position[2]))
spaz.connect_controls_to_player(enable_punch=False,
enable_bomb=False,
enable_run=self.bool1,
enable_jump=self.bool2,
enable_pickup=False)
spaz.play_big_death_sound = True
return spaz
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg)
curtime = bs.time()
msg.getplayer(Player).death_time = curtime
bs.timer(1.0, self._check_end_game)
else:
return super().handlemessage(msg)
return None
def _check_end_game(self) -> None:
living_team_count = 0
for team in self.teams:
for player in team.players:
if player.is_alive():
living_team_count += 1
break
if living_team_count <= 1:
self.end_game()
def end_game(self) -> None:
self.ufo.handlemessage(bs.DieMessage())
cur_time = bs.time()
assert self._timer is not None
start_time = self._timer.getstarttime()
for team in self.teams:
for player in team.players:
survived = False
if player.death_time is None:
survived = True
player.death_time = cur_time + 1
score = int(player.death_time - self._timer.getstarttime())
if survived:
score += 2
self.stats.player_scored(player, score, screenmessage=False)
self._timer.stop(endtime=self._last_player_death_time)
results = bs.GameResults()
for team in self.teams:
longest_life = 0.0
for player in team.players:
assert player.death_time is not None
longest_life = max(longest_life,
player.death_time - start_time)
# Submit the score value in milliseconds.
results.set_team_score(team, int(longest_life))
self.end(results=results)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,643 @@
# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
# 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
import random
from bascenev1lib.actor.bomb import Blast
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.spaz import PunchHitMessage
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.spazfactory import SpazFactory
if TYPE_CHECKING:
from typing import Any, Sequence
lang = bs.app.lang.language
if lang == 'Spanish':
name = 'Guerra de Nieve'
snowball_rate = 'Intervalo de Ataque'
snowball_slowest = 'Más Lento'
snowball_slow = 'Lento'
snowball_fast = 'Rápido'
snowball_lagcity = 'Más Rápido'
snowball_scale = 'Tamaño de Bola de Nieve'
snowball_smallest = 'Más Pequeño'
snowball_small = 'Pequeño'
snowball_big = 'Grande'
snowball_biggest = 'Más Grande'
snowball_insane = 'Insano'
snowball_melt = 'Derretir Bola de Nieve'
snowball_bust = 'Rebotar Bola de Nieve'
snowball_explode = 'Explotar al Impactar'
snowball_snow = 'Modo Nieve'
else:
name = 'Snowball Fight'
snowball_rate = 'Snowball Rate'
snowball_slowest = 'Slowest'
snowball_slow = 'Slow'
snowball_fast = 'Fast'
snowball_lagcity = 'Lag City'
snowball_scale = 'Snowball Scale'
snowball_smallest = 'Smallest'
snowball_small = 'Small'
snowball_big = 'Big'
snowball_biggest = 'Biggest'
snowball_insane = 'Insane'
snowball_melt = 'Snowballs Melt'
snowball_bust = 'Snowballs Bust'
snowball_explode = 'Snowballs Explode'
snowball_snow = 'Snow Mode'
class Snowball(bs.Actor):
def __init__(self,
position: Sequence[float] = (0.0, 1.0, 0.0),
velocity: Sequence[float] = (0.0, 0.0, 0.0),
blast_radius: float = 0.7,
bomb_scale: float = 0.8,
source_player: bs.Player | None = None,
owner: bs.Node | None = None,
melt: bool = True,
bounce: bool = True,
explode: bool = False):
super().__init__()
shared = SharedObjects.get()
self._exploded = False
self.scale = bomb_scale
self.blast_radius = blast_radius
self._source_player = source_player
self.owner = owner
self._hit_nodes = set()
self.snowball_melt = melt
self.snowball_bounce = bounce
self.snowball_explode = explode
self.radius = bomb_scale * 0.1
if bomb_scale <= 1.0:
shadow_size = 0.6
elif bomb_scale <= 2.0:
shadow_size = 0.4
elif bomb_scale <= 3.0:
shadow_size = 0.2
else:
shadow_size = 0.1
self.snowball_material = bs.Material()
self.snowball_material.add_actions(
conditions=(
(
('we_are_younger_than', 5),
'or',
('they_are_younger_than', 100),
),
'and',
('they_have_material', shared.object_material),
),
actions=('modify_node_collision', 'collide', False),
)
self.snowball_material.add_actions(
conditions=('they_have_material', shared.pickup_material),
actions=('modify_part_collision', 'use_node_collide', False),
)
self.snowball_material.add_actions(actions=('modify_part_collision',
'friction', 0.3))
self.snowball_material.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(('modify_part_collision', 'physical', False),
('call', 'at_connect', self.hit)))
self.snowball_material.add_actions(
conditions=(('they_dont_have_material', shared.player_material),
'and',
('they_have_material', shared.object_material),
'or',
('they_have_material', shared.footing_material)),
actions=('call', 'at_connect', self.bounce))
self.node = bs.newnode(
'prop',
delegate=self,
attrs={
'position': position,
'velocity': velocity,
'body': 'sphere',
'body_scale': self.scale,
'mesh': bs.getmesh('frostyPelvis'),
'shadow_size': shadow_size,
'color_texture': bs.gettexture('bunnyColor'),
'reflection': 'soft',
'reflection_scale': [0.15],
'density': 1.0,
'materials': [self.snowball_material]
})
self.light = bs.newnode(
'light',
owner=self.node,
attrs={
'color': (0.6, 0.6, 1.0),
'intensity': 0.8,
'radius': self.radius
})
self.node.connectattr('position', self.light, 'position')
bs.animate(self.node, 'mesh_scale', {
0: 0,
0.2: 1.3 * self.scale,
0.26: self.scale
})
bs.animate(self.light, 'radius', {
0: 0,
0.2: 1.3 * self.radius,
0.26: self.radius
})
if self.snowball_melt:
bs.timer(1.5, bs.WeakCall(self._disappear))
def hit(self) -> None:
if not self.node:
return
if self._exploded:
return
if self.snowball_explode:
self._exploded = True
self.do_explode()
bs.timer(0.001, bs.WeakCall(self.handlemessage, bs.DieMessage()))
else:
self.do_hit()
def do_hit(self) -> None:
v = self.node.velocity
if babase.Vec3(*v).length() > 5.0:
node = bs.getcollision().opposingnode
if node is not None and node and not (
node in self._hit_nodes):
t = self.node.position
hitdir = self.node.velocity
self._hit_nodes.add(node)
node.handlemessage(
bs.HitMessage(
pos=t,
velocity=v,
magnitude=babase.Vec3(*v).length()*0.5,
velocity_magnitude=babase.Vec3(*v).length()*0.5,
radius=0,
srcnode=self.node,
source_player=self._source_player,
force_direction=hitdir,
hit_type='snoBall',
hit_subtype='default'))
if not self.snowball_bounce:
bs.timer(0.05, bs.WeakCall(self.do_bounce))
def do_explode(self) -> None:
Blast(position=self.node.position,
velocity=self.node.velocity,
blast_radius=self.blast_radius,
source_player=babase.existing(self._source_player),
blast_type='impact',
hit_subtype='explode').autoretain()
def bounce(self) -> None:
if not self.node:
return
if self._exploded:
return
if not self.snowball_bounce:
vel = self.node.velocity
bs.timer(0.01, bs.WeakCall(self.calc_bounce, vel))
else:
return
def calc_bounce(self, vel) -> None:
if not self.node:
return
ospd = babase.Vec3(*vel).length()
dot = sum(x*y for x, y in zip(vel, self.node.velocity))
if ospd*ospd - dot > 50.0:
bs.timer(0.05, bs.WeakCall(self.do_bounce))
def do_bounce(self) -> None:
if not self.node:
return
if not self._exploded:
self.do_effect()
def do_effect(self) -> None:
self._exploded = True
bs.emitfx(position=self.node.position,
velocity=[v*0.1 for v in self.node.velocity],
count=10,
spread=0.1,
scale=0.4,
chunk_type='ice')
sound = bs.getsound('impactMedium')
sound.play(1.0, position=self.node.position)
scl = self.node.mesh_scale
bs.animate(self.node, 'mesh_scale', {
0.0: scl*1.0,
0.02: scl*0.5,
0.05: 0.0
})
lr = self.light.radius
bs.animate(self.light, 'radius', {
0.0: lr*1.0,
0.02: lr*0.5,
0.05: 0.0
})
bs.timer(0.08,
bs.WeakCall(self.handlemessage, bs.DieMessage()))
def _disappear(self) -> None:
self._exploded = True
if self.node:
scl = self.node.mesh_scale
bs.animate(self.node, 'mesh_scale', {
0.0: scl*1.0,
0.3: scl*0.5,
0.5: 0.0
})
lr = self.light.radius
bs.animate(self.light, 'radius', {
0.0: lr*1.0,
0.3: lr*0.5,
0.5: 0.0
})
bs.timer(0.55,
bs.WeakCall(self.handlemessage, bs.DieMessage()))
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:
self.node.delete()
elif isinstance(msg, bs.OutOfBoundsMessage):
self.handlemessage(bs.DieMessage())
else:
super().handlemessage(msg)
class NewPlayerSpaz(PlayerSpaz):
def __init__(self, *args: Any, **kwds: Any):
super().__init__(*args, **kwds)
self.snowball_scale = 1.0
self.snowball_melt = True
self.snowball_bounce = True
self.snowball_explode = False
def on_punch_press(self) -> None:
if not self.node or self.frozen or self.node.knockout > 0.0:
return
t_ms = bs.time() * 1000
assert isinstance(t_ms, int)
if t_ms - self.last_punch_time_ms >= self._punch_cooldown:
if self.punch_callback is not None:
self.punch_callback(self)
# snowball
pos = self.node.position
p1 = self.node.position_center
p2 = self.node.position_forward
direction = [p1[0]-p2[0], p2[1]-p1[1], p1[2]-p2[2]]
direction[1] = 0.03
mag = 20.0/babase.Vec3(*direction).length()
vel = [v * mag for v in direction]
Snowball(position=(pos[0], pos[1] + 0.1, pos[2]),
velocity=vel,
blast_radius=self.blast_radius,
bomb_scale=self.snowball_scale,
source_player=self.source_player,
owner=self.node,
melt=self.snowball_melt,
bounce=self.snowball_bounce,
explode=self.snowball_explode).autoretain()
self._punched_nodes = set() # Reset this.
self.last_punch_time_ms = t_ms
self.node.punch_pressed = True
if not self.node.hold_node:
bs.timer(
0.1,
bs.WeakCall(self._safe_play_sound,
SpazFactory.get().swish_sound, 0.8))
self._turbo_filter_add_press('punch')
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, PunchHitMessage):
pass
else:
return super().handlemessage(msg)
return None
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
def __init__(self) -> None:
self.score = 0
# ba_meta export bascenev1.GameActivity
class SnowballFightGame(bs.TeamGameActivity[Player, Team]):
"""A game type based on acquiring kills."""
name = name
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.IntChoiceSetting(
snowball_rate,
choices=[
(snowball_slowest, 500),
(snowball_slow, 400),
('Normal', 300),
(snowball_fast, 200),
(snowball_lagcity, 100),
],
default=300,
),
bs.FloatChoiceSetting(
snowball_scale,
choices=[
(snowball_smallest, 0.4),
(snowball_small, 0.6),
('Normal', 0.8),
(snowball_big, 1.4),
(snowball_biggest, 3.0),
(snowball_insane, 6.0),
],
default=0.8,
),
bs.BoolSetting(snowball_melt, default=True),
bs.BoolSetting(snowball_bust, default=True),
bs.BoolSetting(snowball_explode, default=False),
bs.BoolSetting(snowball_snow, default=True),
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 bs.app.classic.getmaps('melee')
def __init__(self, settings: dict):
super().__init__(settings)
self._scoreboard = Scoreboard()
self._score_to_win: int | None = 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))
self._snowball_rate = int(settings[snowball_rate])
self._snowball_scale = float(settings[snowball_scale])
self._snowball_melt = bool(settings[snowball_melt])
self._snowball_bounce = bool(settings[snowball_bust])
self._snowball_explode = bool(settings[snowball_explode])
self._snow_mode = bool(settings[snowball_snow])
# 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)
def get_instance_description(self) -> str | Sequence:
return 'Crush ${ARG1} of your enemies.', self._score_to_win
def get_instance_description_short(self) -> 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_transition_in(self) -> None:
super().on_transition_in()
if self._snow_mode:
gnode = bs.getactivity().globalsnode
gnode.tint = (0.8, 0.8, 1.3)
bs.timer(0.02, self.emit_snowball, repeat=True)
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()
def emit_snowball(self) -> None:
pos = (-10 + (random.random() * 30), 15,
-10 + (random.random() * 30))
vel = ((-5.0 + random.random() * 30.0) * (-1.0 if pos[0] > 0 else 1.0),
-50.0, (-5.0 + random.random() * 30.0) * (
-1.0 if pos[0] > 0 else 1.0))
bs.emitfx(position=pos,
velocity=vel,
count=10,
scale=1.0 + random.random(),
spread=0.0,
chunk_type='spark')
def spawn_player_spaz(self,
player: Player,
position: Sequence[float] = (0, 0, 0),
angle: float | None = None) -> PlayerSpaz:
from babase import _math
from bascenev1._gameutils import animate
from bascenev1._coopsession import CoopSession
if isinstance(self.session, bs.DualTeamSession):
position = self.map.get_start_position(player.team.id)
else:
# otherwise do free-for-all spawn locations
position = self.map.get_ffa_start_position(self.players)
name = player.getname()
color = player.color
highlight = player.highlight
light_color = _math.normalized_color(color)
display_color = babase.safecolor(color, target_intensity=0.75)
spaz = NewPlayerSpaz(color=color,
highlight=highlight,
character=player.character,
player=player)
player.actor = spaz
assert spaz.node
# If this is co-op and we're on Courtyard or Runaround, add the
# material that allows us to collide with the player-walls.
# FIXME: Need to generalize this.
if isinstance(self.session, CoopSession) and self.map.getname() in [
'Courtyard', 'Tower D'
]:
mat = self.map.preloaddata['collide_with_wall_material']
assert isinstance(spaz.node.materials, tuple)
assert isinstance(spaz.node.roller_materials, tuple)
spaz.node.materials += (mat, )
spaz.node.roller_materials += (mat, )
spaz.node.name = name
spaz.node.name_color = display_color
spaz.connect_controls_to_player(
enable_pickup=False, enable_bomb=False)
# Move to the stand position and add a flash of light.
spaz.handlemessage(
bs.StandMessage(
position,
angle if angle is not None else random.uniform(0, 360)))
self._spawn_sound.play(1, position=spaz.node.position)
light = bs.newnode('light', attrs={'color': light_color})
spaz.node.connectattr('position', light, 'position')
animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
bs.timer(0.5, light.delete)
# custom
spaz._punch_cooldown = self._snowball_rate
spaz.snowball_scale = self._snowball_scale
spaz.snowball_melt = self._snowball_melt
spaz.snowball_bounce = self._snowball_bounce
spaz.snowball_explode = self._snowball_explode
return spaz
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()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)

View file

@ -0,0 +1,505 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
"""you vs BombSquad / Created by: byANG3L"""
# 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
import _babase
import random
from bascenev1lib.actor.spazbot import SpazBotSet, BrawlerBot, SpazBotDiedMessage
from bascenev1lib.actor.onscreentimer import OnScreenTimer
if TYPE_CHECKING:
from typing import Any, Optional
lang = bs.app.lang.language
if lang == 'Spanish':
name = 'Tu vs BombSquad'
name_easy = 'Tu vs BS Fácil'
name_easy_epic = 'Tu vs BS Fácil Épico'
name_hard = 'Tu vs BS Difícil'
name_hard_epic = 'Tu vs BS Difícil Épico'
else:
name = 'You vs BombSquad'
name_easy = 'You vs BS Easy'
name_easy_epic = 'You vs BS Easy Epic'
name_hard = 'You vs BS Hard'
name_hard_epic = 'You vs BS Hard Epic'
# def ba_get_api_version():
# return 6
def ba_get_levels():
return [babase._level.Level(
name_easy,
gametype=TUvsBombSquad,
settings={},
preview_texture_name='footballStadiumPreview'),
babase._level.Level(
name_easy_epic,
gametype=TUvsBombSquad,
settings={'Epic Mode': True},
preview_texture_name='footballStadiumPreview'),
babase._level.Level(
name_hard,
gametype=TUvsBombSquad,
settings={'Hard Mode': True},
preview_texture_name='footballStadiumPreview'),
babase._level.Level(
name_hard_epic,
gametype=TUvsBombSquad,
settings={'Hard Mode': True,
'Epic Mode': True},
preview_texture_name='footballStadiumPreview')]
#### BOTS ####
class SpazBot(BrawlerBot):
character = 'Spaz'
color = (0.1, 0.35, 0.1)
highlight = (1, 0.15, 0.15)
class ZoeBot(BrawlerBot):
character = 'Zoe'
color = (0.6, 0.6, 0.6)
highlight = (0, 1, 0)
class SnakeBot(BrawlerBot):
character = 'Snake Shadow'
color = (1, 1, 1)
highlight = (0.55, 0.8, 0.55)
class MelBot(BrawlerBot):
character = 'Mel'
color = (1, 1, 1)
highlight = (0.1, 0.6, 0.1)
class JackBot(BrawlerBot):
character = 'Jack Morgan'
color = (1, 0.2, 0.1)
highlight = (1, 1, 0)
class SantaBot(BrawlerBot):
character = 'Santa Claus'
color = (1, 0, 0)
highlight = (1, 1, 1)
class FrostyBot(BrawlerBot):
character = 'Frosty'
color = (0.5, 0.5, 1)
highlight = (1, 0.5, 0)
class BonesBot(BrawlerBot):
character = 'Bones'
color = (0.6, 0.9, 1)
highlight = (0.6, 0.9, 1)
class BernardBot(BrawlerBot):
character = 'Bernard'
color = (0.7, 0.5, 0.0)
highlight = (0.6, 0.5, 0.8)
class PascalBot(BrawlerBot):
character = 'Pascal'
color = (0.3, 0.5, 0.8)
highlight = (1, 0, 0)
class TaobaoBot(BrawlerBot):
character = 'Taobao Mascot'
color = (1, 0.5, 0)
highlight = (1, 1, 1)
class BBot(BrawlerBot):
character = 'B-9000'
color = (0.5, 0.5, 0.5)
highlight = (1, 0, 0)
class AgentBot(BrawlerBot):
character = 'Agent Johnson'
color = (0.3, 0.3, 0.33)
highlight = (1, 0.5, 0.3)
class GrumbledorfBot(BrawlerBot):
character = 'Grumbledorf'
color = (0.2, 0.4, 1.0)
highlight = (0.06, 0.15, 0.4)
class PixelBot(BrawlerBot):
character = 'Pixel'
color = (0, 1, 0.7)
highlight = (0.65, 0.35, 0.75)
class BunnyBot(BrawlerBot):
character = 'Easter Bunny'
color = (1, 1, 1)
highlight = (1, 0.5, 0.5)
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export bascenev1.GameActivity
class TUvsBombSquad(bs.TeamGameActivity[Player, Team]):
"""A game type based on acquiring kills."""
name = name
description = 'Defeat all enemies.'
scoreconfig = bs.ScoreConfig(label='Time',
scoretype=bs.ScoreType.MILLISECONDS,
lower_is_better=True)
@classmethod
def get_available_settings(
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
settings = [
bs.BoolSetting('Hard Mode', default=False),
bs.BoolSetting('Epic Mode', default=False),
]
return settings
@classmethod
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
return (issubclass(sessiontype, bs.CoopSession)
or issubclass(sessiontype, bs.MultiTeamSession))
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return ['Football Stadium']
def __init__(self, settings: dict):
super().__init__(settings)
self._winsound = bs.getsound('score')
self._won = False
self._timer: Optional[OnScreenTimer] = None
self._bots = SpazBotSet()
self._hard_mode = bool(settings['Hard Mode'])
self._epic_mode = bool(settings['Epic Mode'])
# Base class overrides.
self.slow_motion = self._epic_mode
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
bs.MusicType.SURVIVAL)
self._spaz_easy: list = [[(-5.4146, 0.9515, -3.0379), 23.0],
[(-5.4146, 0.9515, 1.0379), 23.0]]
self._spaz_hard: list = [[(11.4146, 0.9515, -5.0379), 3.0],
[(-8.4146, 0.9515, -5.0379), 5.0],
[(5.4146, 0.9515, -3.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0]]
self._zoe_easy: list = [[(5.4146, 0.9515, -1.0379), 23.0],
[(-5.4146, 0.9515, 5.0379), 23.0]]
self._zoe_hard: list = [[(-11.4146, 0.9515, -5.0379), 3.0],
[(8.4146, 0.9515, -3.0379), 5.0],
[(-5.4146, 0.9515, -3.0379), 8.0],
[(-5.4146, 0.9515, 1.0379), 8.0]]
self._snake_easy: list = [[(-5.4146, 0.9515, -1.0379), 23.0],
[(5.4146, 0.9515, -5.0379), 23.0]]
self._snake_hard: list = [[(11.4146, 0.9515, -3.0379), 3.0],
[(-8.4146, 0.9515, -3.0379), 5.0],
[(5.4146, 0.9515, -1.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0]]
self._kronk_easy: list = [[(8.4146, 0.9515, 1.0379), 10.0],
[(5.4146, 0.9515, 3.0379), 23.0]]
self._kronk_hard: list = [[(-11.4146, 0.9515, -3.0379), 3.0],
[(8.4146, 0.9515, -1.0379), 5.0],
[(-5.4146, 0.9515, -1.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0]]
self._mel_easy: list = [[(5.4146, 0.9515, 1.0379), 23.0],
[(-11.4146, 0.9515, 1.0379), 3.0]]
self._mel_hard: list = [[(11.4146, 0.9515, -1.0379), 3.0],
[(-8.4146, 0.9515, -1.0379), 5.0],
[(5.4146, 0.9515, 1.0379), 8.0],
[(5.4146, 0.9515, 5.0379), 8.0]]
self._jack_easy: list = [[(-8.4146, 0.9515, 1.0379), 10.0],
[(5.4146, 0.9515, 1.0379), 23.0]]
self._jack_hard: list = [[(-11.4146, 0.9515, -1.0379), 3.0],
[(8.4146, 0.9515, 1.0379), 5.0],
[(-5.4146, 0.9515, 1.0379), 8.0],
[(-5.4146, 0.9515, 5.0379), 8.0],
[(5.4146, 0.9515, -5.0379), 8.0]]
self._frosty_easy: list = [[(8.4146, 0.9515, 1.0379), 10.0],
[(8.4146, 0.9515, -5.0379), 10.0]]
self._frosty_hard: list = [[(-11.4146, 0.9515, 1.0379), 3.0],
[(-5.4146, 0.9515, 3.0379), 8.0],
[(-5.4146, 0.9515, -5.0379), 8.0],
[(5.4146, 0.9515, 3.0379), 8.0]]
self._bunny_easy: list = [[(-8.4146, 0.9515, 3.0379), 10.0],
[(5.4146, 0.9515, 5.0379), 23.0]]
self._bunny_hard: list = [[(8.4146, 0.9515, -5.0379), 5.0],
[(-5.4146, 0.9515, -5.0379), 8.0],
[(-5.4146, 0.9515, 3.0379), 8.0],
[(8.4146, 0.9515, 3.0379), 5.0]]
self._bones_easy: list = [[(11.4146, 0.9515, -5.0379), 3.0],
[(-8.4146, 0.9515, -5.0379), 10.0]]
self._bones_hard: list = [[(5.4146, 0.9515, -3.0379), 8.0],
[(-5.4146, 0.9515, 3.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0],
[(8.4146, 0.9515, 3.0379), 5.0]]
self._bernard_easy: list = [[(-11.4146, 0.9515, -5.0379), 3.0],
[(8.4146, 0.9515, -3.0379), 10.0]]
self._bernard_hard: list = [[(-5.4146, 0.9515, -3.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0],
[(-5.4146, 0.9515, 1.0379), 8.0],
[(-8.4146, 0.9515, 3.0379), 5.0]]
self._pascal_easy: list = [[(11.4146, 0.9515, -3.0379), 3.0],
[(-8.4146, 0.9515, -3.0379), 10.0]]
self._pascal_hard: list = [[(5.4146, 0.9515, -1.0379), 8.0],
[(-5.4146, 0.9515, 1.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0],
[(8.4146, 0.9515, 1.0379), 5.0]]
self._taobao_easy: list = [[(-11.4146, 0.9515, -3.0379), 3.0],
[(8.4146, 0.9515, -1.0379), 10.0]]
self._taobao_hard: list = [[(-5.4146, 0.9515, -1.0379), 8.0],
[(5.4146, 0.9515, 1.0379), 8.0],
[(-5.4146, 0.9515, 1.0379), 8.0],
[(-5.4146, 0.9515, 1.0379), 8.0]]
self._bbot_easy: list = [[(11.4146, 0.9515, -1.0379), 3.0],
[(-8.4146, 0.9515, -1.0379), 10.0]]
self._bbot_hard: list = [[(-5.4146, 0.9515, 1.0379), 8.0],
[(8.4146, 0.9515, 1.0379), 5.0],
[(-5.4146, 0.9515, 1.0379), 8.0],
[(-5.4146, 0.9515, 1.0379), 8.0]]
self._agent_easy: list = [[(-11.4146, 0.9515, -1.0379), 3.0],
[(8.4146, 0.9515, 1.0379), 10.0]]
self._agent_hard: list = [[(5.4146, 0.9515, 5.0379), 8.0],
[(-8.4146, 0.9515, 1.0379), 5.0],
[(-11.4146, 0.9515, 1.0379), 3.0],
[(-11.4146, 0.9515, 1.0379), 3.0]]
self._wizard_easy: list = [[(11.4146, 0.9515, 1.0379), 3.0],
[(-8.4146, 0.9515, 1.0379), 10.0]]
self._wizard_hard: list = [[(-5.4146, 0.9515, 5.0379), 8.0],
[(8.4146, 0.9515, 5.0379), 5.0],
[(-5.4146, 0.9515, 1.0379), 8.0],
[(11.4146, 0.9515, 1.0379), 3.0]]
self._pixel_easy: list = [[(-5.4146, 0.9515, -5.0379), 23.0]]
self._pixel_hard: list = [[(5.4146, 0.9515, -5.0379), 8.0],
[(5.4146, 0.9515, 3.0379), 5.0],
[(-8.4146, 0.9515, 5.0379), 5.0]]
self._santa_easy: list = [[(-8.4146, 0.9515, 5.0379), 23.0]]
self._santa_hard: list = [[(-8.4146, 0.9515, 1.0379), 5.0],
[(-8.4146, 0.9515, 5.0379), 5.0],
[(5.4146, 0.9515, 1.0379), 8.0]]
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_powerup_drops()
self._timer = OnScreenTimer()
bs.timer(4.0, self._timer.start)
for i in range(len(self._spaz_easy)):
self._spawn_bots(4.0, SpazBot,
self._spaz_easy[i][0], self._spaz_easy[i][1])
for i in range(len(self._zoe_easy)):
self._spawn_bots(4.0, ZoeBot,
self._zoe_easy[i][0], self._zoe_easy[i][1])
for i in range(len(self._snake_easy)):
self._spawn_bots(4.0, SnakeBot,
self._snake_easy[i][0], self._snake_easy[i][1])
for i in range(len(self._kronk_easy)):
self._spawn_bots(4.0, BrawlerBot,
self._kronk_easy[i][0], self._kronk_easy[i][1])
for i in range(len(self._mel_easy)):
self._spawn_bots(4.0, MelBot,
self._mel_easy[i][0], self._mel_easy[i][1])
for i in range(len(self._jack_easy)):
self._spawn_bots(4.0, JackBot,
self._jack_easy[i][0], self._jack_easy[i][1])
for i in range(len(self._santa_easy)):
self._spawn_bots(4.0, SantaBot,
self._santa_easy[i][0], self._santa_easy[i][1])
for i in range(len(self._frosty_easy)):
self._spawn_bots(4.0, FrostyBot,
self._frosty_easy[i][0], self._frosty_easy[i][1])
for i in range(len(self._bunny_easy)):
self._spawn_bots(4.0, BunnyBot,
self._bunny_easy[i][0], self._bunny_easy[i][1])
for i in range(len(self._bones_easy)):
self._spawn_bots(4.0, BonesBot,
self._bones_easy[i][0], self._bones_easy[i][1])
for i in range(len(self._bernard_easy)):
self._spawn_bots(4.0, BernardBot,
self._bernard_easy[i][0], self._bernard_easy[i][1])
for i in range(len(self._pascal_easy)):
self._spawn_bots(4.0, PascalBot,
self._pascal_easy[i][0], self._pascal_easy[i][1])
for i in range(len(self._taobao_easy)):
self._spawn_bots(4.0, TaobaoBot,
self._taobao_easy[i][0], self._taobao_easy[i][1])
for i in range(len(self._bbot_easy)):
self._spawn_bots(4.0, BBot,
self._bbot_easy[i][0], self._bbot_easy[i][1])
for i in range(len(self._agent_easy)):
self._spawn_bots(4.0, AgentBot,
self._agent_easy[i][0], self._agent_easy[i][1])
for i in range(len(self._wizard_easy)):
self._spawn_bots(4.0, GrumbledorfBot,
self._wizard_easy[i][0], self._wizard_easy[i][1])
for i in range(len(self._pixel_easy)):
self._spawn_bots(4.0, PixelBot,
self._pixel_easy[i][0], self._pixel_easy[i][1])
if self._hard_mode:
for i in range(len(self._spaz_hard)):
self._spawn_bots(4.0, SpazBot,
self._spaz_hard[i][0], self._spaz_hard[i][1])
for i in range(len(self._zoe_hard)):
self._spawn_bots(4.0, ZoeBot,
self._zoe_hard[i][0], self._zoe_hard[i][1])
for i in range(len(self._snake_hard)):
self._spawn_bots(4.0, SnakeBot,
self._snake_hard[i][0], self._snake_hard[i][1])
for i in range(len(self._kronk_hard)):
self._spawn_bots(4.0, BrawlerBot,
self._kronk_hard[i][0], self._kronk_hard[i][1])
for i in range(len(self._mel_hard)):
self._spawn_bots(4.0, MelBot,
self._mel_hard[i][0], self._mel_hard[i][1])
for i in range(len(self._jack_hard)):
self._spawn_bots(4.0, JackBot,
self._jack_hard[i][0], self._jack_hard[i][1])
for i in range(len(self._santa_hard)):
self._spawn_bots(4.0, SantaBot,
self._santa_hard[i][0], self._santa_hard[i][1])
for i in range(len(self._frosty_hard)):
self._spawn_bots(4.0, FrostyBot,
self._frosty_hard[i][0], self._frosty_hard[i][1])
for i in range(len(self._bunny_hard)):
self._spawn_bots(4.0, BunnyBot,
self._bunny_hard[i][0], self._bunny_hard[i][1])
for i in range(len(self._bones_hard)):
self._spawn_bots(4.0, BonesBot,
self._bones_hard[i][0], self._bones_hard[i][1])
for i in range(len(self._bernard_hard)):
self._spawn_bots(4.0, BernardBot,
self._bernard_hard[i][0], self._bernard_hard[i][1])
for i in range(len(self._pascal_hard)):
self._spawn_bots(4.0, PascalBot,
self._pascal_hard[i][0], self._pascal_hard[i][1])
for i in range(len(self._taobao_hard)):
self._spawn_bots(4.0, TaobaoBot,
self._taobao_hard[i][0], self._taobao_hard[i][1])
for i in range(len(self._bbot_hard)):
self._spawn_bots(4.0, BBot,
self._bbot_hard[i][0], self._bbot_hard[i][1])
for i in range(len(self._agent_hard)):
self._spawn_bots(4.0, AgentBot,
self._agent_hard[i][0], self._agent_hard[i][1])
for i in range(len(self._wizard_hard)):
self._spawn_bots(4.0, GrumbledorfBot,
self._wizard_hard[i][0], self._wizard_hard[i][1])
for i in range(len(self._pixel_hard)):
self._spawn_bots(4.0, PixelBot,
self._pixel_hard[i][0], self._pixel_hard[i][1])
def _spawn_bots(self, time: float, bot: Any,
pos: float, spawn_time: float) -> None:
bs.timer(time, lambda: self._bots.spawn_bot(
bot, pos=pos, spawn_time=spawn_time))
def on_player_join(self, player: Player) -> None:
if self.has_begun():
bs.broadcastmessage(
babase.Lstr(resource='playerDelayedJoinText',
subs=[('${PLAYER}', player.getname(full=True))]),
color=(0, 1, 0),
)
return
self.spawn_player(player)
# Called for each spawning player.
def spawn_player(self, player: Player) -> bs.Actor:
# Let's spawn close to the center.
spawn_center = (0.0728, 0.0227, -1.9888)
pos = (spawn_center[0] + random.uniform(-0.5, 0.5), spawn_center[1],
spawn_center[2] + random.uniform(-0.5, 0.5))
return self.spawn_player_spaz(player, position=pos)
def _check_if_won(self) -> None:
# Simply end the game if there's no living bots.
# FIXME: Should also make sure all bots have been spawned;
# if spawning is spread out enough that we're able to kill
# all living bots before the next spawns, it would incorrectly
# count as a win.
if not self._bots.have_living_bots():
self._won = True
self.end_game()
# Called for miscellaneous messages.
def handlemessage(self, msg: Any) -> Any:
# A player has died.
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard behavior.
self.respawn_player(msg.getplayer(Player))
# A spaz-bot has died.
elif isinstance(msg, SpazBotDiedMessage):
# Unfortunately the bot-set will always tell us there are living
# bots if we ask here (the currently-dying bot isn't officially
# marked dead yet) ..so lets push a call into the event loop to
# check once this guy has finished dying.
babase.pushcall(self._check_if_won)
# Let the base class handle anything we don't.
else:
return super().handlemessage(msg)
return None
# When this is called, we should fill out results and end the game
# *regardless* of whether is has been won. (this may be called due
# to a tournament ending or other external reason).
def end_game(self) -> None:
# Stop our on-screen timer so players can see what they got.
assert self._timer is not None
self._timer.stop()
results = bs.GameResults()
# If we won, set our score to the elapsed time in milliseconds.
# (there should just be 1 team here since this is co-op).
# ..if we didn't win, leave scores as default (None) which means
# we lost.
if self._won:
elapsed_time_ms = int((bs.time() - self._timer.starttime) * 1000.0)
bs.cameraflash()
self._winsound.play()
for team in self.teams:
for player in team.players:
if player.actor:
player.actor.handlemessage(bs.CelebrateMessage())
results.set_team_score(team, elapsed_time_ms)
# Ends the activity.
self.end(results)

View file

@ -1071,6 +1071,139 @@
"md5sum": "5fa8706f36d618f8302551dd2a0403a0"
}
}
},
"disable_friendly_fire": {
"description": "Disables friendly fire",
"external_url": "",
"authors": [
{
"name": "EmperoR",
"email": "",
"discord": "EmperoR#4098"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "f744c41",
"released_on": "24-01-2024",
"md5sum": "203ecefa1c1eb9894cfb0d87e2d7fe09"
}
}
},
"infinity_shield": {
"description": "Gives you unbreakable shield",
"external_url": "https://youtu.be/hp7vbB-hUPg?si=i7Th0NP5xDPLN2P_",
"authors": [
{
"name": "JoseAng3l",
"email": "",
"discord": "joseang3l"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "eb917ca19d206dfd19667181dacc1df5"
}
}
},
"only_night": {
"description": "Night Mode",
"external_url": "",
"authors": [
{
"name": "IDK",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "255d3d6694008cc2f73d115182100b52"
}
}
},
"tag": {
"description": "Get a tag",
"external_url": "",
"authors": [
{
"name": "pranav",
"email": "",
"discord": ""
}
],
"versions": {
"2.0.1": {
"api_version": 8,
"commit_sha": "718039b",
"released_on": "24-01-2024",
"md5sum": "01cf9e10ab0e1bf51c07d80ff842c632"
}
}
},
"xyz_tool": {
"description": "Punch to save the co-ordinates",
"external_url": "",
"authors": [
{
"name": "Yann",
"email": "",
"discord": "riyukiiyan"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "5063690",
"released_on": "03-02-2024",
"md5sum": "3f301456128f422b7277c667f6c5c47e"
}
}
},
"bots_can_accept_powerups": {
"description": "Bots can steal your powerups",
"external_url": "https://youtu.be/Jrk5JfveYEI?si=wYXWVxdC-3XMpuCg",
"authors": [
{
"name": "JoseAng3l",
"email": "",
"discord": "joseang3l"
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "71eae7c5d0e05821809d348ea0c64837"
}
}
},
"cheat_menu": {
"description": "Cheat menu on the settings window",
"external_url": "",
"authors": [
{
"name": "pranav",
"email": "",
"discord": ""
}
],
"versions": {
"1.0.0": {
"api_version": 8,
"commit_sha": "4941d0c",
"released_on": "01-02-2024",
"md5sum": "079e857197979aabf53f232b3cce56ba"
}
}
}
}
}

View file

@ -0,0 +1,42 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# 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.spazbot import SpazBot
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
if TYPE_CHECKING:
pass
# ba_meta export plugin
class BotsCanAcceptPowerupsPlugin(babase.Plugin):
def on_app_running(self) -> None:
SpazBot.oldinit = SpazBot.__init__
def __init__(self) -> None:
self.oldinit()
pam = PowerupBoxFactory.get().powerup_accept_material
materials = self.node.materials
materials = list(materials)
materials.append(pam)
materials = tuple(materials)
self.node.materials = materials
roller_materials = self.node.roller_materials
roller_materials = list(roller_materials)
roller_materials.append(pam)
roller_materials = tuple(roller_materials)
self.node.roller_materials = roller_materials
extras_material = self.node.extras_material
extras_material = list(extras_material)
extras_material.append(pam)
extras_material = tuple(extras_material)
self.node.extras_material = extras_material
SpazBot.__init__ = __init__

View file

@ -0,0 +1,358 @@
# Ported by brostos to api 8
# Tool used to make porting easier.(https://github.com/bombsquad-community/baport)
"""CheatMenu | now cheat much as you can haha!! please eric sir dont kill me
Credits To:
Pranav"Modder"= Creator of the mod.
Emily"Skin and Modder"= Code and Mod Ideas.(pls dont be angry) :"(
And Me(Edited): Well Litterally Nothing lol.
Important Note From the Creator: "I apreciate any kind of modification. So feel free to use or edit code or change credit string.... no problem.
this mod uses activity loop cheacks if change in config and update it on our player node"
Really Awesome servers:
Bombsquad Consultancy Service - https://discord.gg/2RKd9QQdQY.
bombspot - https://discord.gg/ucyaesh.
cyclones - https://discord.gg/pJXxkbQ7kH.
"""
from __future__ import annotations
__author__ = 'egg'
__version__ = 1.0
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
from baenv import TARGET_BALLISTICA_BUILD as build_number
from bauiv1lib.settings.allsettings import AllSettingsWindow
from bascenev1lib.actor.spaz import Spaz
from typing import (
Text,
Tuple,
Optional,
Union,
get_args
)
type
# Default Confings/Settings
CONFIG = "CheatMenu"
APPCONFIG = babase.app.config
Configs = {
"Unlimited Heath": False,
"SpeedBoots": False,
"Fly": False,
"SuperPunch": False,
"ImpactOnly": False,
"StickyOnly": False,
"IceOnly": False,
"Infinite Bombs": False,
"More Are Coming": False,
"Credits": False,
}
def setconfigs() -> None:
"""Set required defualt configs for mod"""
if CONFIG not in APPCONFIG:
APPCONFIG[str(CONFIG)] = Configs
for c in Configs:
if c not in APPCONFIG[str(CONFIG)]:
APPCONFIG[str(CONFIG)][str(c)] = Configs[str(c)]
else:
pass
APPCONFIG.apply_and_commit()
def update_config(config: str, change: any):
"""update's given value in json config file of pluguin"""
APPCONFIG[str(CONFIG)][str(config)] = change
APPCONFIG.apply_and_commit()
# ba_meta require api 8
# ba_meta export plugin
class Plugin(babase.Plugin):
def on_app_running(self) -> None:
if babase.app.build_number if build_number < 21282 else babase.app.env.build_number:
setconfigs()
self.overwrite()
else:
babase.screenmessage(f'{__name__} only works on api 8')
def overwrite(self) -> None:
AllSettingsWindow.init = AllSettingsWindow.__init__
AllSettingsWindow.__init__ = AllSettingsWindowInit
# creating Cheat button, start button
def AllSettingsWindowInit(self, transition: str = 'in_right', origin_widget: bui.Widget = None):
self.init(transition)
uiscale = bui.app.ui_v1.uiscale
btn_width = 720 if uiscale is babase.UIScale.SMALL else 400
btn_height = 380
self.cheat_menu_btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
position=(btn_width, btn_height),
size=(105, 50),
icon=bui.gettexture('settingsIcon'),
label='Cheats',
button_type='square',
text_scale=1.2,
on_activate_call=babase.Call(
on_cheat_menu_btn_press, self))
# on cheat button press call Window
def on_cheat_menu_btn_press(self):
bui.containerwidget(edit=self._root_widget,
transition='out_scale')
bui.app.ui_v1.set_main_menu_window(
CheatMenuWindow(
transition='in_right').get_root_widget(), from_window=self._root_widget)
class CheatMenuWindow(bui.Window):
def __init__(self,
transition: Optional[str] = 'in_right') -> None:
# background window, main widget parameters
uiscale = bui.app.ui_v1.uiscale
self._width = 870.0 if uiscale is babase.UIScale.SMALL else 670.0
self._height = (390.0 if uiscale is babase.UIScale.SMALL else
450.0 if uiscale is babase.UIScale.MEDIUM else 520.0)
extra_x = 100 if uiscale is babase.UIScale.SMALL else 0
self.extra_x = extra_x
top_extra = 20 if uiscale is babase.UIScale.SMALL else 0
# scroll widget parameters
self._scroll_width = self._width - (100 + 2 * extra_x)
self._scroll_height = self._height - 115.0
self._sub_width = self._scroll_width * 0.95
self._sub_height = 640.0
self._spacing = 32
self._extra_button_spacing = self._spacing * 2.5
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height),
transition=transition,
scale=(2.06 if uiscale is babase.UIScale.SMALL else
1.4 if uiscale is babase.UIScale.MEDIUM else 1.0)))
# back button widget
self._back_button = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
position=(52 + self.extra_x,
self._height - 60 - top_extra),
size=(60, 60),
scale=0.8,
label=babase.charstr(babase.SpecialChar.BACK),
button_type='backSmall',
on_activate_call=self._back)
bui.containerwidget(edit=self._root_widget,
cancel_button=self._back_button)
# window title, apears in top center of window
self._title_text = bui.textwidget(
parent=self._root_widget,
position=(0, self._height - 40 - top_extra),
size=(self._width, 25),
text='Cheat Menu',
color=bui.app.ui_v1.title_color,
scale=1.2,
h_align='center',
v_align='top')
self._scrollwidget = bui.scrollwidget(
parent=self._root_widget,
position=(50 + extra_x, 50 - top_extra),
simple_culling_v=20.0,
highlight=False,
size=(self._scroll_width,
self._scroll_height),
selection_loops_to_parent=True)
bui.widget(edit=self._scrollwidget,
right_widget=self._scrollwidget)
# subcontainer represents scroll widget and used as parent
self._subcontainer = bui.containerwidget(
parent=self._scrollwidget,
size=(self._sub_width,
self._sub_height),
background=False,
selection_loops_to_parent=True)
v = self._sub_height - 35
v -= self._spacing * 1.2
conf = APPCONFIG[str(CONFIG)]
for checkbox in Configs:
bui.checkboxwidget(
parent=self._subcontainer,
autoselect=True,
position=(25.0, v),
size=(40, 40),
text=checkbox,
textcolor=(0.8, 0.8, 0.8),
value=APPCONFIG[CONFIG][checkbox],
on_value_change_call=babase.Call(
self.update, checkbox),
scale=1.4,
maxwidth=430)
v -= 70
def update(self, config: str, change) -> None:
"""Change config and get our sounds
Args:
config: str
change: any
"""
try:
if change == True and config == "Fly":
bui.screenmessage("Some maps may not work good for flying",
color=(1, 0, 0))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('error').play()
try:
if change == True and config == "SuperPunch":
bui.screenmessage("SuperPunch Activated",
color=(1, 0, 0))
elif change == False and config == "SuperPunch":
bui.screenmessage("Super Punch Deactivated",
color=(0.5, 0, 0))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('spazOw').play()
try:
if change == True and config == "IceOnly":
bui.screenmessage("Ice Bombs Activated",
color=(0.1, 1, 1))
elif change == False and config == "IceOnly":
bui.screenmessage("Ice Bombs Deactivated",
color=(1, 0, 0))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('spazOw').play()
try:
if change == True and config == "StickyOnly":
bui.screenmessage("Sticky Bombs Activated",
color=(0, 1, 0))
elif change == False and config == "StickyOnly":
bui.screenmessage("Sticky Bombs Deactivated",
color=(1, 0, 0))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('spazOw').play()
try:
if change == True and config == "ImpactOnly":
bui.screenmessage("Impact Bombs Activated",
color=(0.5, 0.5, 0.5))
elif change == False and config == "ImpactOnly":
bui.screenmessage("Impact Bombs Deactivated",
color=(1, 0, 0))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('spazOw').play()
try:
if change == True and config == "More Are Coming":
bui.screenmessage("Check out https://discord.gg/2RKd9QQdQY For More Mods",
color=(4, 9, 2))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('cheer').play()
try:
if change == True and config == "Credits":
bui.screenmessage("To Pranav Made The Mod and Emily For Ideas, Thx",
color=(4, 9, 2))
update_config(config, change)
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('cheer').play()
def _back(self) -> None:
"""Kill the window and get back to previous one
"""
bui.containerwidget(edit=self._root_widget,
transition='out_scale')
bui.app.ui_v1.set_main_menu_window(
AllSettingsWindow(
transition='in_left').get_root_widget(), from_window=self._root_widget)
def ishost():
session = bs.get_foreground_host_session()
with session.context:
for player in session.sessionplayers:
if player.inputdevice.client_id == -1:
return True
def activity_loop():
if bs.get_foreground_host_activity() is not None:
activity = bs.get_foreground_host_activity()
with activity.context:
for player in activity.players:
if not ishost() or not player.actor:
return
config = APPCONFIG[CONFIG]
player.actor.node.invincible = config["Unlimited Heath"]
player.actor.node.fly = config["Fly"]
player.actor.node.hockey = config["SpeedBoots"]
if config["SuperPunch"]:
player.actor._punch_power_scale = 2
elif not config["SuperPunch"]:
player.actor._punch_power_scale = 1.2
if config["IceOnly"]:
player.actor.bomb_type = 'ice'
elif not config["IceOnly"]:
player.actor.bomb_type = 'normal'
player.actor.bomb_count = 1
if config["ImpactOnly"]:
player.actor.bomb_type = 'impact'
player.actor.bomb_count = 1
if config["StickyOnly"]:
player.actor.bomb_type = 'sticky'
player.actor.bomb_count = 1
if config["Infinite Bombs"]:
player.actor.bomb_count = 100
timer = babase.AppTimer(2, activity_loop, repeat=True)

View file

@ -0,0 +1,112 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import bascenev1lib
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
pass
class BombPickupMessage:
""" message says that someone pick up the dropped bomb """
# for bs.FreezeMessage
freeze: bool = True
# ba_meta export plugin
class Plugin(babase.Plugin):
# there are two ways to ignore our team player hits
# either change playerspaz handlemessage or change spaz handlemessage
def playerspaz_new_handlemessage(func: fuction) -> fuction:
def wrapper(*args, **kwargs):
global freeze
# only run if session is dual team
if isinstance(args[0].activity.session, bs.DualTeamSession):
# when spaz got hurt by any reason this statement is runs.
if isinstance(args[1], bs.HitMessage):
our_team_players: list[type(args[0]._player)]
# source_player
attacker = args[1].get_source_player(type(args[0]._player))
# our team payers
our_team_players = args[0]._player.team.players.copy()
if len(our_team_players) > 0:
# removing our self
our_team_players.remove(args[0]._player)
# if we honding teammate or if we have a shield, do hit.
for player in our_team_players:
if player.actor.exists() and args[0]._player.actor.exists():
if args[0]._player.actor.node.hold_node == player.actor.node or args[0]._player.actor.shield:
our_team_players.remove(player)
break
if attacker in our_team_players:
freeze = False
return None
else:
freeze = True
# if ice_bomb blast hits any spaz this statement runs.
elif isinstance(args[1], bs.FreezeMessage):
if not freeze:
freeze = True # use it and reset it
return None
# orignal unchanged code goes here
func(*args, **kwargs)
return wrapper
# replace original fuction to modified function
bascenev1lib.actor.playerspaz.PlayerSpaz.handlemessage = playerspaz_new_handlemessage(
bascenev1lib.actor.playerspaz.PlayerSpaz.handlemessage)
# let's add a message when bomb is pick by player
def bombfact_new_init(func: function) -> function:
def wrapper(*args):
func(*args) # original code
args[0].bomb_material.add_actions(
conditions=('they_have_material', SharedObjects.get().pickup_material),
actions=('message', 'our_node', 'at_connect', BombPickupMessage()),
)
return wrapper
# you get the idea
bascenev1lib.actor.bomb.BombFactory.__init__ = bombfact_new_init(
bascenev1lib.actor.bomb.BombFactory.__init__)
def bomb_new_handlemessage(func: function) -> function:
def wrapper(*args, **kwargs):
# only run if session is dual team
if isinstance(args[0].activity.session, bs.DualTeamSession):
if isinstance(args[1], BombPickupMessage):
# get the pickuper and assign the pickuper to the source_player(attacker) of bomb blast
for player in args[0].activity.players:
if player.actor.exists():
if player.actor.node.hold_node == args[0].node:
args[0]._source_player = player
break
func(*args, **kwargs) # original
return wrapper
bascenev1lib.actor.bomb.Bomb.handlemessage = bomb_new_handlemessage(
bascenev1lib.actor.bomb.Bomb.handlemessage)

View file

@ -0,0 +1,85 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# 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
import random
from bascenev1lib.actor.spaz import Spaz
from bascenev1lib.actor.spazfactory import SpazFactory
if TYPE_CHECKING:
pass
Spaz._old_init = Spaz.__init__
def __init__(self,
color: Sequence[float] = (1.0, 1.0, 1.0),
highlight: Sequence[float] = (0.5, 0.5, 0.5),
character: str = 'Spaz',
source_player: bs.Player = None,
start_invincible: bool = True,
can_accept_powerups: bool = True,
powerups_expire: bool = False,
demo_mode: bool = False):
self._old_init(color, highlight, character, source_player, start_invincible,
can_accept_powerups, powerups_expire, demo_mode)
if self.source_player:
self.equip_shields()
def animate_shield():
if not self.shield:
return
bs.animate_array(self.shield, 'color', 3, {
0.0: self.shield.color,
0.2: (random.random(), random.random(), random.random())
})
bs.timer(0.2, animate_shield, repeat=True)
self.impact_scale = 0
def equip_shields(self, decay: bool = False) -> None:
"""
Give this spaz a nice energy shield.
"""
if not self.node:
babase.print_error('Can\'t equip shields; no node.')
return
factory = SpazFactory.get()
if self.shield is None:
self.shield = bs.newnode('shield',
owner=self.node,
attrs={
'color': (0.3, 0.2, 2.0),
'radius': 1.3
})
self.node.connectattr('position_center', self.shield, 'position')
self.shield_hitpoints = self.shield_hitpoints_max = 650
self.shield_decay_rate = factory.shield_decay_rate if decay else 0
self.shield.hurt = 0
factory.shield_up_sound.play(1.0, position=self.node.position)
if self.impact_scale == 0:
return
if self.shield_decay_rate > 0:
self.shield_decay_timer = bs.Timer(0.5,
bs.WeakCall(self.shield_decay),
repeat=True)
# So user can see the decay.
self.shield.always_show_health_bar = True
# ba_meta export plugin
class InfinityShieldPlugin(babase.Plugin):
Spaz.__init__ = __init__
Spaz.equip_shields = equip_shields

View file

@ -0,0 +1,50 @@
# Ported by brostos to api 8
# Tool used to make porting easier.(https://github.com/bombsquad-community/baport)
"""Only Night."""
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs
from bascenev1._gameactivity import GameActivity
if TYPE_CHECKING:
pass
# ba_meta export plugin
class OnlyNight(babase.Plugin):
GameActivity.old_on_transition_in = GameActivity.on_transition_in
def new_on_transition_in(self) -> None:
self.old_on_transition_in()
gnode = bs.getactivity().globalsnode
if self.map.getname() in [
"Monkey Face",
"Rampage",
"Roundabout",
"Step Right Up",
"Tip Top",
"Zigzag",
"The Pad",
]:
gnode.tint = (0.4, 0.4, 0.4)
elif self.map.getname() in [
"Big G",
"Bridgit",
"Courtyard",
"Crag Castle",
"Doom Shroom",
"Football Stadium",
"Happy Thoughts",
"Hockey Stadium",
]:
gnode.tint = (0.5, 0.5, 0.5)
else:
gnode.tint = (0.3, 0.3, 0.3)
GameActivity.on_transition_in = new_on_transition_in

577
plugins/utilities/tag.py Normal file
View file

@ -0,0 +1,577 @@
# Ported by brostos to api 8
# Tool used to make porting easier.(https://github.com/bombsquad-community/baport)
"""
I apreciate any kind of modification. So feel free to use or edit code or change credit string.... no problem.
really awsome servers:
Bombsquad Consultancy Service - https://discord.gg/2RKd9QQdQY
bombspot - https://discord.gg/ucyaesh
cyclones - https://discord.gg/pJXxkbQ7kH
how to use:
Account -> PlayerProfile -> Edit(new profile -> edit)
Open profile you like (every profile has dirrent tags, settings (Configs))
enable tag for profile you like, edit tag you want. enable cool flashy animation
"""
from __future__ import annotations
from bauiv1lib.profile.edit import EditProfileWindow
from bauiv1lib.colorpicker import ColorPicker
from bauiv1lib.popup import PopupMenu
from bascenev1lib.actor.playerspaz import PlayerSpaz
from baenv import TARGET_BALLISTICA_BUILD as build_number
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
from typing import (
Tuple,
Optional,
Sequence,
Union,
Callable,
Any,
List,
cast
)
__version__ = 2.0
__author__ = "pranav1711#2006"
# Default Confings/Settings
Configs = {
"enabletag": False,
"tag": "",
"scale": "medium",
"opacity": 1.0,
"shadow": 0.0,
"animtag": False,
"frequency": 0.5
}
# Useful global fucntions
def setconfigs() -> None:
"""
Set required defualt configs for mod
"""
cnfg = babase.app.config
profiles = cnfg['Player Profiles']
if not "TagConf" in cnfg:
cnfg["TagConf"] = {}
for p in profiles:
if not p in cnfg["TagConf"]:
cnfg["TagConf"][str(p)] = Configs
babase.app.config.apply_and_commit()
def getanimcolor(name: str) -> dict:
"""
Returns dictnary of colors with prefective time -> {seconds: (r, g, b)}
"""
freq = babase.app.config['TagConf'][str(name)]['frequency']
s1 = 0.0
s2 = s1 + freq
s3 = s2 + freq
animcolor = {
s1: (1, 0, 0),
s2: (0, 1, 0),
s3: (0, 0, 1)
}
return animcolor
def gethostname() -> str:
"""
Return player name, by using -1 only host can use tags.
"""
session = bs.get_foreground_host_session()
with session.context:
for player in session.sessionplayers:
if player.inputdevice.client_id == -1:
name = player.getname(full=True, icon=False)
break
if name == bui.app.plus.get_v1_account_name:
return '__account__'
return name
# Dummy functions for extend functionality for class object
PlayerSpaz.init = PlayerSpaz.__init__
EditProfileWindow.init = EditProfileWindow.__init__
# PlayerSpaz object at -> bascenev1lib.actor.playerspaz
def NewPlayerSzapInit(self,
player: bs.Player,
color: Sequence[float] = (1.0, 1.0, 1.0),
highlight: Sequence[float] = (0.5, 0.5, 0.5),
character: str = 'Spaz',
powerups_expire: bool = True) -> None:
self.init(player, color, highlight, character, powerups_expire)
self.curname = gethostname()
try:
cnfg = babase.app.config["TagConf"]
if cnfg[str(self.curname)]["enabletag"]:
# Tag node
self.mnode = bs.newnode('math', owner=self.node, attrs={
'input1': (0, 1.5, 0), 'operation': 'add'})
self.node.connectattr('torso_position', self.mnode, 'input2')
tagtext = cnfg[str(self.curname)]["tag"]
opacity = cnfg[str(self.curname)]["opacity"]
shadow = cnfg[str(self.curname)]["shadow"]
sl = cnfg[str(self.curname)]["scale"]
scale = 0.01 if sl == 'mediam' else 0.009 if not sl == 'large' else 0.02
self.Tag = bs.newnode(
type='text',
owner=self.node,
attrs={
'text': str(tagtext),
'in_world': True,
'shadow': shadow,
'color': (0, 0, 0),
'scale': scale,
'opacity': opacity,
'flatness': 1.0,
'h_align': 'center'})
self.mnode.connectattr('output', self.Tag, 'position')
if cnfg[str(self.curname)]["animtag"]:
kys = getanimcolor(self.curname)
bs.animate_array(node=self.Tag, attr='color', size=3, keys=kys, loop=True)
except Exception:
pass
def NewEditProfileWindowInit(self,
existing_profile: Optional[str],
in_main_menu: bool,
transition: str = 'in_right') -> None:
"""
New boilerplate for editprofilewindow, addeds button to call TagSettings window
"""
self.existing_profile = existing_profile
self.in_main_menu = in_main_menu
self.init(existing_profile, in_main_menu, transition)
v = self._height - 115.0
x_inset = self._x_inset
b_width = 50
b_height = 30
self.tagwinbtn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
position=(505 + x_inset, v - 38 - 15),
size=(b_width, b_height),
color=(0.6, 0.5, 0.6),
label='Tag',
button_type='square',
text_scale=1.2,
on_activate_call=babase.Call(_on_tagwinbtn_press, self))
def _on_tagwinbtn_press(self):
"""
Calls tag config window passes all paramisters
"""
bui.containerwidget(edit=self._root_widget, transition='out_scale')
bui.app.ui_v1.set_main_menu_window(
TagWindow(self.existing_profile,
self.in_main_menu,
self._name,
transition='in_right').get_root_widget(), from_window=self._root_widget)
# ba_meta require api 8
# ba_meta export plugin
class Tag(babase.Plugin):
def __init__(self) -> None:
"""
Tag above actor player head, replacing PlayerSpaz class for getting actor,
using EditProfileWindow for UI.
"""
if _babase.env().get("build_number", 0) >= 20327:
setconfigs()
self.Replace()
def Replace(self) -> None:
"""
Replacing bolierplates no harm to relative funtionality only extending
"""
PlayerSpaz.__init__ = NewPlayerSzapInit
EditProfileWindow.__init__ = NewEditProfileWindowInit
class TagWindow(bui.Window):
def __init__(self,
existing_profile: Optional[str],
in_main_menu: bool,
profilename: str,
transition: Optional[str] = 'in_right'):
self.existing_profile = existing_profile
self.in_main_menu = in_main_menu
self.profilename = profilename
uiscale = bui.app.ui_v1.uiscale
self._width = 870.0 if uiscale is babase.UIScale.SMALL else 670.0
self._height = (390.0 if uiscale is babase.UIScale.SMALL else
450.0 if uiscale is babase.UIScale.MEDIUM else 520.0)
extra_x = 100 if uiscale is babase.UIScale.SMALL else 0
self.extra_x = extra_x
top_extra = 20 if uiscale is babase.UIScale.SMALL else 0
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height),
transition=transition,
scale=(2.06 if uiscale is babase.UIScale.SMALL else
1.4 if uiscale is babase.UIScale.MEDIUM else 1.0)))
self._back_button = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
selectable=False, # FIXME: when press a in text field it selets to button
position=(52 + self.extra_x, self._height - 60),
size=(60, 60),
scale=0.8,
label=babase.charstr(babase.SpecialChar.BACK),
button_type='backSmall',
on_activate_call=self._back)
bui.containerwidget(edit=self._root_widget, cancel_button=self._back_button)
self._save_button = bui.buttonwidget(
parent=self._root_widget,
position=(self._width - (177 + extra_x),
self._height - 60),
size=(155, 60),
color=(0, 0.7, 0.5),
autoselect=True,
selectable=False, # FIXME: when press a in text field it selets to button
scale=0.8,
label=babase.Lstr(resource='saveText'),
on_activate_call=self.on_save)
bui.widget(edit=self._save_button, left_widget=self._back_button)
bui.widget(edit=self._back_button, right_widget=self._save_button)
bui.containerwidget(edit=self._root_widget, start_button=self._save_button)
self._title_text = bui.textwidget(
parent=self._root_widget,
position=(0, self._height - 52 - top_extra),
size=(self._width, 25),
text='Tag',
color=bui.app.ui_v1.title_color,
scale=1.5,
h_align='center',
v_align='top')
self._scroll_width = self._width - (100 + 2 * extra_x)
self._scroll_height = self._height - 115.0
self._sub_width = self._scroll_width * 0.95
self._sub_height = 724.0
self._spacing = 32
self._extra_button_spacing = self._spacing * 2.5
self._scrollwidget = bui.scrollwidget(
parent=self._root_widget,
position=(50 + extra_x, 50),
simple_culling_v=20.0,
highlight=False,
size=(self._scroll_width,
self._scroll_height),
selection_loops_to_parent=True)
bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget)
self._subcontainer = bui.containerwidget(
parent=self._scrollwidget,
size=(self._sub_width,
self._sub_height),
background=False,
selection_loops_to_parent=True)
v = self._sub_height - 35
v -= self._spacing * 1.2
self._prof = babase.app.config["TagConf"][self.profilename]
self.enabletagcb = bui.checkboxwidget(
parent=self._subcontainer,
autoselect=False,
position=(10.0, v + 30),
size=(10, 10),
text='Enable Tag',
textcolor=(0.8, 0.8, 0.8),
value=self._prof['enabletag'],
on_value_change_call=babase.Call(self.change_val, [f'{self.profilename}', 'enabletag']),
scale=1.1 if uiscale is babase.UIScale.SMALL else 1.5,
maxwidth=430)
self.tag_text = bui.textwidget(
parent=self._subcontainer,
text='Tag',
position=(25.0, v - 30),
flatness=1.0,
scale=1.55,
maxwidth=430,
h_align='center',
v_align='center',
color=(0.8, 0.8, 0.8))
self.tagtextfield = bui.textwidget(
parent=self._subcontainer,
position=(100.0, v - 45),
size=(350, 50),
text=self._prof["tag"],
h_align='center',
v_align='center',
max_chars=16,
autoselect=True,
editable=True,
padding=4,
color=(0.9, 0.9, 0.9, 1.0))
self.tag_color_text = bui.textwidget(
parent=self._subcontainer,
text='Color',
position=(40.0, v - 80),
flatness=1.0,
scale=1.25,
maxwidth=430,
h_align='center',
v_align='center',
color=(0.8, 0.8, 0.8))
self.tag_scale_text = bui.textwidget(
parent=self._subcontainer,
text='Scale',
position=(40.0, v - 130),
flatness=1.0,
scale=1.25,
maxwidth=430,
h_align='center',
v_align='center',
color=(0.8, 0.8, 0.8))
self.tag_scale_button = PopupMenu(
parent=self._subcontainer,
position=(330.0, v - 145),
width=150,
autoselect=True,
on_value_change_call=bs.WeakCall(self._on_menu_choice),
choices=['large', 'medium', 'small'],
button_size=(150, 50),
# choices_display=('large', 'medium', 'small'),
current_choice=self._prof["scale"])
CustomConfigNumberEdit(
parent=self._subcontainer,
position=(40.0, v - 180),
xoffset=65,
displayname='Opacity',
configkey=['TagConf', f'{self.profilename}', 'opacity'],
changesound=False,
minval=0.5,
maxval=2.0,
increment=0.1,
textscale=1.25)
CustomConfigNumberEdit(
parent=self._subcontainer,
position=(40.0, v - 230),
xoffset=65,
displayname='Shadow',
configkey=['TagConf', f'{self.profilename}', 'shadow'],
changesound=False,
minval=0.0,
maxval=2.0,
increment=0.1,
textscale=1.25)
self.enabletaganim = bui.checkboxwidget(
parent=self._subcontainer,
autoselect=True,
position=(10.0, v - 280),
size=(10, 10),
text='Animate tag',
textcolor=(0.8, 0.8, 0.8),
value=self._prof['enabletag'],
on_value_change_call=babase.Call(self.change_val, [f'{self.profilename}', 'animtag']),
scale=1.1 if uiscale is babase.UIScale.SMALL else 1.5,
maxwidth=430)
CustomConfigNumberEdit(
parent=self._subcontainer,
position=(40.0, v - 330),
xoffset=65,
displayname='Frequency',
configkey=['TagConf', f'{self.profilename}', 'frequency'],
changesound=False,
minval=0.1,
maxval=5.0,
increment=0.1,
textscale=1.25)
def _back(self) -> None:
"""
transit window into back window
"""
bui.containerwidget(edit=self._root_widget,
transition='out_scale')
bui.app.ui_v1.set_main_menu_window(EditProfileWindow(
self.existing_profile,
self.in_main_menu,
transition='in_left').get_root_widget(), from_window=self._root_widget)
def change_val(self, config: List[str], val: bool) -> None:
"""
chamges the value of check boxes
"""
cnfg = babase.app.config["TagConf"]
try:
cnfg[config[0]][config[1]] = val
bui.getsound('gunCocking').play()
except Exception:
bui.screenmessage("error", color=(1, 0, 0))
bui.getsound('error').play()
babase.app.config.apply_and_commit()
def _on_menu_choice(self, choice: str):
"""
Changes the given choice in configs
"""
cnfg = babase.app.config["TagConf"][self.profilename]
cnfg["scale"] = choice
babase.app.config.apply_and_commit()
def on_save(self):
"""
Gets the text in text field of tag and then save it
"""
text: str = cast(str, bui.textwidget(query=self.tagtextfield))
profile = babase.app.config["TagConf"][self.profilename]
if not text == "" or not text.strip():
profile['tag'] = text
babase.app.config.apply_and_commit()
bui.getsound('gunCocking').play()
else:
bui.screenmessage(f"please define tag", color=(1, 0, 0))
bui.getsound('error').play()
bui.containerwidget(edit=self._root_widget,
transition='out_scale')
bui.app.ui_v1.set_main_menu_window(EditProfileWindow(
self.existing_profile,
self.in_main_menu,
transition='in_left').get_root_widget(), from_window=self._root_widget)
class CustomConfigNumberEdit:
"""A set of controls for editing a numeric config value.
It will automatically save and apply the config when its
value changes.
Attributes:
nametext
The text widget displaying the name.
valuetext
The text widget displaying the current value.
minusbutton
The button widget used to reduce the value.
plusbutton
The button widget used to increase the value.
"""
def __init__(self,
parent: bui.Widget,
configkey: List[str],
position: Tuple[float, float],
minval: float = 0.0,
maxval: float = 100.0,
increment: float = 1.0,
callback: Callable[[float], Any] = None,
xoffset: float = 0.0,
displayname: Union[str, babase.Lstr] = None,
changesound: bool = True,
textscale: float = 1.0):
self._minval = minval
self._maxval = maxval
self._increment = increment
self._callback = callback
self._configkey = configkey
self._value = babase.app.config[configkey[0]][configkey[1]][configkey[2]]
self.nametext = bui.textwidget(
parent=parent,
position=position,
size=(100, 30),
text=displayname,
maxwidth=160 + xoffset,
color=(0.8, 0.8, 0.8, 1.0),
h_align='left',
v_align='center',
scale=textscale)
self.valuetext = bui.textwidget(
parent=parent,
position=(246 + xoffset, position[1]),
size=(60, 28),
editable=False,
color=(0.3, 1.0, 0.3, 1.0),
h_align='right',
v_align='center',
text=str(self._value),
padding=2)
self.minusbutton = bui.buttonwidget(
parent=parent,
position=(330 + xoffset, position[1]),
size=(28, 28),
label='-',
autoselect=True,
on_activate_call=babase.Call(self._down),
repeat=True,
enable_sound=changesound)
self.plusbutton = bui.buttonwidget(parent=parent,
position=(380 + xoffset, position[1]),
size=(28, 28),
label='+',
autoselect=True,
on_activate_call=babase.Call(self._up),
repeat=True,
enable_sound=changesound)
bui.uicleanupcheck(self, self.nametext)
self._update_display()
def _up(self) -> None:
self._value = min(self._maxval, self._value + self._increment)
self._changed()
def _down(self) -> None:
self._value = max(self._minval, self._value - self._increment)
self._changed()
def _changed(self) -> None:
self._update_display()
if self._callback:
self._callback(self._value)
babase.app.config[self._configkey[0]][self._configkey[1]
][self._configkey[2]] = float(str(f'{self._value:.1f}'))
babase.app.config.apply_and_commit()
def _update_display(self) -> None:
bui.textwidget(edit=self.valuetext, text=f'{self._value:.1f}')

View file

@ -0,0 +1,96 @@
# Ported to api 8 by brostos using baport.(https://github.com/bombsquad-community/baport)
# Released under the MIT License. See LICENSE for details.
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.spazfactory import SpazFactory
import babase
import bauiv1 as bui
import bascenev1 as bs
import math
import os
import _babase
import shutil
if TYPE_CHECKING:
pass
DECIMAL_LIMIT = 7
PlayerSpaz.supershit = PlayerSpaz.__init__
def ShitInit(self,
player: bs.Player,
color: Sequence[float] = (1.0, 1.0, 1.0),
highlight: Sequence[float] = (0.5, 0.5, 0.5),
character: str = 'Spaz',
powerups_expire: bool = True) -> None:
self.supershit(player, color, highlight, character, powerups_expire)
self.offt = bs.newnode('math', owner=self.node, attrs={
'input1': (1.2, 1.8, -0.7), 'operation': 'add'})
self.node.connectattr('torso_position', self.offt, 'input2')
self.txt = bs.newnode('text', owner=self.node, attrs={'text': '3.0', 'in_world': True, 'text': '0', 'shadow': 1.0, 'color': (
1, 0, 0), 'flatness': 0.5, 'scale': 0.01, 'h_align': 'right'})
p = self.node.position
self.xyz = 0
self.txt.text = "X: " + str(p[0]) + "\nY: " + str(p[1]) + "\nZ: " + str(p[2])
self.offt.connectattr('output', self.txt, 'position')
def update():
p = self.node.position
is_moving = abs(self.node.move_up_down) >= 0.01 or abs(self.node.move_left_right) >= 0.01
if is_moving:
self.xyz = (p[0], p[1], p[2])
self.txt.text = "X: " + str(round(self.xyz[0], DECIMAL_LIMIT)) + "\nY: " + str(
round(self.xyz[1], DECIMAL_LIMIT)) + "\nZ: " + str(round(self.xyz[2], DECIMAL_LIMIT))
bs.timer(0.1, update, repeat=True)
def replaceable_punch(self) -> None:
"""
Called to 'press punch' on this spaz;
used for player or AI connections.
"""
if not self.node or self.frozen or self.node.knockout > 0.0:
return
index = 0
path_aid = _babase.env()['python_directory_user'] + '/Saved XYZ'
path, dirs, files = next(os.walk(path_aid))
index += len(files)
c27 = str(index + 1)
with open(path_aid + '/coords' + c27 + '.txt', 'w') as gg:
gg.write("X: " + str(round(self.xyz[0], DECIMAL_LIMIT)) + "\nY: " + str(round(self.xyz[1], DECIMAL_LIMIT)) + "\nZ: " + str(round(self.xyz[2], DECIMAL_LIMIT)) +
'\n\n' + '(' + str(round(self.xyz[0], DECIMAL_LIMIT)) + ', ' + str(round(self.xyz[1], DECIMAL_LIMIT)) + ', ' + str(round(self.xyz[2], DECIMAL_LIMIT)) + ')')
bui.screenmessage("Coordinates saved in: " + "BombSquad/Saved XYZ/" + "coords" + c27)
if _babase.app.classic.platform == 'android':
_babase.android_media_scan_file(path_aid)
t_ms = bs.time() * 1000
assert isinstance(t_ms, int)
if t_ms - self.last_punch_time_ms >= self._punch_cooldown:
if self.punch_callback is not None:
self.punch_callback(self)
self._punched_nodes = set() # Reset this.
self.last_punch_time_ms = t_ms
self.node.punch_pressed = True
if not self.node.hold_node:
bs.timer(
0.1,
bs.WeakCall(self._safe_play_sound,
SpazFactory.get().swish_sound, 0.8))
self._turbo_filter_add_press('punch')
# ba_meta export plugin
class ragingspeedhorn(babase.Plugin):
try:
oath = _babase.env()['python_directory_user'] + '/Saved XYZ'
os.makedirs(oath, exist_ok=False)
except:
pass
PlayerSpaz.on_punch_press = replaceable_punch
PlayerSpaz.__init__ = ShitInit
PlayerSpaz.xyz = 0