def _edit_game_done(self, config: Optional[dict[str, Any]]) -> None: from bastd.ui.playlist.edit import PlaylistEditWindow from bastd.ui.playlist.addgame import PlaylistAddGameWindow from ba.internal import get_type_name if config is None: # If we were editing, go back to our list. if self._editing_game: ba.playsound(ba.getsound('powerdown01')) ba.app.ui.clear_main_menu_window(transition='out_right') ba.app.ui.set_main_menu_window( PlaylistEditWindow(editcontroller=self, transition='in_left').get_root_widget()) # Otherwise we were adding; go back to the add type choice list. else: ba.app.ui.clear_main_menu_window(transition='out_right') ba.app.ui.set_main_menu_window( PlaylistAddGameWindow( editcontroller=self, transition='in_left').get_root_widget()) else: # Make sure type is in there. assert self._editing_game_type is not None config['type'] = get_type_name(self._editing_game_type) if self._editing_game: self._playlist[self._selected_index] = copy.deepcopy(config) else: # Add a new entry to the playlist. insert_index = min(len(self._playlist), self._selected_index + 1) self._playlist.insert(insert_index, copy.deepcopy(config)) self._selected_index = insert_index ba.playsound(ba.getsound('gunCocking')) ba.app.ui.clear_main_menu_window(transition='out_right') ba.app.ui.set_main_menu_window( PlaylistEditWindow(editcontroller=self, transition='in_left').get_root_widget())
def _set_sub_tab(self, value: SubTabType, region_width: float, region_height: float, playsound: bool = False) -> None: assert self._container if playsound: ba.playsound(ba.getsound('click01')) # Reset our selection. # (prevents selecting something way down the list if we switched away # and came back) self._selection = None self._have_user_selected_row = False # Reset refresh to the top and make sure everything refreshes. self._refresh_ui_row = 0 self._sub_tab = value active_color = (0.6, 1.0, 0.6) inactive_color = (0.5, 0.4, 0.5) ba.textwidget( edit=self._join_new_party_text, color=active_color if value is SubTabType.NEW else inactive_color) ba.textwidget( edit=self._join_saved_party_text, color=active_color if value is SubTabType.SAVED else inactive_color) # Clear anything existing in the old sub-tab. for widget in self._container.get_children(): if widget and widget not in {self._join_saved_party_text, self._join_new_party_text}: widget.delete() if value is SubTabType.NEW: self._build_new_party_tab(region_width, region_height) if value is SubTabType.SAVED: self._build_saved_party_tab(region_width, region_height)
def _host_button_press(self) -> None: if self._waiting_for_hosting_state: return if _ba.get_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInErrorText')) ba.playsound(ba.getsound('error')) self._refresh_sub_tab() return if self._hostingstate.unavailable_error is not None: ba.playsound(ba.getsound('error')) return # If we're not hosting, start. if self._hostingstate.party_code is None: # If there's a ticket cost, make sure we have enough tickets. if self._hostingstate.tickets_to_host_now > 0: ticket_count: Optional[int] try: ticket_count = _ba.get_account_ticket_count() except Exception: # FIXME: should add a ba.NotSignedInError we can use here. ticket_count = None ticket_cost = self._hostingstate.tickets_to_host_now if ticket_count is not None and ticket_count < ticket_cost: getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return self._last_action_send_time = time.time() _ba.add_transaction( { 'type': 'PRIVATE_PARTY_START', 'config': asdict(self._hostingconfig) }, callback=ba.WeakCall(self._hosting_state_response)) _ba.run_transactions() else: self._last_action_send_time = time.time() _ba.add_transaction({'type': 'PRIVATE_PARTY_STOP'}, callback=ba.WeakCall( self._hosting_state_response)) _ba.run_transactions() ba.playsound(ba.getsound('click01')) self._waiting_for_hosting_state = True self._refresh_sub_tab()
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, ba.PlayerDiedMessage): # Augment standard behavior. super().handlemessage(msg) player: Player = msg.getplayer(Player) player.lives -= 1 if player.lives < 0: ba.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: ba.playsound(get_factory().single_player_death_sound) # 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(ba.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 handlemessage(self, msg: Any) -> Any: if __debug__: self._handlemessage_sanity_check() if isinstance(msg, ba.DieMessage): if self.node: self.node.delete() elif isinstance(msg, ExplodeHitMessage): node = ba.get_collision_info("opposing_node") if node: assert self.node nodepos = self.node.position # new mag = 2000.0 if self.blast_type == 'ice': mag *= 0.5 elif self.blast_type == 'land_mine': mag *= 2.5 elif self.blast_type == 'tnt': mag *= 2.0 node.handlemessage( ba.HitMessage(pos=nodepos, velocity=(0, 0, 0), magnitude=mag, hit_type=self.hit_type, hit_subtype=self.hit_subtype, radius=self.radius, source_player=self.source_player)) if self.blast_type == "ice": ba.playsound(get_factory().freeze_sound, 10, position=nodepos) node.handlemessage(ba.FreezeMessage()) else: super().handlemessage(msg)
def _on_import_response(self, response: Optional[Dict[str, Any]]) -> None: if response is None: ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return if response['playlistType'] == 'Team Tournament': playlist_type_name = ba.Lstr(resource='playModes.teamsText') elif response['playlistType'] == 'Free-for-All': playlist_type_name = ba.Lstr(resource='playModes.freeForAllText') else: playlist_type_name = ba.Lstr(value=response['playlistType']) ba.screenmessage(ba.Lstr(resource='importPlaylistSuccessText', subs=[('${TYPE}', playlist_type_name), ('${NAME}', response['playlistName'])]), color=(0, 1, 0)) ba.playsound(ba.getsound('gunCocking')) if self._on_success_callback is not None: self._on_success_callback() ba.containerwidget(edit=self._root_widget, transition=self._transition_out)
def _connect(self, textwidget: ba.Widget, port_textwidget: ba.Widget) -> None: addr = cast(str, ba.textwidget(query=textwidget)) if addr == '': ba.screenmessage( ba.Lstr(resource='internal.invalidAddressErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return try: port = int(cast(str, ba.textwidget(query=port_textwidget))) except ValueError: port = -1 if port > 65535 or port < 0: ba.screenmessage(ba.Lstr(resource='internal.invalidPortErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return _HostLookupThread(name=addr, port=port, call=ba.WeakCall(self._host_lookup_result)).start()
def _on_more_press(self) -> None: our_login_id = _ba.get_public_login_id() # our_login_id = _bs.get_account_misc_read_val_2( # 'resolvedAccountID', None) if not self._can_do_more_button or our_login_id is None: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource='unavailableText'), color=(1, 0, 0)) return if self._season is None: season_str = '' else: season_str = ( '&season=' + ('all_time' if self._season == 'a' else self._season)) if self._league_url_arg != '': league_str = '&league=' + self._league_url_arg else: league_str = '' ba.open_url(_ba.get_master_server_address() + '/highscores?list=powerRankings&v=2' + league_str + season_str + '&player=' + our_login_id)
def drop_shield(self) -> None: """Drop a shield powerup in random place""" # FIXME: should use map defs shield = PowerupBox(poweruptype='shield', position=(random.uniform(-10, 10), 6, random.uniform(-5, 5))).autoretain() ba.playsound(self._ding_sound) p_light = ba.newnode('light', owner=shield.node, attrs={ 'position': (0, 0, 0), 'color': (0.3, 0.0, 0.4), 'radius': 0.3, 'intensity': 2, 'volume_intensity_scale': 10.0 }) shield.node.connectattr('position', p_light, 'position') ba.animate(p_light, 'intensity', {0: 2, 8: 0})
def _smooth_update(self) -> None: if not self._ticket_count_text: self._smooth_update_timer = None return finished = False # if we're going down, do it immediately assert self._smooth_ticket_count is not None if int(self._smooth_ticket_count) >= self._ticket_count: self._smooth_ticket_count = float(self._ticket_count) finished = True else: # we're going up; start a sound if need be self._smooth_ticket_count = min( self._smooth_ticket_count + 1.0 * self._smooth_increase_speed, self._ticket_count) if int(self._smooth_ticket_count) >= self._ticket_count: finished = True self._smooth_ticket_count = float(self._ticket_count) elif self._ticking_node is None: with ba.Context('ui'): self._ticking_node = ba.newnode( 'sound', attrs={ 'sound': ba.getsound('scoreIncrease'), 'positional': False }) ba.textwidget(edit=self._ticket_count_text, text=str(int(self._smooth_ticket_count))) # if we've reached the target, kill the timer/sound/etc if finished: self._smooth_update_timer = None if self._ticking_node is not None: self._ticking_node.delete() self._ticking_node = None ba.playsound(ba.getsound('cashRegister2'))
def _purchase(self) -> None: from ba.internal import get_store_item_name_translated from bastd.ui import getcurrency from bastd.ui import confirm if self._offer['item'] == 'pro': _ba.purchase('pro_sale') elif self._offer['item'] == 'pro_fullprice': _ba.purchase('pro') elif self._is_bundle_sale: # With bundle sales, the price is the name of the IAP. _ba.purchase(self._offer['price']) else: ticket_count: Optional[int] try: ticket_count = _ba.get_account_ticket_count() except Exception: ticket_count = None if (ticket_count is not None and ticket_count < self._offer['price']): getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return def do_it() -> None: _ba.in_game_purchase('offer:' + str(self._offer['id']), self._offer['price']) ba.playsound(ba.getsound('swish')) confirm.ConfirmWindow(ba.Lstr( resource='store.purchaseConfirmText', subs=[('${ITEM}', get_store_item_name_translated(self._offer['item']))]), width=400, height=120, action=do_it, ok_text=ba.Lstr( resource='store.purchaseText', fallback_resource='okText'))
def handlemessage(self, msg: Any) -> Any: assert not self.expired if isinstance(msg, ba.PowerupAcceptMessage): factory = PowerupBoxFactory.get() assert self.node if self.poweruptype == 'health': ba.playsound(factory.health_powerup_sound, 3, position=self.node.position) ba.playsound(factory.powerup_sound, 3, position=self.node.position) self._powersgiven = True self.handlemessage(ba.DieMessage()) elif isinstance(msg, _TouchedMessage): if not self._powersgiven: node = ba.getcollision().opposingnode node.handlemessage( ba.PowerupMessage(self.poweruptype, sourcenode=self.node)) elif isinstance(msg, ba.DieMessage): if self.node: if msg.immediate: self.node.delete() else: ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) ba.timer(0.1, self.node.delete) elif isinstance(msg, ba.OutOfBoundsMessage): self.handlemessage(ba.DieMessage()) elif isinstance(msg, ba.HitMessage): # Don't die on punches (that's annoying). if msg.hit_type != 'punch': self.handlemessage(ba.DieMessage()) else: return super().handlemessage(msg) return None
def _tick(self) -> None: self._update_flag_state() # Give holding players points. for player in self.players: if player.gamedata['at_flag'] > 0: self.stats.player_scored(player, 3, screenmessage=False, display=False) if self._scoring_team is None: scoring_team = None else: scoring_team = self._scoring_team() if scoring_team: if scoring_team.gamedata['time_remaining'] > 0: ba.playsound(self._tick_sound) scoring_team.gamedata['time_remaining'] = max( 0, scoring_team.gamedata['time_remaining'] - 1) self._update_scoreboard() if scoring_team.gamedata['time_remaining'] > 0: assert self._flag is not None self._flag.set_score_text( str(scoring_team.gamedata['time_remaining'])) # Announce numbers we have sounds for. try: ba.playsound(self._countdownsounds[ scoring_team.gamedata['time_remaining']]) except Exception: pass # winner if scoring_team.gamedata['time_remaining'] <= 0: self.end_game()
def _save_server(self, textwidget: ba.Widget, port_textwidget: ba.Widget) -> None: addr = cast(str, ba.textwidget(query=textwidget)) if addr == '': ba.screenmessage( ba.Lstr(resource='internal.invalidAddressErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return try: port = int(cast(str, ba.textwidget(query=port_textwidget))) except ValueError: port = -1 if port > 65535 or port < 0: ba.screenmessage(ba.Lstr(resource='internal.invalidPortErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return try: import socket addr = socket.gethostbyname(addr) except Exception: addr = None config=ba.app.config if addr is not None: if 'Saved Servers' in config: config['Saved Servers'][addr+str(port)]={"addr":addr,"port":port,"name":''} else: config['Saved Servers']={} config['Saved Servers'][addr+str(port)]={"addr":addr,"port":port,"name":''} config.commit() ba.screenmessage("Saved Successfully", color=(0, 1, 0)) else: ba.screenmessage("Invalid Address", color=(1, 0, 0))
def _new_playlist(self) -> None: # pylint: disable=cyclic-import from bastd.ui.playlist.editcontroller import PlaylistEditController from bastd.ui.purchase import PurchaseWindow if not ba.app.accounts.have_pro_options(): PurchaseWindow(items=['pro']) return # Clamp at our max playlist number. if len(ba.app.config[self._config_name_full]) > self._max_playlists: ba.screenmessage( ba.Lstr(translate=('serverResponses', 'Max number of playlists reached.')), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return # In case they cancel so we can return to this state. self._save_playlist_selection() # Kick off the edit UI. PlaylistEditController(sessiontype=self._sessiontype) ba.containerwidget(edit=self._root_widget, transition='out_left')
def arm(self, actor: stdbomb.Bomb): factory = stdbomb.BombFactory.get() elon_mine_lit_tex = ba.gettexture('circleNoAlpha') elon_mine_tex = ba.gettexture('achievementCrossHair') actor.texture_sequence = ba.newnode('texture_sequence', owner=actor.node, attrs={ 'rate': 30, 'input_textures': (elon_mine_lit_tex, elon_mine_tex) }) ba.timer(0.5, actor.texture_sequence.delete) ba.playsound(ba.getsound('activateBeep'), position=actor.node.position) actor.aim = AutoAim(actor.node, actor.owner) # we now make it explodable. ba.timer( 0.25, ba.WeakCall(actor._add_material, factory.land_mine_blast_material)) actor.texture_sequence.connectattr('output_texture', actor.node, 'color_texture')
def _edit_soundtrack(self) -> None: # pylint: disable=cyclic-import from ba.internal import have_pro_options from bastd.ui import purchase from bastd.ui.soundtrack import edit as stedit if not have_pro_options(): purchase.PurchaseWindow(items=['pro']) return if self._selected_soundtrack is None: return if self._selected_soundtrack == '__default__': ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource=self._r + '.cantEditDefaultText'), color=(1, 0, 0)) return self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') ba.app.ui.set_main_menu_window( stedit.SoundtrackEditWindow( existing_soundtrack=self._selected_soundtrack).get_root_widget( ))
def _on_egg_player_collide(self) -> None: if self.has_ended(): return collision = ba.getcollision() try: egg = collision.sourcenode.getdelegate(Egg, True) player = collision.opposingnode.getdelegate(PlayerSpaz, True).getplayer( Player, True) except Exception: return player.team.score += 1 # Displays a +1 (and adds to individual player score in # teams mode). self.stats.player_scored(player, 1, screenmessage=False) if self._max_eggs < 5: self._max_eggs += 1.0 elif self._max_eggs < 10: self._max_eggs += 0.5 elif self._max_eggs < 30: self._max_eggs += 0.3 self._update_scoreboard() ba.playsound(self._collect_sound, 0.5, position=egg.node.position) # Create a flash. light = ba.newnode('light', attrs={ 'position': egg.node.position, 'height_attenuated': False, 'radius': 0.1, 'color': (1, 1, 0) }) ba.animate(light, 'intensity', {0: 0, 0.1: 1.0, 0.2: 0}, loop=False) ba.timer(0.200, light.delete) egg.handlemessage(ba.DieMessage())
def _restore_editor(cls, state: Dict[str, Any], musictype: str, entry: Any) -> None: from ba.internal import get_soundtrack_entry_type # Apply the change and recreate the window. soundtrack = state['soundtrack'] existing_entry = (None if musictype not in soundtrack else soundtrack[musictype]) if existing_entry != entry: ba.playsound(ba.getsound('gunCocking')) # Make sure this doesn't get mucked with after we get it. if entry is not None: entry = copy.deepcopy(entry) entry_type = get_soundtrack_entry_type(entry) if entry_type == 'default': # For 'default' entries simply exclude them from the list. if musictype in soundtrack: del soundtrack[musictype] else: soundtrack[musictype] = entry ba.app.main_menu_window = (cls(state, transition='in_left').get_root_widget())
def _rename_my_replay(self, replay: str) -> None: new_name = None try: if not self._my_replay_rename_text: return new_name_raw = cast( str, ba.textwidget(query=self._my_replay_rename_text)) new_name = new_name_raw + '.brp' # Ignore attempts to change it to what it already is # (or what it looks like to the user). if (replay != new_name and self._get_replay_display_name(replay) != new_name_raw): old_name_full = (_ba.get_replays_dir() + '/' + replay).encode('utf-8') new_name_full = (_ba.get_replays_dir() + '/' + new_name).encode('utf-8') # False alarm; ba.textwidget can return non-None val. # pylint: disable=unsupported-membership-test if os.path.exists(new_name_full): ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource=self._r + '.replayRenameErrorAlreadyExistsText'), color=(1, 0, 0)) elif any(char in new_name_raw for char in ['/', '\\', ':']): ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource=self._r + '.replayRenameErrorInvalidName'), color=(1, 0, 0)) else: _ba.increment_analytics_count('Replay rename') os.rename(old_name_full, new_name_full) self._refresh_my_replays() ba.playsound(ba.getsound('gunCocking')) except Exception: ba.print_exception( f"Error renaming replay '{replay}' to '{new_name}'.") ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource=self._r + '.replayRenameErrorText'), color=(1, 0, 0), ) ba.containerwidget(edit=self._my_replays_rename_window, transition='out_scale')
def handlemessage(self, msg: Any) -> Any: assert not self.expired if isinstance(msg, ba.DieMessage): if self.node: self.node.delete() elif isinstance(msg, ExplodeHitMessage): node = ba.getcollision().opposingnode assert self.node nodepos = self.node.position mag = 2000.0 if self.blast_type == 'ice': mag *= 0.5 elif self.blast_type == 'land_mine': mag *= 2.5 elif self.blast_type == 'tnt': mag *= 2.0 node.handlemessage( ba.HitMessage(pos=nodepos, velocity=(0, 0, 0), magnitude=mag, hit_type=self.hit_type, hit_subtype=self.hit_subtype, radius=self.radius, source_player=ba.existing(self._source_player))) if self.blast_type == 'ice': ba.playsound(BombFactory.get().freeze_sound, 10, position=nodepos) node.handlemessage(ba.FreezeMessage()) else: return super().handlemessage(msg) return None
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 = ba.TeamGameResults() # 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((ba.time() - self._timer.starttime) * 1000.0) ba.cameraflash() ba.playsound(self._winsound) for team in self.teams: for player in team.players: if player.actor: player.actor.handlemessage(ba.CelebrateMessage()) results.set_team_score(team, elapsed_time_ms) # Ends the activity. self.end(results)
def _do_it(self) -> None: from bastd.ui.soundtrack import browser as stb music = ba.app.music cfg = ba.app.config new_name = cast(str, ba.textwidget(query=self._text_field)) if (new_name != self._soundtrack_name and new_name in cfg['Soundtracks']): ba.screenmessage( ba.Lstr(resource=self._r + '.cantSaveAlreadyExistsText')) ba.playsound(ba.getsound('error')) return if not new_name: ba.playsound(ba.getsound('error')) return if new_name == ba.Lstr(resource=self._r + '.defaultSoundtrackNameText').evaluate(): ba.screenmessage( ba.Lstr(resource=self._r + '.cantOverwriteDefaultText')) ba.playsound(ba.getsound('error')) return # Make sure config exists. if 'Soundtracks' not in cfg: cfg['Soundtracks'] = {} # If we had an old one, delete it. if (self._existing_soundtrack_name is not None and self._existing_soundtrack_name in cfg['Soundtracks']): del cfg['Soundtracks'][self._existing_soundtrack_name] cfg['Soundtracks'][new_name] = self._soundtrack cfg['Soundtrack'] = new_name cfg.commit() ba.playsound(ba.getsound('gunCocking')) ba.containerwidget(edit=self._root_widget, transition='out_right') # Resets music back to normal. music.set_music_play_mode(ba.MusicPlayMode.REGULAR, force_restart=True) ba.app.ui.set_main_menu_window( stb.SoundtrackBrowserWindow( transition='in_left').get_root_widget())
def _on_entry_activated(self, entry: str) -> None: # pylint: disable=too-many-branches new_path = None try: assert self._path is not None if entry == '..': chunks = self._path.split('/') if len(chunks) > 1: new_path = '/'.join(chunks[:-1]) if new_path == '': new_path = '/' else: ba.playsound(ba.getsound('error')) else: if self._path == '/': test_path = self._path + entry else: test_path = self._path + '/' + entry if os.path.isdir(test_path): ba.playsound(ba.getsound('swish')) new_path = test_path elif os.path.isfile(test_path): if self._is_valid_file_path(test_path): ba.playsound(ba.getsound('swish')) ba.containerwidget(edit=self._root_widget, transition='out_right') if self._callback is not None: self._callback(test_path) else: ba.playsound(ba.getsound('error')) else: print(('Error: FileSelectorWindow found non-file/dir:', test_path)) except Exception: ba.print_exception( 'Error in FileSelectorWindow._on_entry_activated().') if new_path is not None: self._set_path(new_path)
def _purchase_check_result(self, item: str, result: Optional[Dict[str, Any]]) -> None: if result is None: ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource='internal.unavailableNoConnectionText'), color=(1, 0, 0)) else: if result['allow']: self._do_purchase(item) else: if result['reason'] == 'versionTooOld': ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource='getTicketsWindow.versionTooOldText'), color=(1, 0, 0)) else: ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource='getTicketsWindow.unavailableText'), color=(1, 0, 0))
def _on_upgrade_press(self) -> None: from bastd.ui import getcurrency if self._status is None: # If it appears we don't have enough tickets, offer to buy more. tickets = _ba.get_account_ticket_count() if tickets < self._cost: ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() return ba.screenmessage(ba.Lstr(resource='purchasingText'), color=(0, 1, 0)) self._status = 'pre_upgrading' # Now we tell the original editor to save the profile, add an # upgrade transaction, and then sit and wait for everything to # go through. edit_profile_window = self._edit_profile_window() if edit_profile_window is None: print('profile upgrade: original edit window gone') return success = edit_profile_window.save(transition_out=False) if not success: print('profile upgrade: error occurred saving profile') ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return _ba.add_transaction({ 'type': 'UPGRADE_PROFILE', 'name': self._name }) _ba.run_transactions() self._status = 'upgrading' self._upgrade_start_time = time.time() else: ba.playsound(ba.getsound('error'))
def _print_already_own(self, charname: str) -> None: ba.screenmessage(ba.Lstr(resource=self._r + '.alreadyOwnText', subs=[('${NAME}', charname)]), color=(1, 0, 0)) ba.playsound(ba.getsound('error'))
def _purchase_check_result(self, item: str, is_ticket_purchase: bool, result: Optional[Dict[str, Any]]) -> None: if result is None: ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource='internal.unavailableNoConnectionText'), color=(1, 0, 0)) else: if is_ticket_purchase: if result['allow']: price = _ba.get_account_misc_read_val( 'price.' + item, None) if (price is None or not isinstance(price, int) or price <= 0): print('Error; got invalid local price of', price, 'for item', item) ba.playsound(ba.getsound('error')) else: ba.playsound(ba.getsound('click01')) _ba.in_game_purchase(item, price) else: if result['reason'] == 'versionTooOld': ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.versionTooOldText'), color=(1, 0, 0)) else: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.unavailableText'), color=(1, 0, 0)) # Real in-app purchase. else: if result['allow']: _ba.purchase(item) else: if result['reason'] == 'versionTooOld': ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.versionTooOldText'), color=(1, 0, 0)) else: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.unavailableText'), color=(1, 0, 0))
def on_popup_cancel(self) -> None: ba.playsound(ba.getsound('swish')) self._on_cancel()
def lucky_block_callback(self: stdspaz.Spaz, msg: ba.PowerupMessage): event_number = random.randint(1, 15) if event_number in (1, 2, 3): powerup_type = stdpowerup.PowerupBoxFactory().get_random_powerup_type() self.node.handlemessage(ba.PowerupMessage(poweruptype=powerup_type)) elif event_number == 4: ba.camerashake(1) ba.emitfx(position=(self.node.position[0], self.node.position[1] + 4, self.node.position[2]), velocity=(0, 0, 0), count=700, spread=0.7, chunk_type='spark') powerup_type = stdpowerup.PowerupBoxFactory.get( ).get_random_powerup_type() stdpowerup.PowerupBox(position=(self.node.position[0], self.node.position[1] + 4, self.node.position[2]), poweruptype=powerup_type, expire=True).autoretain() powerup_type = stdpowerup.PowerupBoxFactory.get( ).get_random_powerup_type() stdpowerup.PowerupBox(position=(self.node.position[0], self.node.position[1] + 4, self.node.position[2]), poweruptype=powerup_type, expire=True).autoretain() powerup_type = stdpowerup.PowerupBoxFactory.get( ).get_random_powerup_type() stdpowerup.PowerupBox(position=(self.node.position[0], self.node.position[1] + 4, self.node.position[2]), poweruptype=powerup_type, expire=True).autoretain() elif event_number == 5: stdbomb.Bomb(position=(self.node.position[0], self.node.position[1] + 3, self.node.position[2]), source_player=self.source_player, owner=self.node, blast_radius=6).autoretain() elif event_number == 6: self.node.handlemessage(ba.FreezeMessage()) elif event_number == 7: chunk_type = ('ice', 'rock', 'metal', 'spark', 'splinter', 'slime') ba.emitfx(position=self.node.position, velocity=(random.random() * 2, random.random() * 2, random.random() * 2), count=600, spread=random.random(), chunk_type=random.choice(chunk_type)) ba.camerashake(1) ba.playsound(ba.getsound('corkPop')) # position=self.node.position? elif event_number == 8: position = self.node.position def rain_wrapper(): p_type = stdpowerup.PowerupBoxFactory.get( ).get_random_powerup_type() new_position = (-10 + position[0] + random.random() * 20, position[1] + 6, -10 + position[2] + random.random() * 20) stdpowerup.PowerupBox(poweruptype=p_type, position=new_position).autoretain() if random.random() > 0.04: ba.timer(0.1, rain_wrapper) rain_wrapper() elif event_number == 9: stdbomb.Blast(position=self.node.position, velocity=self.node.velocity, blast_radius=1.0, blast_type='normal', source_player=None, hit_type='punch', hit_subtype='normal') elif event_number == 10: def blast(x: int, y: int, z: int) -> None: # add sound ba.NodeActor(node=ba.newnode('scorch', attrs={ 'position': (x, z, y), 'size': 0.2, 'big': False, })).autoretain() def smoke(x: int, y: int, z: int) -> None: ba.emitfx(position=(x, z, y), velocity=(0, 2, 0), count=1, emit_type='tendrils', tendril_type='smoke') ba.emitfx(position=(x, z, y), velocity=(0, 2, 0), count=int(1.0 + random.random() * 2), scale=0.8, spread=1.5, chunk_type='spark') star_positions = [ (2, 0), (0, 2), (-1.2, -1.6), (1.82, 0.83), (-1.83, 0.82), (1.23, -1.57), (-1.25, 1.56), (-0.65, 1.89), (0.82, 1.82), (1.27, 1.55), (1.82, -0.84), (0.31, -1.98), (-0.42, -1.96), (-1.75, -0.96), (-2, -0.14), (-0.69, -0.07), (-0.39, 0.82), (0.41, 0.82), (0.71, -0.06), (0.01, -0.62), (-0.99, 0.82), (-1.26, 0.37), (-0.89, -0.65), (-0.52, -1.05), (0.59, -1.07), (0.96, -0.8), (1.22, 0.35), (1.07, 0.82), (0.21, 1.39), (-0.17, 1.48), # --- (-1.94, 0.47), (-1.51, 1.31), (-0.95, 1.76), (-0.38, 1.96), (0.45, 1.95), (1.05, 1.7), (1.57, 1.24), (1.94, 0.49), (1.96, -0.42), (1.62, -1.17), (0.84, -1.82), (-0.78, -1.84), (-1.5, -1.33), (-1.91, -0.59), (-1.99, 0.17), (-1, 0.17), (-0.7, 0.82), (-0.27, 1.19), (0.29, 1.15), (0.77, 0.82), (1, 0.17), (0.84, -0.42), (0.31, -0.85), (-0.8, -1.27), (-1, -1), (-0.56, 0.33), (-0.47, 0.61), (0.52, 0.51), (-0.1, 0.82), (0.13, 0.82), (0.6, 0.27), (0.46, -0.27), (0.29, -0.4), (-0.44, -0.27), (-0.24, -0.42), (-1.36, 0.82), (-1.53, 0.59), (1.35, 0.83), (1.55, 0.61), (0.85, -1.28), (1.08, -1.13), (0.78, -0.34), (-0.21, -0.8), (0.11, 1.68) ] class Sparkling(ba.Actor): def __init__(self, position: Sequence[float], target: ba.Node): super().__init__() # nah; nodes not needed self.position = position self.position = (self.position[0], self.position[1] + 0.5, self.position[2]) self.target = target ba.timer(0.001, ba.WeakCall(self._update)) def _sparkle(self): ba.emitfx(position=self.position, velocity=(0, 1, 0), count=int(random.random() * 5 + 5), scale=0.8, spread=0.3, chunk_type='spark') def _blast(self): stdbomb.Blast(position=self.position, velocity=self.target.velocity, blast_radius=2, blast_type='normal', source_player=None, hit_type='punch', hit_subtype='normal').autoretain() def _update(self): if not self.target: del self # commit suicide because we have no goal in our existing :( return d = ba.Vec3(self.target.position) - ba.Vec3(self.position) if d.length() < 0.1: self._blast() del self return d = d.normalized() * 0.04 from math import sin, cos self.position = (self.position[0] + d.x + sin(ba.time() * 2) * 0.03, self.position[1] + d.y, self.position[2] + d.z + cos(ba.time() * 2) * 0.03) self._sparkle() ba.timer(0.001, ba.WeakCall(self._update)) def sparkling(x, y, z): Sparkling(position=(x, z, y), target=self.node).autoretain() def summon_tnt(x, y, z): stdbomb.Bomb(bomb_type='tnt', blast_radius=3, position=(x, z + 4, y), velocity=(0, -10, 0)).autoretain() scale = 1 delta = 0.02 op = self.node.position for i, (x, y) in enumerate(star_positions): ba.timer( i * delta, ba.Call(blast, self.node.position[0] + x * scale, self.node.position[2] + y * scale, self.node.position[1])) for i in range(4): ba.timer((len(star_positions)) * delta + i * 0.2, ba.Call(summon_tnt, op[0], op[2], op[1])) ba.timer((len(star_positions)) * delta + 1.0, ba.Call(sparkling, self.node.position[0], self.node.position[2], self.node.position[1])) def last_blast(): stdbomb.Blast(position=self.node.position, velocity=(self.node.velocity[0], self.node.velocity[1] + 10, self.node.velocity[2]), blast_radius=2, blast_type='normal', source_player=None, hit_type='punch', hit_subtype='normal').autoretain() # ba.timer( # 2 * len(star_positions) * delta + 0.2, # last_blast) elif event_number == 11: offset = -15 case = 1 if random.random() < 0.5 else -1 while offset < 15: velocity = (case * (12 + 8 * random.random()), -0.1, 0) stdbomb.Bomb(bomb_type='tnt', position=(self.node.position[0] - case * 10, self.node.position[1] + 3, self.node.position[2] + offset), velocity=velocity).autoretain() offset += 1.5 elif event_number == 12: color = { 0.0: (0, 0, 3), 0.5: (0, 3, 0), 1.0: (3, 0, 0), 1.5: (0, 0, 3) } # FIXME # ba.animate_array(self.node, 'color', 3, color, True) # self.node.handlemessage('celebrate', 100000000) elif event_number == 13: CompanionCube(position=(self.node.position[0], self.node.position[1] + 1, self.node.position[2]), velocity=(0, 10, 0)).autoretain() elif event_number == 14: ba.emitfx(position=self.node.position, count=100, emit_type='tendrils', tendril_type='smoke') elif event_number == 15: def drop_man(): botset: stdbot.SpazBotSet activity = ba.getactivity() if not hasattr(activity, 'botset'): activity.botset = botset = stdbot.SpazBotSet() botset = activity.botset aoi_bounds = self.activity.globalsnode.area_of_interest_bounds botset.spawn_bot( stdbot.BrawlerBotLite, (random.randrange(int(aoi_bounds[0]), int(aoi_bounds[3]) + 1), aoi_bounds[4] - 1, random.randrange(int(aoi_bounds[2]), int(aoi_bounds[5]) + 1)), spawn_time=0.001) def lightning_bolt(position, radius=1): ba.camerashake(4) vignette_outer = self.activity.globalsnode.vignette_outer # if ba.getactivity().tint is None: # ba.getactivity().std_tint = ba.sharedobj('globals').vignette_outer # vignette_outer = ba.sharedobj('globals').vignette_outer # else: # vignette_outer = ba.getactivity().tint light = ba.newnode('light', attrs={ 'position': position, 'color': (0.4, 0.4, 0.8), 'volume_intensity_scale': 1.0, 'radius': radius }) ba.animate(light, 'intensity', { 0: 1, 50: radius, 150: radius / 2, 250: 0, 260: radius, 410: radius / 2, 510: 0 }, timeformat=ba.TimeFormat.MILLISECONDS, suppress_format_warning=True) ba.animate_array(self.activity.globalsnode, 'vignette_outer', 3, { 0: vignette_outer, 0.2: (0.2, 0.2, 0.2), 0.51: vignette_outer }) # ba.playsound( # ba.getsound('grom'), # volume=10, # position=(0, 10, 0)) lightning_bolt(self.node.position) for time in range(15): ba.timer(time, drop_man)