diff --git a/plugins/minigames.json b/plugins/minigames.json index b1fb800..f110956 100644 --- a/plugins/minigames.json +++ b/plugins/minigames.json @@ -249,6 +249,34 @@ "md5sum": "ec3980f3f3a5da96c27f4cbd61f98550" } } + }, + "shimla": { + "description": "Death match with elevators, 2-D view.", + "external_url": "https://www.youtube.com/channel/UCaQajfKHrTPgiOhuias5iPg", + "authors": [ + { + "name": "Mr.Smoothy", + "email": "smoothy@bombsquad.ga", + "discord": "mr.smoothy#5824" + } + ], + "versions": { + "1.0.0": null + } + }, + "air_soccer": { + "description": "Play soccer while flying in air", + "external_url": "https://youtu.be/j6FFk7E6W_U", + "authors": [ + { + "name": "Mr.Smoothy", + "email": "smoothy@bombsquad.ga", + "discord": "mr.smoothy#5824" + } + ], + "versions": { + "1.0.0": null + } } } } \ No newline at end of file diff --git a/plugins/minigames/air_soccer.py b/plugins/minigames/air_soccer.py new file mode 100644 index 0000000..83ecd46 --- /dev/null +++ b/plugins/minigames/air_soccer.py @@ -0,0 +1,639 @@ +# Released under the MIT License. See LICENSE for details. +# BY Stary_Agent +"""Hockey game and support classes.""" + +# ba_meta require api 7 +# (see https://ballistica.net/wiki/meta-tag-system) + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import ba,_ba +from bastd.actor.playerspaz import PlayerSpaz +from bastd.actor.scoreboard import Scoreboard +from bastd.actor.powerupbox import PowerupBoxFactory +from bastd.gameutils import SharedObjects + +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 + +def create_slope(self): + shared = SharedObjects.get() + x=5 + y=12 + for i in range(0,10): + ba.newnode('region',attrs={'position': (x, y, -5.52),'scale': (0.2,0.1,6),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + x = x+0.3 + y = y+0.1 + +class Puck(ba.Actor): + """A lovely giant hockey puck.""" + + def __init__(self, position: Sequence[float] = (0.0, 13.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: Dict[int, Player] = {} + self.scored = False + assert activity is not None + assert isinstance(activity, HockeyGame) + pmats = [shared.object_material, activity.puck_material] + self.node = ba.newnode('prop', + delegate=self, + attrs={ + 'model': activity.puck_model, + 'color_texture': activity.puck_tex, + 'body': 'sphere', + 'reflection': 'soft', + 'reflection_scale': [0.2], + 'gravity_scale':0.3, + 'shadow_size': 0.5, + 'is_area_of_interest': True, + 'position': self._spawn_pos, + 'materials': pmats + }) + ba.animate(self.node, 'model_scale', {0: 0, 0.2: 1.3, 0.26: 1}) + + def handlemessage(self, msg: Any) -> Any: + if isinstance(msg, ba.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, ba.OutOfBoundsMessage): + assert self.node + self.node.position = self._spawn_pos + + elif isinstance(msg, ba.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.team.id] = s_player + else: + super().handlemessage(msg) + + +class Player(ba.Player['Team']): + """Our player type for this game.""" + + +class Team(ba.Team[Player]): + """Our team type for this game.""" + + def __init__(self) -> None: + self.score = 0 + + +# ba_meta export game +class AirSoccerGame(ba.TeamGameActivity[Player, Team]): + """Ice hockey game.""" + + name = 'Epic Air Soccer' + description = 'Score some goals.' + available_settings = [ + ba.IntSetting( + 'Score to Win', + min_value=1, + default=1, + increment=1, + ), + ba.IntChoiceSetting( + 'Time Limit', + choices=[ + ('None', 0), + ('1 Minute', 60), + ('2 Minutes', 120), + ('5 Minutes', 300), + ('10 Minutes', 600), + ('20 Minutes', 1200), + ], + default=0, + ), + ba.FloatChoiceSetting( + 'Respawn Times', + choices=[ + ('Shorter', 0.1), + ('Short', 0.5), + ('Normal', 1.0), + ('Long', 2.0), + ('Longer', 4.0), + ], + default=1.0, + ), + ] + default_music = ba.MusicType.HOCKEY + + @classmethod + def supports_session_type(cls, sessiontype: Type[ba.Session]) -> bool: + return issubclass(sessiontype, ba.DualTeamSession) + + @classmethod + def get_supported_maps(cls, sessiontype: Type[ba.Session]) -> List[str]: + return ['Creative Thoughts'] + + def __init__(self, settings: dict): + super().__init__(settings) + shared = SharedObjects.get() + self.slow_motion = True + self._scoreboard = Scoreboard() + self._cheer_sound = ba.getsound('cheer') + self._chant_sound = ba.getsound('crowdChant') + self._foghorn_sound = ba.getsound('foghorn') + self._swipsound = ba.getsound('swip') + self._whistle_sound = ba.getsound('refWhistle') + self.puck_model = ba.getmodel('bomb') + self.puck_tex = ba.gettexture('landMine') + self.puck_scored_tex = ba.gettexture('landMineLit') + self._puck_sound = ba.getsound('metalHit') + self.puck_material = ba.Material() + 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)) + self._real_wall_material=ba.Material() + self._real_wall_material.add_actions( + + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + + self._real_wall_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self._goal_post_material=ba.Material() + self._goal_post_material.add_actions( + + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + + self._goal_post_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=( + ('modify_part_collision', 'collide', False), + ('modify_part_collision', 'physical', False) + + )) + # 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', ba.DieMessage()))) + self._score_region_material = ba.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._puck_spawn_pos: Optional[Sequence[float]] = None + self._score_regions: Optional[List[ba.NodeActor]] = None + self._puck: Optional[Puck] = None + self._score_to_win = int(settings['Score to Win']) + self._time_limit = float(settings['Time Limit']) + + def get_instance_description(self) -> Union[str, Sequence]: + if self._score_to_win == 1: + return 'Score a goal.' + return 'Score ${ARG1} goals.', self._score_to_win + + def get_instance_description_short(self) -> Union[str, Sequence]: + if self._score_to_win == 1: + return 'score a goal' + return 'score ${ARG1} goals', self._score_to_win + + def on_begin(self) -> None: + super().on_begin() + + self.setup_standard_time_limit(self._time_limit) + self.setup_standard_powerup_drops() + self._puck_spawn_pos =(0,16.9,-5.5) + self._spawn_puck() + self.make_map() + + # Set up the two score regions. + defs = self.map.defs + self._score_regions = [] + self._score_regions.append( + ba.NodeActor( + ba.newnode('region', + attrs={ + 'position': (17,14.5,-5.52), + 'scale': (1,3,1), + 'type': 'box', + 'materials': [self._score_region_material] + }))) + self._score_regions.append( + ba.NodeActor( + ba.newnode('region', + attrs={ + 'position': (-17,14.5,-5.52), + 'scale': (1,3,1), + 'type': 'box', + 'materials': [self._score_region_material] + }))) + self._update_scoreboard() + ba.playsound(self._chant_sound) + + def on_team_join(self, team: Team) -> None: + self._update_scoreboard() + + def _handle_puck_player_collide(self) -> None: + collision = ba.getcollision() + try: + puck = collision.sourcenode.getdelegate(Puck, True) + player = collision.opposingnode.getdelegate(PlayerSpaz, + True).getplayer( + Player, True) + except ba.NotFoundError: + return + + puck.last_players_to_touch[player.team.id] = player + + def make_map(self): + shared = SharedObjects.get() + _ba.get_foreground_host_activity()._map.leftwall.materials= [shared.footing_material,self._real_wall_material ] + + _ba.get_foreground_host_activity()._map.rightwall.materials=[shared.footing_material,self._real_wall_material ] + + _ba.get_foreground_host_activity()._map.topwall.materials=[shared.footing_material,self._real_wall_material ] + self.floorwall=ba.newnode('region',attrs={'position': (0, 5, -5.52),'scale': (35.4,0.2,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + ba.newnode('locator', attrs={'shape':'box', 'position':(0, 5, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(35.4,0.2,2)}) + + + self.create_goal_post(-16.65,12.69) + self.create_goal_post(-16.65,16.69) + + self.create_goal_post(16.65,12.69) + self.create_goal_post(16.65,16.69) + + self.create_static_step(0,16.29) + + self.create_static_step(4.35,11.1) + self.create_static_step(-4.35,11.1) + + self.create_vertical(10, 15.6) + self.create_vertical(-10, 15.6) + + def create_static_step(self,x,y): + floor="" + for i in range(0,7): + floor+="_ " + shared = SharedObjects.get() + step={} + step["r"]=ba.newnode('region',attrs={'position': (x, y, -5.52),'scale': (3,0.1,6),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + ba.newnode('locator', attrs={'shape':'box', 'position':( x, y, -5.52), 'color':(1,1,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(3,0.1,2)}) + + return step + def create_goal_post(self,x,y): + shared = SharedObjects.get() + if x > 0: + color = (1,0,0) #change to team specific color + else: + color = (0,0,1) + floor="" + for i in range(0,4): + floor+="_ " + ba.newnode('region',attrs={'position': (x-0.2, y, -5.52),'scale': (1.8,0.1,6),'type': 'box','materials': [shared.footing_material,self._goal_post_material]}) + + ba.newnode('locator', attrs={'shape':'box', 'position':( x-0.2, y, -5.52), 'color': color, 'opacity':1,'draw_beauty':True,'additive':False,'size':(1.8,0.1,2)}) + + + def create_vertical(self,x,y): + shared = SharedObjects.get() + floor = "" + for i in range(0,4): + floor +="|\n" + ba.newnode('region',attrs={'position': (x, y, -5.52),'scale': (0.1,2.8,1),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + ba.newnode('locator', attrs={'shape':'box', 'position':( x, y, -5.52), 'color':(1,1,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(0.1,2.8,2)}) + + + def spawn_player_spaz(self, + player: Player, + position: Sequence[float] = None, + angle: float = None) -> PlayerSpaz: + """Intercept new spazzes and add our team material for them.""" + if player.team.id==0: + position=(-10.75152479, 5.057427485, -5.52) + elif player.team.id==1: + position=(8.75152479, 5.057427485, -5.52) + + + spaz = super().spawn_player_spaz(player, position, angle) + return spaz + + def _kill_puck(self) -> None: + self._puck = None + + 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 = ba.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(ba.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() + + ba.playsound(self._foghorn_sound) + ba.playsound(self._cheer_sound) + + 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). + ba.timer(1.0, self._kill_puck) + + light = ba.newnode('light', + attrs={ + 'position': ba.getcollision().position, + 'height_attenuated': False, + 'color': (1, 0, 0) + }) + ba.animate(light, 'intensity', {0: 0, 0.5: 1, 1.0: 0}, loop=True) + ba.timer(1.0, light.delete) + + ba.cameraflash(duration=10.0) + self._update_scoreboard() + + def end_game(self) -> None: + results = ba.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, ba.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(): + ba.timer(3.0, self._spawn_puck) + else: + super().handlemessage(msg) + + def _flash_puck_spawn(self) -> None: + light = ba.newnode('light', + attrs={ + 'position': self._puck_spawn_pos, + 'height_attenuated': False, + 'color': (1, 0, 0) + }) + ba.animate(light, 'intensity', {0.0: 0, 0.25: 1, 0.5: 0}, loop=True) + ba.timer(1.0, light.delete) + + def _spawn_puck(self) -> None: + ba.playsound(self._swipsound) + ba.playsound(self._whistle_sound) + self._flash_puck_spawn() + assert self._puck_spawn_pos is not None + self._puck = Puck(position=self._puck_spawn_pos) + + +class mapdefs: + points = {} + # noinspection PyDictCreation + boxes = {} + boxes['area_of_interest_bounds'] = (-1.045859963, 12.67722855, + -5.401537075) + (0.0, 0.0, 0.0) + ( + 42.46156851, 20.94044653, 0.6931564611) + points['ffa_spawn1'] = (-9.295167711, 8.010664315, + -5.44451005) + (1.555840357, 1.453808816, 0.1165648888) + points['ffa_spawn2'] = (7.484707127, 8.172681752, -5.614479365) + ( + 1.553861796, 1.453808816, 0.04419853907) + points['ffa_spawn3'] = (9.55724115, 11.30789446, -5.614479365) + ( + 1.337925849, 1.453808816, 0.04419853907) + points['ffa_spawn4'] = (-11.55747023, 10.99170684, -5.614479365) + ( + 1.337925849, 1.453808816, 0.04419853907) + points['ffa_spawn5'] = (-1.878892369, 9.46490571, -5.614479365) + ( + 1.337925849, 1.453808816, 0.04419853907) + points['ffa_spawn6'] = (-0.4912812943, 5.077006397, -5.521672101) + ( + 1.878332089, 1.453808816, 0.007578097856) + points['flag1'] = (-11.75152479, 8.057427485, -5.52) + points['flag2'] = (9.840909039, 8.188634282, -5.52) + points['flag3'] = (-0.2195258696, 5.010273907, -5.52) + points['flag4'] = (-0.04605809154, 12.73369108, -5.52) + points['flag_default'] = (-0.04201942896, 12.72374492, -5.52) + boxes['map_bounds'] = (-0.8748348681, 9.212941713, -5.729538885) + ( + 0.0, 0.0, 0.0) + (42.09666006, 26.19950145, 7.89541168) + points['powerup_spawn1'] = (1.160232442, 6.745963662, -5.469115985) + points['powerup_spawn2'] = (-1.899700206, 10.56447241, -5.505721177) + points['powerup_spawn3'] = (10.56098871, 12.25165669, -5.576232453) + points['powerup_spawn4'] = (-12.33530337, 12.25165669, -5.576232453) + points['spawn1'] = (-9.295167711, 8.010664315, + -5.44451005) + (1.555840357, 1.453808816, 0.1165648888) + points['spawn2'] = (7.484707127, 8.172681752, + -5.614479365) + (1.553861796, 1.453808816, 0.04419853907) + points['spawn_by_flag1'] = (-9.295167711, 8.010664315, -5.44451005) + ( + 1.555840357, 1.453808816, 0.1165648888) + points['spawn_by_flag2'] = (7.484707127, 8.172681752, -5.614479365) + ( + 1.553861796, 1.453808816, 0.04419853907) + points['spawn_by_flag3'] = (-1.45994593, 5.038762459, -5.535288724) + ( + 0.9516389866, 0.6666414677, 0.08607244075) + points['spawn_by_flag4'] = (0.4932087091, 12.74493212, -5.598987003) + ( + 0.5245740665, 0.5245740665, 0.01941146064) +class CreativeThoughts(ba.Map): + """Freaking map by smoothy.""" + + defs = mapdefs + + name = 'Creative Thoughts' + + @classmethod + def get_play_types(cls) -> List[str]: + """Return valid play types for this map.""" + return [ + 'melee', 'keep_away', 'team_flag' + ] + + @classmethod + def get_preview_texture_name(cls) -> str: + return 'alwaysLandPreview' + + @classmethod + def on_preload(cls) -> Any: + data: Dict[str, Any] = { + 'model': ba.getmodel('alwaysLandLevel'), + 'bottom_model': ba.getmodel('alwaysLandLevelBottom'), + 'bgmodel': ba.getmodel('alwaysLandBG'), + 'collide_model': ba.getcollidemodel('alwaysLandLevelCollide'), + 'tex': ba.gettexture('alwaysLandLevelColor'), + 'bgtex': ba.gettexture('alwaysLandBGColor'), + 'vr_fill_mound_model': ba.getmodel('alwaysLandVRFillMound'), + 'vr_fill_mound_tex': ba.gettexture('vrFillMound') + } + return data + + @classmethod + def get_music_type(cls) -> ba.MusicType: + return ba.MusicType.FLYING + + def __init__(self) -> None: + super().__init__(vr_overlay_offset=(0, -3.7, 2.5)) + shared = SharedObjects.get() + self._fake_wall_material=ba.Material() + self._real_wall_material=ba.Material() + self._fake_wall_material.add_actions( + conditions=(('they_are_younger_than',9000),'and',('they_have_material', shared.player_material)), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self._real_wall_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self.background = ba.newnode( + 'terrain', + attrs={ + 'model': self.preloaddata['bgmodel'], + 'lighting': False, + 'background': True, + 'color_texture': ba.gettexture("rampageBGColor") + }) + + self.leftwall=ba.newnode('region',attrs={'position': (-17.75152479, 13, -5.52),'scale': (0.1,15.5,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + self.rightwall=ba.newnode('region',attrs={'position': (17.75, 13, -5.52),'scale': (0.1,15.5,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + self.topwall=ba.newnode('region',attrs={'position': (0, 21.0, -5.52),'scale': (35.4,0.2,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + ba.newnode('locator', attrs={'shape':'box', 'position':(-17.75152479, 13, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(0.1,15.5,2)}) + ba.newnode('locator', attrs={'shape':'box', 'position':(17.75, 13, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(0.1,15.5,2)}) + ba.newnode('locator', attrs={'shape':'box', 'position':(0, 21.0, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(35.4,0.2,2)}) + + gnode = ba.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 + + # throw out some tips on flying + txt = ba.newnode('text', + attrs={ + 'text': ba.Lstr(resource='pressJumpToFlyText'), + 'scale': 1.2, + 'maxwidth': 800, + 'position': (0, 200), + 'shadow': 0.5, + 'flatness': 0.5, + 'h_align': 'center', + 'v_attach': 'bottom' + }) + cmb = ba.newnode('combine', + owner=txt, + attrs={ + 'size': 4, + 'input0': 0.3, + 'input1': 0.9, + 'input2': 0.0 + }) + ba.animate(cmb, 'input3', {3.0: 0, 4.0: 1, 9.0: 1, 10.0: 0}) + cmb.connectattr('output', txt, 'color') + ba.timer(10.0, txt.delete) + + +try: + ba._map.register_map(CreativeThoughts) +except: + pass \ No newline at end of file diff --git a/plugins/minigames/shimla.py b/plugins/minigames/shimla.py new file mode 100644 index 0000000..6d19656 --- /dev/null +++ b/plugins/minigames/shimla.py @@ -0,0 +1,396 @@ +# Released under the MIT License. See LICENSE for details. +# +"""DeathMatch game and support classes.""" + +# ba_meta require api 7 +# (see https://ballistica.net/wiki/meta-tag-system) + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import ba +import _ba +from bastd.actor.playerspaz import PlayerSpaz +from bastd.actor.scoreboard import Scoreboard +from bastd.gameutils import SharedObjects +from bastd.game.deathmatch import DeathMatchGame, Player, Team +from bastd.gameutils import SharedObjects +if TYPE_CHECKING: + from typing import Any, Sequence, Dict, Type, List, Optional, Union + +# ba_meta export game +class ShimlaGame(DeathMatchGame): + name = 'Shimla' + + @classmethod + def supports_session_type(cls, sessiontype: Type[ba.Session]) -> bool: + return issubclass(sessiontype, ba.DualTeamSession) + + @classmethod + def get_supported_maps(cls, sessiontype: Type[ba.Session]) -> List[str]: + return ['Creative Thoughts'] + + def __init__(self, settings: dict): + super().__init__(settings) + shared = SharedObjects.get() + self.lifts = {} + self._real_wall_material = ba.Material() + self._real_wall_material.add_actions( + + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + + self._real_wall_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self._lift_material = ba.Material() + self._lift_material.add_actions( + + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self._lift_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=(('call', 'at_connect', self._handle_lift),), + ) + self._lift_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=(('call', 'at_disconnect', self._handle_lift_disconnect),), + ) + + def on_begin(self): + ba.getactivity().globalsnode.happy_thoughts_mode = False + super().on_begin() + + self.make_map() + ba.timer(2, self.disable_fly) + def disable_fly(self): + activity = _ba.get_foreground_host_activity() + + for players in activity.players: + players.actor.node.fly = False + def spawn_player_spaz( + self, + player: Player, + position: Sequence[float] | None = None, + angle: float | None = None, + ) -> PlayerSpaz: + """Intercept new spazzes and add our team material for them.""" + spaz = super().spawn_player_spaz(player, position, angle) + + spaz.connect_controls_to_player(enable_punch=True, + enable_bomb=True, + enable_pickup=True, + enable_fly=False, + enable_jump=True) + spaz.fly = False + return spaz + + def make_map(self): + shared = SharedObjects.get() + _ba.get_foreground_host_activity()._map.leftwall.materials = [ + shared.footing_material, self._real_wall_material] + + _ba.get_foreground_host_activity()._map.rightwall.materials = [ + shared.footing_material, self._real_wall_material] + + _ba.get_foreground_host_activity()._map.topwall.materials = [ + shared.footing_material, self._real_wall_material] + + self.floorwall1 = ba.newnode('region', attrs={'position': (-10, 5, -5.52), 'scale': + (15, 0.2, 2), 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + self.floorwall2 = ba.newnode('region', attrs={'position': (10, 5, -5.52), 'scale': ( + 15, 0.2, 2), 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + + self.wall1 = ba.newnode('region', attrs={'position': (0, 11, -6.90), 'scale': ( + 35.4, 20, 1), 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + self.wall2 = ba.newnode('region', attrs={'position': (0, 11, -4.14), 'scale': ( + 35.4, 20, 1), 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + + ba.newnode('locator', attrs={'shape': 'box', 'position': (-10, 5, -5.52), 'color': ( + 0, 0, 0), 'opacity': 1, 'draw_beauty': True, 'additive': False, 'size': (15, 0.2, 2)}) + + ba.newnode('locator', attrs={'shape': 'box', 'position': (10, 5, -5.52), 'color': ( + 0, 0, 0), 'opacity': 1, 'draw_beauty': True, 'additive': False, 'size': (15, 0.2, 2)}) + self.create_lift(-16.65, 8) + + self.create_lift(16.65, 8) + + self.create_static_step(0, 18.29) + self.create_static_step(0, 7) + + self.create_static_step(13, 17) + self.create_static_step(-13, 17) + self.create_slope(8, 15, True) + self.create_slope(-8, 15, False) + self.create_static_step(5, 15) + self.create_static_step(-5, 15) + + self.create_static_step(13, 12) + self.create_static_step(-13, 12) + self.create_slope(8, 10, True) + self.create_slope(-8, 10, False) + self.create_static_step(5, 10) + self.create_static_step(-5, 10) + + def create_static_step(self, x, y): + + shared = SharedObjects.get() + + ba.newnode('region', attrs={'position': (x, y, -5.52), 'scale': (5.5, 0.1, 6), + 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + ba.newnode('locator', attrs={'shape': 'box', 'position': (x, y, -5.52), 'color': ( + 1, 1, 0), 'opacity': 1, 'draw_beauty': True, 'additive': False, 'size': (5.5, 0.1, 2)}) + + def create_lift(self, x, y): + shared = SharedObjects.get() + color = (0.7, 0.6, 0.5) + + floor = ba.newnode('region', attrs={'position': (x, y, -5.52), 'scale': ( + 1.8, 0.1, 2), 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material ,self._lift_material]}) + + cleaner = ba.newnode('region', attrs={'position': (x, y, -5.52), 'scale': ( + 2, 0.3, 2), 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + + lift = ba.newnode('locator', attrs={'shape': 'box', 'position': ( + x, y, -5.52), 'color': color, 'opacity': 1, 'draw_beauty': True, 'additive': False, 'size': (1.8, 3.7, 2)}) + + _tcombine = ba.newnode('combine', + owner=floor, + attrs={ + 'input0': x, + 'input2': -5.5, + 'size': 3 + }) + mnode = ba.newnode('math', + owner=lift, + attrs={ + 'input1': (0, 2, 0), + 'operation': 'add' + }) + _tcombine.connectattr('output', mnode, 'input2') + + _cleaner_combine = ba.newnode('combine', + owner=cleaner, + attrs={ + 'input1': 5.6, + 'input2': -5.5, + 'size': 3 + }) + _cleaner_combine.connectattr('output', cleaner, 'position') + ba.animate(_tcombine, 'input1', { + 0: 5.1, + }) + ba.animate(_cleaner_combine, 'input0', { + 0: -19 if x < 0 else 19, + }) + + _tcombine.connectattr('output', floor, 'position') + mnode.connectattr('output', lift, 'position') + self.lifts[floor] = {"state":"origin","lift":_tcombine,"cleaner":_cleaner_combine,'leftLift': x < 0} + + def _handle_lift(self): + region = ba.getcollision().sourcenode + lift = self.lifts[region] + def clean(lift): + ba.animate(lift["cleaner"], 'input0', { + 0: -19 if lift["leftLift"] else 19 , + 2: -16 if lift["leftLift"] else 16, + 4.3: -19 if lift["leftLift"] else 19 + }) + if lift["state"] == "origin": + lift["state"] = "transition" + ba.animate(lift["lift"], 'input1', { + 0: 5.1, + 1.3: 5.1, + 6: 5+12, + 9: 5+12, + 15: 5.1 + }) + ba.timer(16, ba.Call(lambda lift: lift.update({'state': 'end'}), lift)) + ba.timer(12, ba.Call(clean, lift)) + + + def _handle_lift_disconnect(self): + region = ba.getcollision().sourcenode + lift = self.lifts[region] + if lift["state"] == 'end': + lift["state"] = "origin" + + def create_slope(self, x, y, backslash): + shared = SharedObjects.get() + + for i in range(0, 21): + ba.newnode('region', attrs={'position': (x, y, -5.52), 'scale': (0.2, 0.1, 6), + 'type': 'box', 'materials': [shared.footing_material, self._real_wall_material]}) + ba.newnode('locator', attrs={'shape': 'box', 'position': (x, y, -5.52), 'color': ( + 1, 1, 0), 'opacity': 1, 'draw_beauty': True, 'additive': False, 'size': (0.2, 0.1, 2)}) + if backslash: + x = x+0.1 + y = y+0.1 + else: + x = x-0.1 + y = y+0.1 + +class mapdefs: + points = {} + # noinspection PyDictCreation + boxes = {} + boxes['area_of_interest_bounds'] = (-1.045859963, 12.67722855, + -5.401537075) + (0.0, 0.0, 0.0) + ( + 42.46156851, 20.94044653, 0.6931564611) + points['ffa_spawn1'] = (-9.295167711, 8.010664315, + -5.44451005) + (1.555840357, 1.453808816, 0.1165648888) + points['ffa_spawn2'] = (7.484707127, 8.172681752, -5.614479365) + ( + 1.553861796, 1.453808816, 0.04419853907) + points['ffa_spawn3'] = (9.55724115, 11.30789446, -5.614479365) + ( + 1.337925849, 1.453808816, 0.04419853907) + points['ffa_spawn4'] = (-11.55747023, 10.99170684, -5.614479365) + ( + 1.337925849, 1.453808816, 0.04419853907) + points['ffa_spawn5'] = (-1.878892369, 9.46490571, -5.614479365) + ( + 1.337925849, 1.453808816, 0.04419853907) + points['ffa_spawn6'] = (-0.4912812943, 5.077006397, -5.521672101) + ( + 1.878332089, 1.453808816, 0.007578097856) + points['flag1'] = (-11.75152479, 8.057427485, -5.52) + points['flag2'] = (9.840909039, 8.188634282, -5.52) + points['flag3'] = (-0.2195258696, 5.010273907, -5.52) + points['flag4'] = (-0.04605809154, 12.73369108, -5.52) + points['flag_default'] = (-0.04201942896, 12.72374492, -5.52) + boxes['map_bounds'] = (-0.8748348681, 9.212941713, -5.729538885) + ( + 0.0, 0.0, 0.0) + (42.09666006, 26.19950145, 7.89541168) + points['powerup_spawn1'] = (1.160232442, 6.745963662, -5.469115985) + points['powerup_spawn2'] = (-1.899700206, 10.56447241, -5.505721177) + points['powerup_spawn3'] = (10.56098871, 12.25165669, -5.576232453) + points['powerup_spawn4'] = (-12.33530337, 12.25165669, -5.576232453) + points['spawn1'] = (-9.295167711, 8.010664315, + -5.44451005) + (1.555840357, 1.453808816, 0.1165648888) + points['spawn2'] = (7.484707127, 8.172681752, + -5.614479365) + (1.553861796, 1.453808816, 0.04419853907) + points['spawn_by_flag1'] = (-9.295167711, 8.010664315, -5.44451005) + ( + 1.555840357, 1.453808816, 0.1165648888) + points['spawn_by_flag2'] = (7.484707127, 8.172681752, -5.614479365) + ( + 1.553861796, 1.453808816, 0.04419853907) + points['spawn_by_flag3'] = (-1.45994593, 5.038762459, -5.535288724) + ( + 0.9516389866, 0.6666414677, 0.08607244075) + points['spawn_by_flag4'] = (0.4932087091, 12.74493212, -5.598987003) + ( + 0.5245740665, 0.5245740665, 0.01941146064) +class CreativeThoughts(ba.Map): + """Freaking map by smoothy.""" + + defs = mapdefs + + name = 'Creative Thoughts' + + @classmethod + def get_play_types(cls) -> List[str]: + """Return valid play types for this map.""" + return [ + 'melee', 'keep_away', 'team_flag' + ] + + @classmethod + def get_preview_texture_name(cls) -> str: + return 'alwaysLandPreview' + + @classmethod + def on_preload(cls) -> Any: + data: Dict[str, Any] = { + 'model': ba.getmodel('alwaysLandLevel'), + 'bottom_model': ba.getmodel('alwaysLandLevelBottom'), + 'bgmodel': ba.getmodel('alwaysLandBG'), + 'collide_model': ba.getcollidemodel('alwaysLandLevelCollide'), + 'tex': ba.gettexture('alwaysLandLevelColor'), + 'bgtex': ba.gettexture('alwaysLandBGColor'), + 'vr_fill_mound_model': ba.getmodel('alwaysLandVRFillMound'), + 'vr_fill_mound_tex': ba.gettexture('vrFillMound') + } + return data + + @classmethod + def get_music_type(cls) -> ba.MusicType: + return ba.MusicType.FLYING + + def __init__(self) -> None: + super().__init__(vr_overlay_offset=(0, -3.7, 2.5)) + shared = SharedObjects.get() + self._fake_wall_material=ba.Material() + self._real_wall_material=ba.Material() + self._fake_wall_material.add_actions( + conditions=(('they_are_younger_than',9000),'and',('they_have_material', shared.player_material)), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self._real_wall_material.add_actions( + conditions=('they_have_material', shared.player_material), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', True) + + )) + self.background = ba.newnode( + 'terrain', + attrs={ + 'model': self.preloaddata['bgmodel'], + 'lighting': False, + 'background': True, + 'color_texture': ba.gettexture("rampageBGColor") + }) + + self.leftwall=ba.newnode('region',attrs={'position': (-17.75152479, 13, -5.52),'scale': (0.1,15.5,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + self.rightwall=ba.newnode('region',attrs={'position': (17.75, 13, -5.52),'scale': (0.1,15.5,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + self.topwall=ba.newnode('region',attrs={'position': (0, 21.0, -5.52),'scale': (35.4,0.2,2),'type': 'box','materials': [shared.footing_material,self._real_wall_material ]}) + ba.newnode('locator', attrs={'shape':'box', 'position':(-17.75152479, 13, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(0.1,15.5,2)}) + ba.newnode('locator', attrs={'shape':'box', 'position':(17.75, 13, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(0.1,15.5,2)}) + ba.newnode('locator', attrs={'shape':'box', 'position':(0, 21.0, -5.52), 'color':(0,0,0), 'opacity':1,'draw_beauty':True,'additive':False,'size':(35.4,0.2,2)}) + + gnode = ba.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 + + # throw out some tips on flying + txt = ba.newnode('text', + attrs={ + 'text': ba.Lstr(resource='pressJumpToFlyText'), + 'scale': 1.2, + 'maxwidth': 800, + 'position': (0, 200), + 'shadow': 0.5, + 'flatness': 0.5, + 'h_align': 'center', + 'v_attach': 'bottom' + }) + cmb = ba.newnode('combine', + owner=txt, + attrs={ + 'size': 4, + 'input0': 0.3, + 'input1': 0.9, + 'input2': 0.0 + }) + ba.animate(cmb, 'input3', {3.0: 0, 4.0: 1, 9.0: 1, 10.0: 0}) + cmb.connectattr('output', txt, 'color') + ba.timer(10.0, txt.delete) + + +try: + ba._map.register_map(CreativeThoughts) +except: + pass \ No newline at end of file