# 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)