From 46645a6aadc254ab8df26db6a2ba6017f4722ce8 Mon Sep 17 00:00:00 2001 From: Ayush Saini <36878972+imayushsaini@users.noreply.github.com> Date: Wed, 5 Oct 2022 21:36:29 +0530 Subject: [PATCH] added character chooser and builder --- plugins/utilities.json | 34 +- plugins/utilities/character_chooser.py | 360 +++++++++++++++++ plugins/utilities/character_maker.py | 538 +++++++++++++++++++++++++ 3 files changed, 929 insertions(+), 3 deletions(-) create mode 100644 plugins/utilities/character_chooser.py create mode 100644 plugins/utilities/character_maker.py diff --git a/plugins/utilities.json b/plugins/utilities.json index 6d65e74..b542013 100644 --- a/plugins/utilities.json +++ b/plugins/utilities.json @@ -133,7 +133,7 @@ "authors": [ { "name": "Mr.Smoothy", - "email": "", + "email": "smoothy@bombsquad.ga", "discord": "mr.smoothy#5824" } ], @@ -158,7 +158,7 @@ "authors": [ { "name": "Mr.Smoothy", - "email": "smoothyt@bombsquad.ga", + "email": "smoothy@bombsquad.ga", "discord": "mr.smoothy#5824" } ], @@ -177,7 +177,7 @@ "authors": [ { "name": "Mr.Smoothy", - "email": "smoothyt@bombsquad.ga", + "email": "smoothy@bombsquad.ga", "discord": "mr.smoothy#5824" } ], @@ -190,6 +190,34 @@ } } }, + "character_chooser": { + "description": "Let you choose your character before joining game.", + "external_url": "https://www.youtube.com/watch?v=hNmv2l-NahE", + "authors": [ + { + "name": "Mr.Smoothy", + "email": "smoothy@bombsquad.ga", + "discord": "mr.smoothy#5824" + } + ], + "versions": { + "1.0.0": null + } + }, + "character_builder": { + "description": "Make new characters by manipulating models and textures.", + "external_url": "https://www.youtube.com/watch?v=q0KxY1hfMPQ", + "authors": [ + { + "name": "Mr.Smoothy", + "email": "smoothy@bombsquad.ga", + "discord": "mr.smoothy#5824" + } + ], + "versions": { + "1.0.0": null + } + }, "icons_keyboard": { "description": "Enable 'Always Use Internal Keyboard' in Settings>Advanced. Double tap space-bar to change keyboards", "external_url": "", diff --git a/plugins/utilities/character_chooser.py b/plugins/utilities/character_chooser.py new file mode 100644 index 0000000..388479e --- /dev/null +++ b/plugins/utilities/character_chooser.py @@ -0,0 +1,360 @@ +# ba_meta require api 7 + +''' +Character Chooser by Mr.Smoothy + +This plugin will let you choose your character from lobby. + +Install this plugin on your Phone/PC or on Server + +If installed on server :- this will also let players choose server specific custom characters . so no more sharing of character file with all players, +just install this plugin on server ...and players can pick character from lobby . + +Use:- +> select your profile (focus on color and name) +> press ready (punch) +> now use UP/DOWN buttons to scroll character list +> Press ready again (punch) to join the game +> or press Bomb button to go back to profile choosing menu +> END + +Watch : https://www.youtube.com/watch?v=hNmv2l-NahE +Join : https://discord.gg/ucyaesh +Contact : discord mr.smoothy#5824 + + +Share this plugin with your server owner /admins to use it online + + :) + +''' + + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import ba,_ba +from bastd.actor.playerspaz import PlayerSpaz + + +from ba._error import print_exception, print_error, NotFoundError +from ba._gameutils import animate, animate_array +from ba._language import Lstr +from ba._generated.enums import SpecialChar, InputType +from ba._profile import get_player_profile_colors +if TYPE_CHECKING: + from typing import Any, Type, List, Dict, Tuple, Union, Sequence, Optional +import weakref +import os,json +from ba import _lobby +from bastd.actor.spazappearance import * +from ba._lobby import ChangeMessage +from ba._lobby import PlayerReadyMessage + +def __init__(self, vpos: float, sessionplayer: _ba.SessionPlayer, + lobby: 'Lobby') -> None: + self._deek_sound = _ba.getsound('deek') + self._click_sound = _ba.getsound('click01') + self._punchsound = _ba.getsound('punch01') + self._swish_sound = _ba.getsound('punchSwish') + self._errorsound = _ba.getsound('error') + self._mask_texture = _ba.gettexture('characterIconMask') + self._vpos = vpos + self._lobby = weakref.ref(lobby) + self._sessionplayer = sessionplayer + self._inited = False + self._dead = False + self._text_node: Optional[ba.Node] = None + self._profilename = '' + self._profilenames: List[str] = [] + self._ready: bool = False + self._character_names: List[str] = [] + self._last_change: Sequence[Union[float, int]] = (0, 0) + self._profiles: Dict[str, Dict[str, Any]] = {} + + app = _ba.app + + self.bakwas_chars=["Lee","Todd McBurton","Zola","Butch","Witch","warrior","Middle-Man","Alien","OldLady","Gladiator","Wrestler","Gretel","Robot"] + + # Load available player profiles either from the local config or + # from the remote device. + self.reload_profiles() + for name in _ba.app.spaz_appearances: + if name not in self._character_names and name not in self.bakwas_chars: + self._character_names.append(name) + # Note: this is just our local index out of available teams; *not* + # the team-id! + self._selected_team_index: int = self.lobby.next_add_team + + # Store a persistent random character index and colors; we'll use this + # for the '_random' profile. Let's use their input_device id to seed + # it. This will give a persistent character for them between games + # and will distribute characters nicely if everyone is random. + self._random_color, self._random_highlight = ( + get_player_profile_colors(None)) + + # To calc our random character we pick a random one out of our + # unlocked list and then locate that character's index in the full + # list. + char_index_offset = app.lobby_random_char_index_offset + self._random_character_index = ( + (sessionplayer.inputdevice.id + char_index_offset) % + len(self._character_names)) + + # Attempt to set an initial profile based on what was used previously + # for this input-device, etc. + self._profileindex = self._select_initial_profile() + self._profilename = self._profilenames[self._profileindex] + + self._text_node = _ba.newnode('text', + delegate=self, + attrs={ + 'position': (-100, self._vpos), + 'maxwidth': 190, + 'shadow': 0.5, + 'vr_depth': -20, + 'h_align': 'left', + 'v_align': 'center', + 'v_attach': 'top' + }) + animate(self._text_node, 'scale', {0: 0, 0.1: 1.0}) + self.icon = _ba.newnode('image', + owner=self._text_node, + attrs={ + 'position': (-130, self._vpos + 20), + 'mask_texture': self._mask_texture, + 'vr_depth': -10, + 'attach': 'topCenter' + }) + + animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)}) + + # Set our initial name to '' in case anyone asks. + self._sessionplayer.setname( + Lstr(resource='choosingPlayerText').evaluate(), real=False) + + # Init these to our rando but they should get switched to the + # selected profile (if any) right after. + self._character_index = self._random_character_index + self._color = self._random_color + self._highlight = self._random_highlight + self.characterchooser=False + self.update_from_profile() + self.update_position() + self._inited = True + + self._set_ready(False) + + + + +def _set_ready(self, ready: bool) -> None: + + # pylint: disable=cyclic-import + from bastd.ui.profile import browser as pbrowser + from ba._general import Call + profilename = self._profilenames[self._profileindex] + + # Handle '_edit' as a special case. + if profilename == '_edit' and ready: + with _ba.Context('ui'): + pbrowser.ProfileBrowserWindow(in_main_menu=False) + + # Give their input-device UI ownership too + # (prevent someone else from snatching it in crowded games) + _ba.set_ui_input_device(self._sessionplayer.inputdevice) + return + + if ready==False: + self._sessionplayer.assigninput( + InputType.LEFT_PRESS, + Call(self.handlemessage, ChangeMessage('team', -1))) + self._sessionplayer.assigninput( + InputType.RIGHT_PRESS, + Call(self.handlemessage, ChangeMessage('team', 1))) + self._sessionplayer.assigninput( + InputType.BOMB_PRESS, + Call(self.handlemessage, ChangeMessage('character', 1))) + self._sessionplayer.assigninput( + InputType.UP_PRESS, + Call(self.handlemessage, ChangeMessage('profileindex', -1))) + self._sessionplayer.assigninput( + InputType.DOWN_PRESS, + Call(self.handlemessage, ChangeMessage('profileindex', 1))) + self._sessionplayer.assigninput( + (InputType.JUMP_PRESS, InputType.PICK_UP_PRESS, + InputType.PUNCH_PRESS), + Call(self.handlemessage, ChangeMessage('ready', 1))) + self._ready = False + self._update_text() + self._sessionplayer.setname('untitled', real=False) + elif ready == True: + self.characterchooser=True + self._sessionplayer.assigninput( + (InputType.LEFT_PRESS, InputType.RIGHT_PRESS, + InputType.UP_PRESS, InputType.DOWN_PRESS, + InputType.JUMP_PRESS, InputType.BOMB_PRESS, + InputType.PICK_UP_PRESS), self._do_nothing) + self._sessionplayer.assigninput( + (InputType.UP_PRESS),Call(self.handlemessage,ChangeMessage('characterchooser',-1))) + self._sessionplayer.assigninput( + (InputType.DOWN_PRESS),Call(self.handlemessage,ChangeMessage('characterchooser',1))) + self._sessionplayer.assigninput( + (InputType.BOMB_PRESS),Call(self.handlemessage,ChangeMessage('ready',0))) + + self._sessionplayer.assigninput( + (InputType.JUMP_PRESS,InputType.PICK_UP_PRESS, InputType.PUNCH_PRESS), + Call(self.handlemessage, ChangeMessage('ready', 2))) + + # Store the last profile picked by this input for reuse. + input_device = self._sessionplayer.inputdevice + name = input_device.name + unique_id = input_device.unique_identifier + device_profiles = _ba.app.config.setdefault( + 'Default Player Profiles', {}) + + # Make an exception if we have no custom profiles and are set + # to random; in that case we'll want to start picking up custom + # profiles if/when one is made so keep our setting cleared. + special = ('_random', '_edit', '__account__') + have_custom_profiles = any(p not in special + for p in self._profiles) + + profilekey = name + ' ' + unique_id + if profilename == '_random' and not have_custom_profiles: + if profilekey in device_profiles: + del device_profiles[profilekey] + else: + device_profiles[profilekey] = profilename + _ba.app.config.commit() + + # Set this player's short and full name. + self._sessionplayer.setname(self._getname(), + self._getname(full=True), + real=True) + self._ready = True + self._update_text() + else: + + + + # Inform the session that this player is ready. + _ba.getsession().handlemessage(PlayerReadyMessage(self)) + + +def handlemessage(self, msg: Any) -> Any: + """Standard generic message handler.""" + + if isinstance(msg, ChangeMessage): + self._handle_repeat_message_attack() + + # If we've been removed from the lobby, ignore this stuff. + if self._dead: + print_error('chooser got ChangeMessage after dying') + return + + if not self._text_node: + print_error('got ChangeMessage after nodes died') + return + if msg.what=='characterchooser': + _ba.playsound(self._click_sound) + # update our index in our local list of characters + self._character_index = ((self._character_index + msg.value) % + len(self._character_names)) + self._update_text() + self._update_icon() + + if msg.what == 'team': + sessionteams = self.lobby.sessionteams + if len(sessionteams) > 1: + _ba.playsound(self._swish_sound) + self._selected_team_index = ( + (self._selected_team_index + msg.value) % + len(sessionteams)) + self._update_text() + self.update_position() + self._update_icon() + + elif msg.what == 'profileindex': + if len(self._profilenames) == 1: + + # This should be pretty hard to hit now with + # automatic local accounts. + _ba.playsound(_ba.getsound('error')) + else: + + # Pick the next player profile and assign our name + # and character based on that. + _ba.playsound(self._deek_sound) + self._profileindex = ((self._profileindex + msg.value) % + len(self._profilenames)) + self.update_from_profile() + + elif msg.what == 'character': + _ba.playsound(self._click_sound) + self.characterchooser=True + # update our index in our local list of characters + self._character_index = ((self._character_index + msg.value) % + len(self._character_names)) + self._update_text() + self._update_icon() + + elif msg.what == 'ready': + self._handle_ready_msg(msg.value) + +def _update_text(self) -> None: + assert self._text_node is not None + if self._ready: + + # Once we're ready, we've saved the name, so lets ask the system + # for it so we get appended numbers and stuff. + text = Lstr(value=self._sessionplayer.getname(full=True)) + if self.characterchooser: + text = Lstr(value='${A}\n${B}', + subs=[('${A}', text), + ('${B}', Lstr(value=""+self._character_names[self._character_index]))]) + self._text_node.scale=0.8 + else: + text = Lstr(value='${A} (${B})', + subs=[('${A}', text), + ('${B}', Lstr(resource='readyText'))]) + else: + text = Lstr(value=self._getname(full=True)) + self._text_node.scale=1.0 + + can_switch_teams = len(self.lobby.sessionteams) > 1 + + # Flash as we're coming in. + fin_color = _ba.safecolor(self.get_color()) + (1, ) + if not self._inited: + animate_array(self._text_node, 'color', 4, { + 0.15: fin_color, + 0.25: (2, 2, 2, 1), + 0.35: fin_color + }) + else: + + # Blend if we're in teams mode; switch instantly otherwise. + if can_switch_teams: + animate_array(self._text_node, 'color', 4, { + 0: self._text_node.color, + 0.1: fin_color + }) + else: + self._text_node.color = fin_color + + self._text_node.text = text + +# ba_meta export plugin +class HeySmoothy(ba.Plugin): + + def __init__(self): + _lobby.Chooser.__init__=__init__ + _lobby.Chooser._set_ready=_set_ready + + _lobby.Chooser._update_text=_update_text + _lobby.Chooser.handlemessage=handlemessage + + + diff --git a/plugins/utilities/character_maker.py b/plugins/utilities/character_maker.py new file mode 100644 index 0000000..f69d909 --- /dev/null +++ b/plugins/utilities/character_maker.py @@ -0,0 +1,538 @@ +# Released under the MIT License. See LICENSE for details. + + +''' +Character Builder/Maker by Mr.Smoothy +Plugin helps to mix character models and textures in interactive way. + +Watch tutorial : https://www.youtube.com/c/HeySmoothy +Join discord: https://discord.gg/ucyaesh for help +https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server/ + +> create team playlist and add character maker mini game +> Use export command to save character +> Character will be saved in CustomCharacter folder inside Bombsquad Mods folder + +*Only one player in that mini game supported ... + +Characters can be used offline or online +for online you need to share character file with server owners. + +*For server owners:_ + You might know what to do with that file, + Still , + refer code after line 455 in this file , add it as a plugin to import characters from json file. + +*For modders:- + You can add more models and texture , check line near 400 and add asset names , you can also modify sounds and icon in json file (optional) . + +To share your character with friends , + send them character .json file and tell them to put file in same location i.e Bombsquad/CustomCharacter or for PC appdata/Local/Bombsquad/Mods/CustomCharacter + this plugin should be installed on their device too + +Dont forget to share your creativity with me , +send your character screenshot discord: mr.smoothy#5824 https://discord.gg/ucyaesh + +Register your character in above discord server , so other server owners can add your characters. + + +Released on 28 May 2021 + + +Update 2 june : use import +''' + + +# ba_meta require api 7 + + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import ba,_ba +from bastd.actor.playerspaz import PlayerSpaz +from bastd.actor.scoreboard import Scoreboard + +if TYPE_CHECKING: + from typing import Any, Type, List, Dict, Tuple, Union, Sequence, Optional + +import os,json +from bastd.actor.spazappearance import * +spazoutfit={ + "color_mask":"neoSpazColorMask", + "color_texture":"neoSpazColor", + "head":"neoSpazHead", + "hand":"neoSpazHand", + "torso":"neoSpazTorso", + "pelvis":"neoSpazTorso", + "upper_arm":"neoSpazUpperArm", + "forearm":"neoSpazForeArm", + "upper_leg":"neoSpazUpperLeg", + "lower_leg":"neoSpazLowerLeg", + "toes_model":"neoSpazToes", + "jump_sounds":['spazJump01', 'spazJump02', 'spazJump03', 'spazJump04'], + "attack_sounds":['spazAttack01', 'spazAttack02', 'spazAttack03', 'spazAttack04'], + "impact_sounds":['spazImpact01', 'spazImpact02', 'spazImpact03', 'spazImpact04'], + "death_sounds":['spazDeath01'], + "pickup_sounds":['spazPickup01'], + "fall_sounds":['spazFall01'], + "icon_texture":"neoSpazIcon", + "icon_mask_texture":"neoSpazIconColorMask", + "style":"spaz" + } +character=None + +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 CharacterBuilder(ba.TeamGameActivity[Player, Team]): + """A game type based on acquiring kills.""" + + name = 'Character Maker' + description = 'Create your own custom Characters' + + # Print messages when players die since it matters here. + announce_player_deaths = True + + @classmethod + def get_available_settings( + cls, sessiontype: Type[ba.Session]) -> List[ba.Setting]: + settings = [ + ba.IntSetting( + 'Kills to Win Per Player', + min_value=1, + default=5, + 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.25), + ('Short', 0.5), + ('Normal', 1.0), + ('Long', 2.0), + ('Longer', 4.0), + ], + default=1.0, + ), + ba.BoolSetting('Epic Mode', default=False), + ] + + + if issubclass(sessiontype, ba.FreeForAllSession): + settings.append( + ba.BoolSetting('Allow Negative Scores', default=False)) + + return settings + + @classmethod + def supports_session_type(cls, sessiontype: Type[ba.Session]) -> bool: + return (issubclass(sessiontype, ba.DualTeamSession) + or issubclass(sessiontype, ba.FreeForAllSession)) + + @classmethod + def get_supported_maps(cls, sessiontype: Type[ba.Session]) -> List[str]: + return ['Rampage'] + + def __init__(self, settings: dict): + + + super().__init__(settings) + + self.initdic() + _ba.set_party_icon_always_visible(True) + self._score_to_win: Optional[int] = None + self._dingsound = ba.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.bodyindex=0 + self.modelindex=0 + self.youtube= ba.newnode( + 'text', + attrs={ + 'text': "youtube.com/c/HeySmoothy", + 'in_world': True, + 'scale': 0.02, + 'color': (1, 0, 0, 0.4), + 'h_align': 'center', + 'position': (0,4,-1.9) + }) + self.discordservere= ba.newnode( + 'text', + attrs={ + 'text': "discord.gg/ucyaesh", + 'in_world': True, + 'scale': 0.02, + 'color': (0.12, 0.3, 0.6, 0.4), + 'h_align': 'center', + 'position': (-3,2.7,-1.9) + }) + # self.discord= ba.newnode( + # 'text', + # attrs={ + # 'text': "mr.smoothy#5824", + # 'in_world': True, + # 'scale': 0.02, + # 'color': (01.2, 0.3, 0.7, 0.4), + # 'h_align': 'center', + # 'position': (4,2.7,-1.9) + # }) + # Base class overrides. + self.bodypart= ba.newnode( + 'text', + attrs={ + 'text': "", + 'in_world': True, + 'scale': 0.02, + 'color': (1, 1, 0, 1), + 'h_align': 'center', + 'position': (-4,6,-4) + }) + self.newmodel = ba.newnode( + 'text', + attrs={ + 'text': "", + 'in_world': True, + 'scale': 0.02, + 'color': (1, 1, 0, 1), + 'h_align': 'center', + 'position': (6,6,-4) + }) + self.slow_motion = self._epic_mode + self.default_music = (ba.MusicType.EPIC if self._epic_mode else + ba.MusicType.TO_THE_DEATH) + + def get_instance_description(self) -> Union[str, Sequence]: + return '' + + def get_instance_description_short(self) -> Union[str, Sequence]: + return '' + + def on_team_join(self, team: Team) -> None: + if self.has_begun(): + pass + + def on_begin(self) -> None: + super().on_begin() + + + def nextBodyPart(self): + self.bodyindex =(self.bodyindex+1)%len(self.dic.keys()) + self.bodypart.delete() + PART=list(self.dic.keys())[self.bodyindex] + self.bodypart=ba.newnode( + 'text', + attrs={ + 'text': PART, + 'in_world': True, + 'scale': 0.02, + 'color': (1, 1, 1, 1), + 'h_align': 'center', + 'position': (-4,6,-4) + }) + + + + def prevBodyPart(self): + self.bodyindex =(self.bodyindex-1)%len(self.dic.keys()) + self.bodypart.delete() + PART=list(self.dic.keys())[self.bodyindex] + self.bodypart=ba.newnode( + 'text', + attrs={ + 'text': PART, + 'in_world': True, + 'scale': 0.02, + 'color': (1, 1, 1, 1), + 'h_align': 'center', + 'position': (-4,6,-4) + }) + + def nextModel(self): + + self.newmodel.delete() + PART=list(self.dic.keys())[self.bodyindex] + self.modelindex =(self.modelindex+1)%len(self.dic[PART]) + model=self.dic[PART][self.modelindex] + self.newmodel=ba.newnode( + 'text', + attrs={ + 'text': model, + 'in_world': True, + 'scale': 0.02, + 'color': (1, 1, 1, 1), + 'h_align': 'center', + 'position': (6,6,-4) + }) + + self.setModel(PART,model) + + def prevModel(self): + + self.newmodel.delete() + PART=list(self.dic.keys())[self.bodyindex] + self.modelindex =(self.modelindex-1)%len(self.dic[PART]) + model=self.dic[PART][self.modelindex] + self.newmodel=ba.newnode( + 'text', + attrs={ + 'text': model, + 'in_world': True, + 'scale': 0.02, + 'color': (1, 1, 1, 1), + 'h_align': 'center', + 'position': (6,6,-4) + }) + self.setModel(PART,model) + + def setModel(self,bodypart,modelname): + global spazoutfit + body=_ba.get_foreground_host_activity().players[0].actor.node + if bodypart=='head': + body.head_model=ba.getmodel(modelname) + elif bodypart=='torso': + body.torso_model=ba.getmodel(modelname) + elif bodypart=='pelvis': + body.pelvis_model=ba.getmodel(modelname) + elif bodypart=='upper_arm': + body.upper_arm_model=ba.getmodel(modelname) + elif bodypart=='forearm': + body.forearm_model=ba.getmodel(modelname) + elif bodypart=='hand': + body.hand_model=ba.getmodel(modelname) + elif bodypart=='upper_leg': + body.upper_leg_model=ba.getmodel(modelname) + elif bodypart=='lower_leg': + body.lower_leg_model=ba.getmodel(modelname) + elif bodypart=='toes_model': + body.toes_model=ba.getmodel(modelname) + elif bodypart=='style': + body.style=modelname + elif bodypart=='color_texture': + body.color_texture=ba.gettexture(modelname) + elif bodypart=='color_mask': + body.color_mask_texture=ba.gettexture(modelname) + spazoutfit[bodypart]=modelname + + def spawn_player(self, player: Player) -> ba.Actor: + global character + if character!=None: + player.character=character + + self.setcurrentcharacter(player.character) + + 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=False, + enable_jump=False, + enable_bomb=False, + enable_pickup=False) + intp = ba.InputType + player.assigninput(intp.JUMP_PRESS, self.nextBodyPart) + player.assigninput(intp.PICK_UP_PRESS, self.prevBodyPart) + player.assigninput(intp.PUNCH_PRESS, self.nextModel) + player.assigninput(intp.BOMB_PRESS, self.prevModel) + # Also lets have them make some noise when they die. + spaz.play_big_death_sound = True + return spaz + + def handlemessage(self, msg: Any) -> Any: + + if isinstance(msg, ba.PlayerDiedMessage): + + # Augment standard behavior. + super().handlemessage(msg) + + player = msg.getplayer(Player) + self.respawn_player(player) + + + + + + + + else: + return super().handlemessage(msg) + return None + def setcurrentcharacter(self,charname): + global spazoutfit + char=ba.app.spaz_appearances[charname] + spazoutfit['head']=char.head_model + spazoutfit['hand']=char.hand_model + spazoutfit['torso']=char.torso_model + spazoutfit['pelvis']=char.pelvis_model + spazoutfit['upper_arm']=char.upper_arm_model + spazoutfit['forearm']=char.forearm_model + spazoutfit['upper_leg']=char.upper_leg_model + spazoutfit['lower_leg']=char.lower_leg_model + spazoutfit['toes_model']=char.toes_model + spazoutfit['style']=char.style + spazoutfit['color_mask']=char.color_mask_texture + spazoutfit['color_texture']=char.color_texture + + + 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 = ba.GameResults() + for team in self.teams: + results.set_team_score(team, team.score) + self.end(results=results) + def initdic(self): + self.dic={"head":["bomb","landMine","trees","wing","eyeLid","impactBomb"], + "hand":["hairTuft3","bomb","powerup"], + "torso":["bomb","landMine","bomb"], + "pelvis":["hairTuft4","bomb"], + "upper_arm":["wing","locator","bomb"], + "forearm":["flagPole","bomb"], + "upper_leg":["bomb"], + "lower_leg":["bomb"], + "toes_model":["bomb"], + "style":["spaz","female","ninja","kronk","mel","pirate","santa","frosty","bones","bear","penguin","ali","cyborg","agent","pixie","bunny"], + "color_texture":["kronk","egg1","egg2","egg3","achievementGotTheMoves","bombColor","crossOut","explosion","rgbStripes","powerupCurse","powerupHealth","impactBombColorLit"], + "color_mask":["egg1","egg2","egg3","bombColor","crossOutMask","fontExtras3"] + + } + chars=["neoSpaz","zoe","ninja","kronk","mel","jack","santa","frosty","bones","bear","penguin","ali","cyborg","agent","wizard","pixie","bunny"] + for char in chars: + self.dic["head"].append(char+"Head") + self.dic["hand"].append(char+"Hand") + self.dic["torso"].append(char+"Torso") + if char not in ['mel',"jack","santa"]: + self.dic["pelvis"].append(char+"Pelvis") + self.dic["upper_arm"].append(char+"UpperArm") + self.dic["forearm"].append(char+"ForeArm") + self.dic["upper_leg"].append(char+"UpperLeg") + self.dic["lower_leg"].append(char+"LowerLeg") + self.dic["toes_model"].append(char+"Toes") + self.dic["color_mask"].append(char+"ColorMask") + if char !="kronk": + self.dic["color_texture"].append(char+"Color") + + +cm=_ba.chatmessage + +def _new_chatmessage(msg): + if msg.split(" ")[0]=="export": + if len(msg.split(" "))>1: + savecharacter(msg.split(" ")[1]) + else: + _ba.screenmessage("Enter name of character") + elif msg.split(" ")[0]=="import": + importcharacter(msg[7:]) + + else: + cm(msg) +_ba.chatmessage=_new_chatmessage + + +def savecharacter(name): + path=os.path.join(_ba.env()["python_directory_user"],"CustomCharacters" + os.sep) + if not os.path.isdir(path): + os.makedirs(path) + if _ba.get_foreground_host_activity()!=None: + + with open(path+name+".json",'w') as f: + json.dump(spazoutfit,f,indent=4) + registercharacter(name,spazoutfit) + ba.playsound(ba.getsound("gunCocking")) + _ba.screenmessage("Character Saved") + else: + _ba.screenmessage("Works offline with Character Maker") + +def importcharacter(name): + if name in ba.app.spaz_appearances: + global character + character=name + try: + _ba.get_foreground_host_activity().players[0].actor.node.handlemessage(ba.DieMessage()) + _ba.screenmessage("Imported") + except: + _ba.screenmessage("works offline with character maker") + + + else: + _ba.screenmessage("invalid name check typo \n name is case sensitive") + + +def registercharacter(name,char): + t = Appearance(name.split(".")[0]) + t.color_texture = char['color_texture'] + t.color_mask_texture = char['color_mask'] + t.default_color = (0.6, 0.6, 0.6) + t.default_highlight = (0, 1, 0) + t.icon_texture = char['icon_texture'] + t.icon_mask_texture = char['icon_mask_texture'] + t.head_model = char['head'] + t.torso_model = char['torso'] + t.pelvis_model = char['pelvis'] + t.upper_arm_model = char['upper_arm'] + t.forearm_model = char['forearm'] + t.hand_model = char['hand'] + t.upper_leg_model = char['upper_leg'] + t.lower_leg_model = char['lower_leg'] + t.toes_model = char['toes_model'] + t.jump_sounds = char['jump_sounds'] + t.attack_sounds = char['attack_sounds'] + t.impact_sounds = char['impact_sounds'] + t.death_sounds = char['death_sounds'] + t.pickup_sounds = char['pickup_sounds'] + t.fall_sounds = char['fall_sounds'] + t.style = char['style'] + + + + + + + +# ba_meta export plugin +class HeySmoothy(ba.Plugin): + + def __init__(self): + _ba.set_party_icon_always_visible(True) + + path=os.path.join(_ba.env()["python_directory_user"],"CustomCharacters" + os.sep) + if not os.path.isdir(path): + os.makedirs(path) + files=os.listdir(path) + for file in files: + with open(path+file, 'r') as f: + character = json.load(f) + registercharacter(file,character) + + + +