From e67d89171651a05158629cf25e59b3fb2ddd0e85 Mon Sep 17 00:00:00 2001 From: DinoWattz <116862698+DinoWattz@users.noreply.github.com> Date: Mon, 23 Feb 2026 08:18:30 +0000 Subject: [PATCH] [ci] apply-plugin-metadata-and-formatting --- plugins/utilities.json | 56 +++ plugins/utilities/better_camera_shake.py | 567 ++++++++++++----------- plugins/utilities/chat_bubbles.py | 179 +++---- plugins/utilities/enhanced_effects.py | 277 +++++------ plugins/utilities/sleep_on_afk.py | 67 +-- 5 files changed, 627 insertions(+), 519 deletions(-) diff --git a/plugins/utilities.json b/plugins/utilities.json index 0c62bac..cf8f0e7 100644 --- a/plugins/utilities.json +++ b/plugins/utilities.json @@ -2426,6 +2426,62 @@ "md5sum": "d8002dfc93be74d71f8e6e4aacfe6fd1" } } + }, + "better_camera_shake": { + "description": "This plugin makes the camera only shake when an explosion hits a player character, begone excessive camera shaking!", + "external_url": "", + "authors": [ + { + "name": "DinoWattz", + "email": "", + "discord": "" + } + ], + "versions": { + "1.0.0": null + } + }, + "chat_bubbles": { + "description": "Adds whatever the players say above their character, includes a few other features too!", + "external_url": "", + "authors": [ + { + "name": "DinoWattz", + "email": "", + "discord": "" + } + ], + "versions": { + "1.0.0": null + } + }, + "enhanced_effects": { + "description": "Explosions affect character colors, slow-mo tnt, fair colored shields, a trippy background screen and maybe more in the future!", + "external_url": "", + "authors": [ + { + "name": "DinoWattz", + "email": "", + "discord": "" + } + ], + "versions": { + "1.0.0": null + } + }, + "sleep_on_afk": { + "description": "Staying idle for 40 seconds will make your character fall asleep, they need rest too..", + "external_url": "", + "authors": [ + { + "name": "DinoWattz", + "email": "", + "discord": "" + } + ], + "versions": { + "1.0.0": null + } } } } \ No newline at end of file diff --git a/plugins/utilities/better_camera_shake.py b/plugins/utilities/better_camera_shake.py index 634fb1a..d176f46 100644 --- a/plugins/utilities/better_camera_shake.py +++ b/plugins/utilities/better_camera_shake.py @@ -26,356 +26,363 @@ plugman = dict( ) # Camera shake only on player impact + + def new__init__( - self, - *, - position: Sequence[float] = (0.0, 1.0, 0.0), - velocity: Sequence[float] = (0.0, 0.0, 0.0), - blast_radius: float = 2.0, - blast_type: str = 'normal', - source_player: bs.Player | None = None, - hit_type: str = 'explosion', - hit_subtype: str = 'normal', - ): - """Instantiate with given values.""" + self, + *, + position: Sequence[float] = (0.0, 1.0, 0.0), + velocity: Sequence[float] = (0.0, 0.0, 0.0), + blast_radius: float = 2.0, + blast_type: str = 'normal', + source_player: bs.Player | None = None, + hit_type: str = 'explosion', + hit_subtype: str = 'normal', +): + """Instantiate with given values.""" - # bah; get off my lawn! - # pylint: disable=too-many-locals - # pylint: disable=too-many-statements + # bah; get off my lawn! + # pylint: disable=too-many-locals + # pylint: disable=too-many-statements - super(type(self), self).__init__() + super(type(self), self).__init__() - shared = SharedObjects.get() - factory = BombFactory.get() + shared = SharedObjects.get() + factory = BombFactory.get() - self.blast_type = blast_type - self._source_player = source_player - self.hit_type = hit_type - self.hit_subtype = hit_subtype - self.radius = blast_radius + self.blast_type = blast_type + self._source_player = source_player + self.hit_type = hit_type + self.hit_subtype = hit_subtype + self.radius = blast_radius - # Set our position a bit lower so we throw more things upward. - rmats = (factory.blast_material, shared.attack_material) - self.node = bs.newnode( - 'region', - delegate=self, - attrs={ - 'position': (position[0], position[1] - 0.1, position[2]), - 'scale': (self.radius, self.radius, self.radius), - 'type': 'sphere', - 'materials': rmats, - }, - ) + # Set our position a bit lower so we throw more things upward. + rmats = (factory.blast_material, shared.attack_material) + self.node = bs.newnode( + 'region', + delegate=self, + attrs={ + 'position': (position[0], position[1] - 0.1, position[2]), + 'scale': (self.radius, self.radius, self.radius), + 'type': 'sphere', + 'materials': rmats, + }, + ) - bs.timer(0.05, self.node.delete) + bs.timer(0.05, self.node.delete) - # Throw in an explosion and flash. - evel = (velocity[0], max(-1.0, velocity[1]), velocity[2]) - explosion = bs.newnode( - 'explosion', - attrs={ - 'position': position, - 'velocity': evel, - 'radius': self.radius, - 'big': (self.blast_type == 'tnt'), - }, - ) - if self.blast_type == 'ice': - explosion.color = (0, 0.05, 0.4) + # Throw in an explosion and flash. + evel = (velocity[0], max(-1.0, velocity[1]), velocity[2]) + explosion = bs.newnode( + 'explosion', + attrs={ + 'position': position, + 'velocity': evel, + 'radius': self.radius, + 'big': (self.blast_type == 'tnt'), + }, + ) + if self.blast_type == 'ice': + explosion.color = (0, 0.05, 0.4) - bs.timer(1.0, explosion.delete) + bs.timer(1.0, explosion.delete) - if self.blast_type != 'ice': - bs.emitfx( - position=position, - velocity=velocity, - count=int(1.0 + random.random() * 4), - emit_type='tendrils', - tendril_type='thin_smoke', - ) + if self.blast_type != 'ice': bs.emitfx( position=position, velocity=velocity, - count=int(4.0 + random.random() * 4), + count=int(1.0 + random.random() * 4), emit_type='tendrils', - tendril_type='ice' if self.blast_type == 'ice' else 'smoke', - ) - bs.emitfx( - position=position, - emit_type='distortion', - spread=1.0 if self.blast_type == 'tnt' else 2.0, + tendril_type='thin_smoke', ) + bs.emitfx( + position=position, + velocity=velocity, + count=int(4.0 + random.random() * 4), + emit_type='tendrils', + tendril_type='ice' if self.blast_type == 'ice' else 'smoke', + ) + bs.emitfx( + position=position, + emit_type='distortion', + spread=1.0 if self.blast_type == 'tnt' else 2.0, + ) - # And emit some shrapnel. - if self.blast_type == 'ice': + # And emit some shrapnel. + if self.blast_type == 'ice': - def emit() -> None: - bs.emitfx( - position=position, - velocity=velocity, - count=30, - spread=2.0, - scale=0.4, - chunk_type='ice', - emit_type='stickers', - ) + def emit() -> None: + bs.emitfx( + position=position, + velocity=velocity, + count=30, + spread=2.0, + scale=0.4, + chunk_type='ice', + emit_type='stickers', + ) - # It looks better if we delay a bit. - bs.timer(0.05, emit) + # It looks better if we delay a bit. + bs.timer(0.05, emit) - elif self.blast_type == 'sticky': + elif self.blast_type == 'sticky': - def emit() -> None: + def emit() -> None: + bs.emitfx( + position=position, + velocity=velocity, + count=int(4.0 + random.random() * 8), + spread=0.7, + chunk_type='slime', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=int(4.0 + random.random() * 8), + scale=0.5, + spread=0.7, + chunk_type='slime', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=15, + scale=0.6, + chunk_type='slime', + emit_type='stickers', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=20, + scale=0.7, + chunk_type='spark', + emit_type='stickers', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=int(6.0 + random.random() * 12), + scale=0.8, + spread=1.5, + chunk_type='spark', + ) + + # It looks better if we delay a bit. + bs.timer(0.05, emit) + + elif self.blast_type == 'impact': + + def emit() -> None: + bs.emitfx( + position=position, + velocity=velocity, + count=int(4.0 + random.random() * 8), + scale=0.8, + chunk_type='metal', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=int(4.0 + random.random() * 8), + scale=0.4, + chunk_type='metal', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=20, + scale=0.7, + chunk_type='spark', + emit_type='stickers', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=int(8.0 + random.random() * 15), + scale=0.8, + spread=1.5, + chunk_type='spark', + ) + + # It looks better if we delay a bit. + bs.timer(0.05, emit) + + else: # Regular or land mine bomb shrapnel. + + def emit() -> None: + if self.blast_type != 'tnt': bs.emitfx( position=position, velocity=velocity, count=int(4.0 + random.random() * 8), - spread=0.7, - chunk_type='slime', + chunk_type='rock', ) bs.emitfx( position=position, velocity=velocity, count=int(4.0 + random.random() * 8), scale=0.5, - spread=0.7, - chunk_type='slime', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=15, - scale=0.6, - chunk_type='slime', - emit_type='stickers', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=20, - scale=0.7, - chunk_type='spark', - emit_type='stickers', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=int(6.0 + random.random() * 12), - scale=0.8, - spread=1.5, - chunk_type='spark', + chunk_type='rock', ) + bs.emitfx( + position=position, + velocity=velocity, + count=30, + scale=1.0 if self.blast_type == 'tnt' else 0.7, + chunk_type='spark', + emit_type='stickers', + ) + bs.emitfx( + position=position, + velocity=velocity, + count=int(18.0 + random.random() * 20), + scale=1.0 if self.blast_type == 'tnt' else 0.8, + spread=1.5, + chunk_type='spark', + ) - # It looks better if we delay a bit. - bs.timer(0.05, emit) + # TNT throws splintery chunks. + if self.blast_type == 'tnt': - elif self.blast_type == 'impact': - - def emit() -> None: - bs.emitfx( - position=position, - velocity=velocity, - count=int(4.0 + random.random() * 8), - scale=0.8, - chunk_type='metal', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=int(4.0 + random.random() * 8), - scale=0.4, - chunk_type='metal', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=20, - scale=0.7, - chunk_type='spark', - emit_type='stickers', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=int(8.0 + random.random() * 15), - scale=0.8, - spread=1.5, - chunk_type='spark', - ) - - # It looks better if we delay a bit. - bs.timer(0.05, emit) - - else: # Regular or land mine bomb shrapnel. - - def emit() -> None: - if self.blast_type != 'tnt': + def emit_splinters() -> None: bs.emitfx( position=position, velocity=velocity, - count=int(4.0 + random.random() * 8), - chunk_type='rock', + count=int(20.0 + random.random() * 25), + scale=0.8, + spread=1.0, + chunk_type='splinter', ) + + bs.timer(0.01, emit_splinters) + + # Every now and then do a sparky one. + if self.blast_type == 'tnt' or random.random() < 0.1: + + def emit_extra_sparks() -> None: bs.emitfx( position=position, velocity=velocity, - count=int(4.0 + random.random() * 8), - scale=0.5, - chunk_type='rock', + count=int(10.0 + random.random() * 20), + scale=0.8, + spread=1.5, + chunk_type='spark', ) - bs.emitfx( - position=position, - velocity=velocity, - count=30, - scale=1.0 if self.blast_type == 'tnt' else 0.7, - chunk_type='spark', - emit_type='stickers', - ) - bs.emitfx( - position=position, - velocity=velocity, - count=int(18.0 + random.random() * 20), - scale=1.0 if self.blast_type == 'tnt' else 0.8, - spread=1.5, - chunk_type='spark', - ) - # TNT throws splintery chunks. - if self.blast_type == 'tnt': + bs.timer(0.02, emit_extra_sparks) - def emit_splinters() -> None: - bs.emitfx( - position=position, - velocity=velocity, - count=int(20.0 + random.random() * 25), - scale=0.8, - spread=1.0, - chunk_type='splinter', - ) + # It looks better if we delay a bit. + bs.timer(0.05, emit) - bs.timer(0.01, emit_splinters) + lcolor = (0.6, 0.6, 1.0) if self.blast_type == 'ice' else (1, 0.3, 0.1) + light = bs.newnode( + 'light', + attrs={ + 'position': position, + 'volume_intensity_scale': 10.0, + 'color': lcolor, + }, + ) - # Every now and then do a sparky one. - if self.blast_type == 'tnt' or random.random() < 0.1: + scl = random.uniform(0.6, 0.9) + scorch_radius = light_radius = self.radius + if self.blast_type == 'tnt': + light_radius *= 1.4 + scorch_radius *= 1.15 + scl *= 3.0 - def emit_extra_sparks() -> None: - bs.emitfx( - position=position, - velocity=velocity, - count=int(10.0 + random.random() * 20), - scale=0.8, - spread=1.5, - chunk_type='spark', - ) + iscale = 1.6 + bs.animate( + light, + 'intensity', + { + 0: 2.0 * iscale, + scl * 0.02: 0.1 * iscale, + scl * 0.025: 0.2 * iscale, + scl * 0.05: 17.0 * iscale, + scl * 0.06: 5.0 * iscale, + scl * 0.08: 4.0 * iscale, + scl * 0.2: 0.6 * iscale, + scl * 2.0: 0.00 * iscale, + scl * 3.0: 0.0, + }, + ) + bs.animate( + light, + 'radius', + { + 0: light_radius * 0.2, + scl * 0.05: light_radius * 0.55, + scl * 0.1: light_radius * 0.3, + scl * 0.3: light_radius * 0.15, + scl * 1.0: light_radius * 0.05, + }, + ) + bs.timer(scl * 3.0, light.delete) - bs.timer(0.02, emit_extra_sparks) + # Make a scorch that fades over time. + scorch = bs.newnode( + 'scorch', + attrs={ + 'position': position, + 'size': scorch_radius * 0.5, + 'big': (self.blast_type == 'tnt'), + }, + ) + if self.blast_type == 'ice': + scorch.color = (1, 1, 1.5) - # It looks better if we delay a bit. - bs.timer(0.05, emit) + bs.animate(scorch, 'presence', {3.000: 1, 13.000: 0}) + bs.timer(13.0, scorch.delete) - lcolor = (0.6, 0.6, 1.0) if self.blast_type == 'ice' else (1, 0.3, 0.1) - light = bs.newnode( - 'light', - attrs={ - 'position': position, - 'volume_intensity_scale': 10.0, - 'color': lcolor, - }, - ) + if self.blast_type == 'ice': + factory.hiss_sound.play(position=light.position) - scl = random.uniform(0.6, 0.9) - scorch_radius = light_radius = self.radius - if self.blast_type == 'tnt': - light_radius *= 1.4 - scorch_radius *= 1.15 - scl *= 3.0 + lpos = light.position + factory.random_explode_sound().play(position=lpos) + factory.debris_fall_sound.play(position=lpos) - iscale = 1.6 - bs.animate( - light, - 'intensity', - { - 0: 2.0 * iscale, - scl * 0.02: 0.1 * iscale, - scl * 0.025: 0.2 * iscale, - scl * 0.05: 17.0 * iscale, - scl * 0.06: 5.0 * iscale, - scl * 0.08: 4.0 * iscale, - scl * 0.2: 0.6 * iscale, - scl * 2.0: 0.00 * iscale, - scl * 3.0: 0.0, - }, - ) - bs.animate( - light, - 'radius', - { - 0: light_radius * 0.2, - scl * 0.05: light_radius * 0.55, - scl * 0.1: light_radius * 0.3, - scl * 0.3: light_radius * 0.15, - scl * 1.0: light_radius * 0.05, - }, - ) - bs.timer(scl * 3.0, light.delete) + # ↓ We don't need any of this, we're fully trained professionals. + # bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0) - # Make a scorch that fades over time. - scorch = bs.newnode( - 'scorch', - attrs={ - 'position': position, - 'size': scorch_radius * 0.5, - 'big': (self.blast_type == 'tnt'), - }, - ) - if self.blast_type == 'ice': - scorch.color = (1, 1, 1.5) - - bs.animate(scorch, 'presence', {3.000: 1, 13.000: 0}) - bs.timer(13.0, scorch.delete) - - if self.blast_type == 'ice': - factory.hiss_sound.play(position=light.position) - - lpos = light.position + # TNT is more epic. + if self.blast_type == 'tnt': factory.random_explode_sound().play(position=lpos) - factory.debris_fall_sound.play(position=lpos) - # ↓ We don't need any of this, we're fully trained professionals. - #bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0) - - # TNT is more epic. - if self.blast_type == 'tnt': + def _extra_boom() -> None: factory.random_explode_sound().play(position=lpos) - def _extra_boom() -> None: - factory.random_explode_sound().play(position=lpos) + bs.timer(0.25, _extra_boom) - bs.timer(0.25, _extra_boom) + def _extra_debris_sound() -> None: + factory.debris_fall_sound.play(position=lpos) + factory.wood_debris_fall_sound.play(position=lpos) - def _extra_debris_sound() -> None: - factory.debris_fall_sound.play(position=lpos) - factory.wood_debris_fall_sound.play(position=lpos) + bs.timer(0.4, _extra_debris_sound) - bs.timer(0.4, _extra_debris_sound) bomb.Blast.__init__ = new__init__ org_handlemessage = bomb.Blast.handlemessage + + def new_handlemessage(self, msg: Any, *args, **kwargs) -> Any: org_handlemessage(self, msg, *args, **kwargs) if isinstance(msg, ExplodeHitMessage): - node = bs.getcollision().opposingnode - delegate = node.getdelegate(PlayerSpaz) - has_used_camerashake = getattr(self, 'has_used_camerashake', False) - if not has_used_camerashake and (delegate and not delegate.shield) and not node.invincible: - self.has_used_camerashake = True - bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0) + node = bs.getcollision().opposingnode + delegate = node.getdelegate(PlayerSpaz) + has_used_camerashake = getattr(self, 'has_used_camerashake', False) + if not has_used_camerashake and (delegate and not delegate.shield) and not node.invincible: + self.has_used_camerashake = True + bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0) bomb.Blast.handlemessage = new_handlemessage # ba_meta export babase.Plugin + + class Plugin(babase.Plugin): - pass \ No newline at end of file + pass diff --git a/plugins/utilities/chat_bubbles.py b/plugins/utilities/chat_bubbles.py index 446ad07..af7eb31 100644 --- a/plugins/utilities/chat_bubbles.py +++ b/plugins/utilities/chat_bubbles.py @@ -23,8 +23,9 @@ plugman = dict( version="1.0.0", ) -PRINT_CHAT_MESSAGES = True # Prints chat messages to the console as "[Character's Name] Player 1/Player 2: Hello World!" -BUBBLE_DURATION: float = 4.7 # 4.7 matches the default onscreen message duration +# Prints chat messages to the console as "[Character's Name] Player 1/Player 2: Hello World!" +PRINT_CHAT_MESSAGES = True +BUBBLE_DURATION: float = 4.7 # 4.7 matches the default onscreen message duration SPEAK_VOLUME = 1.5 SHOUT_VOLUME = 3.0 @@ -35,13 +36,13 @@ CELEBRATE_MESSAGE = [(bs.CelebrateMessage, {"duration": 1.0})] EASTER_EGGS = [ { "triggers": { - "hi", "hello", "hey", "hiya", "yo", "sup", "heya", "howdy", - "greetings", "salutations", "ahoy", "ahoyhoy", "bonjour", - "hola", "ciao", "ola", "oi", "eae", "salve", + "hi", "hello", "hey", "hiya", "yo", "sup", "heya", "howdy", + "greetings", "salutations", "ahoy", "ahoyhoy", "bonjour", + "hola", "ciao", "ola", "oi", "eae", "salve", "bye", "goodbye", "see ya", "see you", "cya", "adios", "chau", "chao", "tchau", "adeus", "ate mais", "falou", "flw" - }, + }, "node_messages": [("celebrate_r", 1000)], }, { @@ -229,6 +230,8 @@ EASTER_EGGS = [ ] # Chat bubble functionality + + def create_bubble(player: bs.SessionPlayer, text: str, name: str): msg = normalize_message(text) is_caps = len(text) > 1 and text.isupper() @@ -240,11 +243,14 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str): def _fallback_logic(): nonlocal handled, activityplayer color = (1, 1, 0.4) - image_icon = {'texture': bs.gettexture("cuteSpaz"), 'tint_texture': bs.gettexture("cuteSpaz"), 'tint_color': (1.0, 1.0, 1.0), 'tint2_color': (1.0, 1.0, 1.0)} + image_icon = {'texture': bs.gettexture("cuteSpaz"), 'tint_texture': bs.gettexture( + "cuteSpaz"), 'tint_color': (1.0, 1.0, 1.0), 'tint2_color': (1.0, 1.0, 1.0)} if activityplayer: - normal_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=1.15) ) - saturated_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=0.75) ) + normal_highlight = bs.normalized_color( + bs.safecolor(player.highlight, target_intensity=1.15)) + saturated_highlight = bs.normalized_color( + bs.safecolor(player.highlight, target_intensity=0.75)) color = saturated_highlight if is_caps else normal_highlight image_icon = activityplayer.get_icon() @@ -252,7 +258,7 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str): color=color, top=True, image=image_icon) - + for egg in EASTER_EGGS: if msg in egg["triggers"]: if egg.get("sound"): @@ -298,7 +304,7 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str): activity = actor.getactivity() if activity is None or getattr(activity, 'expired', False): continue - + with activity.context: # Calculate scale based on text length base_scale = 0.015 @@ -312,23 +318,25 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str): }) char_node.connectattr('torso_position', mnode, 'input2') - normal_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=1.15) ) - saturated_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=0.75) ) + normal_highlight = bs.normalized_color( + bs.safecolor(player.highlight, target_intensity=1.15)) + saturated_highlight = bs.normalized_color( + bs.safecolor(player.highlight, target_intensity=0.75)) bubble_color = saturated_highlight if is_caps else normal_highlight textnode = bs.newnode('text', - owner=char_node, - attrs={ - 'text': text, - 'color': bubble_color, - 'shadow': 0.5, - 'flatness': 0.5, - 'scale': scale, - 'h_align': 'center', - 'v_align': 'bottom', - 'in_world': True, - 'opacity': 1.0, - }) + owner=char_node, + attrs={ + 'text': text, + 'color': bubble_color, + 'shadow': 0.5, + 'flatness': 0.5, + 'scale': scale, + 'h_align': 'center', + 'v_align': 'bottom', + 'in_world': True, + 'opacity': 1.0, + }) mnode.connectattr('output', textnode, 'position') @@ -366,7 +374,8 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str): sound = random.choice(pickup_sounds) if sound: - _play_chat_sound(actor, sound, SHOUT_VOLUME if is_caps else SPEAK_VOLUME) + _play_chat_sound( + actor, sound, SHOUT_VOLUME if is_caps else SPEAK_VOLUME) # Animation logic if egg.get('messages') and actor.is_alive() and hasattr(actor, 'handlemessage'): @@ -417,12 +426,13 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str): b for b in activityplayer.customdata['chat_bubbles'] if b[0] != textnode ] bs.timer(bubble_duration, cleanup) - + if not bubble_created: # No valid actor found: play sound if possible _fallback_logic() return + def update_bubble_offsets(bubbles): for i, (tnode, mnode) in enumerate(reversed(bubbles)): y_offset = 1.2 + 0.25 * i @@ -430,6 +440,8 @@ def update_bubble_offsets(bubbles): mnode.input1 = (0, y_offset, 0) # Tools + + def normalize_message(text): msg = text.strip().lower() msg = msg.translate(str.maketrans('', '', string.punctuation)) @@ -437,27 +449,30 @@ def normalize_message(text): c for c in unicodedata.normalize('NFD', msg) if unicodedata.category(c) != 'Mn' ) - + return msg # ba_meta export babase.Plugin + + class Plugin(babase.Plugin): def on_app_running(self) -> None: # Hook into chat message filtering # We're delaying until the app runs and using a timer so it's more likely to work with other chat plugins (please don't make a chat plugin/filter that is delayed like this). bs.apptimer(0.001, bs.WeakCallStrict(self.apply_patch)) - + def apply_patch(self): import bascenev1._hooks as _hooks - + _org_filter_chat_message = _hooks.filter_chat_message + def _chat_bubbles_hook(msg: str, client_id: int, *args, **kwargs) -> str | None: org_msg = _org_filter_chat_message(msg, client_id, *args, **kwargs) - + if org_msg is None: # The original message has been ignored, so we should ignore it as well return org_msg - + # Chat bubble sorcery try: roster = bs.get_game_roster() @@ -478,42 +493,43 @@ class Plugin(babase.Plugin): player_list.append(player) continue if player_list: - # Get combined name for multiple players on same client - name = '' - created_bubble = False - - for player in player_list: - if name == '': - name = player.getname() - else: - name = name + '/' + player.getname() + # Get combined name for multiple players on same client + name = '' + created_bubble = False - # Create chat bubble - for player in player_list: - activityplayer: bs.Player = player.activityplayer - # We have a session player but not an activity player - if not activityplayer: - if not dead_bubble: - create_bubble(player, org_msg, name) - created_bubble = dead_bubble = True - continue - # We have a dead activity player - if not activityplayer.is_alive() and not dead_bubble: + for player in player_list: + if name == '': + name = player.getname() + else: + name = name + '/' + player.getname() + + # Create chat bubble + for player in player_list: + activityplayer: bs.Player = player.activityplayer + # We have a session player but not an activity player + if not activityplayer: + if not dead_bubble: create_bubble(player, org_msg, name) created_bubble = dead_bubble = True - character = activityplayer.character if character is None else character - # elif we have an alive activity player - elif activityplayer.is_alive(): - create_bubble(player, org_msg, name) - created_bubble = True - character = activityplayer.character if character is None else character + continue + # We have a dead activity player + if not activityplayer.is_alive() and not dead_bubble: + create_bubble(player, org_msg, name) + created_bubble = dead_bubble = True + character = activityplayer.character if character is None else character + # elif we have an alive activity player + elif activityplayer.is_alive(): + create_bubble(player, org_msg, name) + created_bubble = True + character = activityplayer.character if character is None else character - if created_bubble: - matched = True - if PRINT_CHAT_MESSAGES: - print(f"[{character}] {name}: {org_msg}") - else: - bs.logging.warning("Failed to create a chat bubble, using non-session player method now.") + if created_bubble: + matched = True + if PRINT_CHAT_MESSAGES: + print(f"[{character}] {name}: {org_msg}") + else: + bs.logging.warning( + "Failed to create a chat bubble, using non-session player method now.") # Non-session players (in lobby or just spectating, this only works for servers) if not matched and not dead_bubble: for client in roster: @@ -537,41 +553,42 @@ class Plugin(babase.Plugin): class Focus(bs.Actor): def __init__( - self, - *, - owner: bs.Node | None = None, - ): + self, + *, + owner: bs.Node | None = None, + ): super().__init__() self._focusnode_material = bs.Material() self._focusnode_material.add_actions( - actions=('modify_node_collision', 'collide', False), - ) - + actions=('modify_node_collision', 'collide', False), + ) + if getattr(owner, 'owner', None): assert owner is not None - area_of_interest = False if getattr(owner.owner, 'is_area_of_interest', False) else True + area_of_interest = False if getattr( + owner.owner, 'is_area_of_interest', False) else True else: area_of_interest = False if getattr(owner, 'is_area_of_interest', False) else True # I'm tired of this area_of_interest nonsense, it never works right - #area_of_interest = False + # area_of_interest = False # actually it might work now that we don't spawn bubbles for dead players. self.node = bs.newnode( - 'prop', - delegate=self, - owner=owner, - attrs={ - 'body': 'sphere', + 'prop', + delegate=self, + owner=owner, + attrs={ + 'body': 'sphere', 'body_scale': 0.0, 'shadow_size': 0.0, 'gravity_scale': 0.0, 'is_area_of_interest': area_of_interest, 'materials': (self._focusnode_material,), - }, - ) - + }, + ) + @override def handlemessage(self, msg: Any) -> Any: assert not self.expired diff --git a/plugins/utilities/enhanced_effects.py b/plugins/utilities/enhanced_effects.py index 9a33534..d78f561 100644 --- a/plugins/utilities/enhanced_effects.py +++ b/plugins/utilities/enhanced_effects.py @@ -25,92 +25,95 @@ plugman = dict( # Transparent Background (kinda hacky, works best on pc with high/higher quality 'visuals' setting) BACKGROUND_OPACITY = 0.5 + + def __modified_background_init( - self, - fade_time: float = 0.5, - start_faded: bool = False, - show_logo: bool = False, - ): - super(type(self), self).__init__() - self._dying = False - self.fade_time = fade_time - # We're special in that we create our node in the session - # scene instead of the activity scene. - # This way we can overlap multiple activities for fades - # and whatnot. - session = bs.getsession() - self._session = weakref.ref(session) - with session.context: - self.node = bs.newnode( + self, + fade_time: float = 0.5, + start_faded: bool = False, + show_logo: bool = False, +): + super(type(self), self).__init__() + self._dying = False + self.fade_time = fade_time + # We're special in that we create our node in the session + # scene instead of the activity scene. + # This way we can overlap multiple activities for fades + # and whatnot. + session = bs.getsession() + self._session = weakref.ref(session) + with session.context: + self.node = bs.newnode( + 'image', + delegate=self, + attrs={ + 'fill_screen': True, + 'texture': bs.gettexture('bg'), + 'tilt_translate': -0.3, + 'has_alpha_channel': False, + 'color': (1, 1, 1), + 'opacity': BACKGROUND_OPACITY, + }, + ) + if not start_faded: + bs.animate( + self.node, + 'opacity', + {0.0: 0.0, self.fade_time: BACKGROUND_OPACITY}, + loop=False, + ) + if show_logo: + logo_texture = bs.gettexture('logo') + logo_mesh = bs.getmesh('logo') + logo_mesh_transparent = bs.getmesh('logoTransparent') + self.logo = bs.newnode( 'image', - delegate=self, + owner=self.node, attrs={ - 'fill_screen': True, - 'texture': bs.gettexture('bg'), - 'tilt_translate': -0.3, - 'has_alpha_channel': False, - 'color': (1, 1, 1), - 'opacity': BACKGROUND_OPACITY, + 'texture': logo_texture, + 'mesh_opaque': logo_mesh, + 'mesh_transparent': logo_mesh_transparent, + 'scale': (0.7, 0.7), + 'vr_depth': -250, + 'color': (0.15, 0.15, 0.15), + 'position': (0, 0), + 'tilt_translate': -0.05, + 'absolute_scale': False, }, ) - if not start_faded: - bs.animate( - self.node, - 'opacity', - {0.0: 0.0, self.fade_time: BACKGROUND_OPACITY}, - loop=False, + self.node.connectattr('opacity', self.logo, 'opacity') + # add jitter/pulse for a stop-motion-y look unless we're in VR + # in which case stillness is better + if not bs.app.env.vr: + self.cmb = bs.newnode( + 'combine', owner=self.node, attrs={'size': 2} ) - if show_logo: - logo_texture = bs.gettexture('logo') - logo_mesh = bs.getmesh('logo') - logo_mesh_transparent = bs.getmesh('logoTransparent') - self.logo = bs.newnode( - 'image', - owner=self.node, - attrs={ - 'texture': logo_texture, - 'mesh_opaque': logo_mesh, - 'mesh_transparent': logo_mesh_transparent, - 'scale': (0.7, 0.7), - 'vr_depth': -250, - 'color': (0.15, 0.15, 0.15), - 'position': (0, 0), - 'tilt_translate': -0.05, - 'absolute_scale': False, - }, + for attr in ['input0', 'input1']: + bs.animate( + self.cmb, + attr, + {0.0: 0.693, 0.05: 0.7, 0.5: 0.693}, + loop=True, + ) + self.cmb.connectattr('output', self.logo, 'scale') + cmb = bs.newnode( + 'combine', owner=self.node, attrs={'size': 2} ) - self.node.connectattr('opacity', self.logo, 'opacity') - # add jitter/pulse for a stop-motion-y look unless we're in VR - # in which case stillness is better - if not bs.app.env.vr: - self.cmb = bs.newnode( - 'combine', owner=self.node, attrs={'size': 2} - ) - for attr in ['input0', 'input1']: - bs.animate( - self.cmb, - attr, - {0.0: 0.693, 0.05: 0.7, 0.5: 0.693}, - loop=True, - ) - self.cmb.connectattr('output', self.logo, 'scale') - cmb = bs.newnode( - 'combine', owner=self.node, attrs={'size': 2} - ) - cmb.connectattr('output', self.logo, 'position') - # Gen some random keys for that stop-motion-y look. - keys = {} - timeval = 0.0 - for _i in range(10): - keys[timeval] = (random.random() - 0.5) * 0.0015 - timeval += random.random() * 0.1 - bs.animate(cmb, 'input0', keys, loop=True) - keys = {} - timeval = 0.0 - for _i in range(10): - keys[timeval] = (random.random() - 0.5) * 0.0015 + 0.05 - timeval += random.random() * 0.1 - bs.animate(cmb, 'input1', keys, loop=True) + cmb.connectattr('output', self.logo, 'position') + # Gen some random keys for that stop-motion-y look. + keys = {} + timeval = 0.0 + for _i in range(10): + keys[timeval] = (random.random() - 0.5) * 0.0015 + timeval += random.random() * 0.1 + bs.animate(cmb, 'input0', keys, loop=True) + keys = {} + timeval = 0.0 + for _i in range(10): + keys[timeval] = (random.random() - 0.5) * 0.0015 + 0.05 + timeval += random.random() * 0.1 + bs.animate(cmb, 'input1', keys, loop=True) + def _modified_background_die(self, immediate: bool = False) -> None: session = self._session() @@ -138,10 +141,13 @@ def _modified_background_die(self, immediate: bool = False) -> None: ) bs.timer(self.fade_time + 0.1, self.node.delete) + Background.__init__ = __modified_background_init Background._die = _modified_background_die # Tools + + def blend_toward( rgb: tuple[float, float, float], target: tuple[float, float, float], @@ -177,6 +183,8 @@ def blend_toward( return tuple(clamp01(linear_to_srgb(c)) for c in blended_lin) # ba_meta export babase.Plugin + + class Plugin(babase.Plugin): def on_app_running(self) -> None: from bascenev1lib.actor.bomb import Blast, ExplodeHitMessage @@ -190,7 +198,8 @@ class Plugin(babase.Plugin): def equip_shields(self, decay: bool = False) -> None: org_equip_shields(self, decay) if self.shield is not None: - safe_highlight = bs.safecolor(getattr(self, '_original_highlight', self.node.highlight), target_intensity=0.75) + safe_highlight = bs.safecolor( + getattr(self, '_original_highlight', self.node.highlight), target_intensity=0.75) self.shield.color = bs.normalized_color(safe_highlight) Spaz.equip_shields = equip_shields @@ -208,7 +217,8 @@ class Plugin(babase.Plugin): # Pause gnode.paused = True - self._pause_timer = bs.DisplayTimer(0.01, bs.CallPartial(setattr, gnode, 'paused', True), True) + self._pause_timer = bs.DisplayTimer( + 0.01, bs.CallPartial(setattr, gnode, 'paused', True), True) delay = 0.06 @@ -217,12 +227,14 @@ class Plugin(babase.Plugin): # Slow Motion gnode.slow_motion = True - self._slow_motion_timer = bs.DisplayTimer(0.01, bs.CallPartial(setattr, gnode, 'slow_motion', True), True) + self._slow_motion_timer = bs.DisplayTimer( + 0.01, bs.CallPartial(setattr, gnode, 'slow_motion', True), True) delay += 0.14 bs.displaytimer(delay, bs.CallPartial(setattr, self, '_slow_motion_timer', None)) - bs.displaytimer(delay, bs.CallPartial(setattr, gnode, 'slow_motion', activity.slow_motion)) + bs.displaytimer(delay, bs.CallPartial( + setattr, gnode, 'slow_motion', activity.slow_motion)) Blast.__init__ = new_init @@ -233,58 +245,61 @@ class Plugin(babase.Plugin): org_handlemessage(self, msg, *args, **kwargs) if isinstance(msg, ExplodeHitMessage): - node = bs.getcollision().opposingnode - delegate = node.getdelegate(PlayerSpaz) or node.getdelegate(SpazBot) - if (delegate and not delegate.shield) and not node.invincible: - if not hasattr(delegate, '_original_color'): - delegate._original_color = node.color - delegate._original_highlight = node.highlight + node = bs.getcollision().opposingnode + delegate = node.getdelegate(PlayerSpaz) or node.getdelegate(SpazBot) + if (delegate and not delegate.shield) and not node.invincible: + if not hasattr(delegate, '_original_color'): + delegate._original_color = node.color + delegate._original_highlight = node.highlight - original_color = delegate._original_color - original_highlight = delegate._original_highlight + original_color = delegate._original_color + original_highlight = delegate._original_highlight + blend_time = 1.0 + start_time = 6.0 + end_time = 7.0 + + bs.emitfx( + position=node.position, + velocity=node.velocity, + count=int(4.0 + random.random() * 4), + emit_type='tendrils', + tendril_type='ice' if self.blast_type == 'ice' else 'smoke', + ) + + explosion_intensity = 0.70 if not self.blast_type == 'tnt' else 0.96 + + if self.blast_type == 'ice': + explosion_intensity = 0.91 + explosion_color = (0.07, 0.6, 1.5) + elif self.blast_type == 'sticky': + explosion_intensity = 0.91 + explosion_color = (0.07, 0.9, 0.07) blend_time = 1.0 - start_time = 6.0 - end_time = 7.0 + start_time = 1.2 + end_time = 3.0 + else: + explosion_color = (0.07, 0.03, 0.0) - bs.emitfx( - position=node.position, - velocity=node.velocity, - count=int(4.0 + random.random() * 4), - emit_type='tendrils', - tendril_type='ice' if self.blast_type == 'ice' else 'smoke', - ) + blend_color = blend_toward(node.color, explosion_color, + explosion_intensity, True) + blend_highlight = blend_toward( + node.highlight, explosion_color, explosion_intensity-0.03, True) - explosion_intensity = 0.70 if not self.blast_type == 'tnt' else 0.96 - - if self.blast_type == 'ice': - explosion_intensity = 0.91 - explosion_color = (0.07, 0.6, 1.5) - elif self.blast_type == 'sticky': - explosion_intensity = 0.91 - explosion_color = (0.07, 0.9, 0.07) - blend_time = 1.0 - start_time = 1.2 - end_time = 3.0 - else: - explosion_color = (0.07, 0.03, 0.0) + bs.animate_array(node, 'color', 3, { + blend_time: blend_color, + start_time: blend_color, + end_time: original_color + }) + bs.animate_array(node, 'highlight', 3, { + blend_time: blend_highlight, + start_time: blend_highlight, + end_time: original_highlight + }) + delegate._color_timer = bs.Timer( + end_time, bs.CallPartial(delattr, delegate, '_original_color')) + delegate._highlight_timer = bs.Timer( + end_time, bs.CallPartial(delattr, delegate, '_original_highlight')) - blend_color = blend_toward(node.color, explosion_color, explosion_intensity, True) - blend_highlight = blend_toward(node.highlight, explosion_color, explosion_intensity-0.03, True) - - bs.animate_array(node, 'color', 3, { - blend_time: blend_color, - start_time: blend_color, - end_time: original_color - }) - bs.animate_array(node, 'highlight', 3, { - blend_time: blend_highlight, - start_time: blend_highlight, - end_time: original_highlight - }) - - delegate._color_timer = bs.Timer(end_time, bs.CallPartial(delattr, delegate, '_original_color')) - delegate._highlight_timer = bs.Timer(end_time, bs.CallPartial(delattr, delegate, '_original_highlight')) - - Blast.handlemessage = new_handlemessage \ No newline at end of file + Blast.handlemessage = new_handlemessage diff --git a/plugins/utilities/sleep_on_afk.py b/plugins/utilities/sleep_on_afk.py index 7c14354..3f3ad3b 100644 --- a/plugins/utilities/sleep_on_afk.py +++ b/plugins/utilities/sleep_on_afk.py @@ -16,28 +16,35 @@ plugman = dict( version="1.0.0", ) -INGAME_TIME = 40 # (in seconds) +INGAME_TIME = 40 # (in seconds) # Spaz Changes + + def _afk_sleep_knockout(self, value: float) -> None: - if not self.node: - return - if not self.is_alive() and hasattr(self.getplayer(self), 'sessionplayer'): - current_activity = self.getactivity() - player = self.getplayer(self).sessionplayer - #Pop timer data if the player is dead - if current_activity.customdata.get(str(player.id) +'_knockoutTimer') is not None: - current_activity.customdata.pop(str(player.id) +'_knockoutTimer') - return + if not self.node: + return + if not self.is_alive() and hasattr(self.getplayer(self), 'sessionplayer'): + current_activity = self.getactivity() + player = self.getplayer(self).sessionplayer + # Pop timer data if the player is dead + if current_activity.customdata.get(str(player.id) + '_knockoutTimer') is not None: + current_activity.customdata.pop(str(player.id) + '_knockoutTimer') + return - self.node.handlemessage('knockout', value) + self.node.handlemessage('knockout', value) -Spaz._afk_sleep_knockout = _afk_sleep_knockout # type: ignore + +Spaz._afk_sleep_knockout = _afk_sleep_knockout # type: ignore # Idle Checker + + def idle_start(activity: bs.Activity): - activity.customdata['afk_timer'] = bs.Timer(0.5, bs.CallStrict(idle_check, activity), repeat=True) - + activity.customdata['afk_timer'] = bs.Timer( + 0.5, bs.CallStrict(idle_check, activity), repeat=True) + + def idle_check(current_activity: bs.Activity): current_session = current_activity.session wait_time = INGAME_TIME if not current_activity.slow_motion else round(INGAME_TIME / 3) @@ -57,7 +64,7 @@ def idle_check(current_activity: bs.Activity): and not getattr(player.activityplayer.actor.node, 'invincible', False) ): continue - + player_actor = player.activityplayer.actor player_node = player_actor.node player_data = player.activityplayer.customdata @@ -66,22 +73,24 @@ def idle_check(current_activity: bs.Activity): if player_node.move_up_down != 0.0 or player_node.move_left_right != 0.0: player_data['last_input'] = current elif player_turbo_times: - highest_turbo_time = player_turbo_times.get(max(player_turbo_times, key=player_turbo_times.get)) + highest_turbo_time = player_turbo_times.get( + max(player_turbo_times, key=player_turbo_times.get)) if highest_turbo_time > player_data.get('last_input', current): - player_data.update({'last_input': highest_turbo_time }) + player_data.update({'last_input': highest_turbo_time}) player_data['last_input'] = player_data.get('last_input', current) - + last_input = max(0, player_data['last_input']) afk_time = int((current - last_input) / 1000) - #print(player_data) - #print(last_input) - #print(current) - #print(player.getname() + ": " + str(afk_time)) - #print(wait_time) - + # print(player_data) + # print(last_input) + # print(current) + # print(player.getname() + ": " + str(afk_time)) + # print(wait_time) + if afk_time >= wait_time: - current_activity.customdata[str(player.id) +'_knockoutTimer'] = bs.Timer(0.1, bs.WeakCallStrict(player_actor._afk_sleep_knockout, 100.0), repeat=True) + current_activity.customdata[str(player.id) + '_knockoutTimer'] = bs.Timer( + 0.1, bs.WeakCallStrict(player_actor._afk_sleep_knockout, 100.0), repeat=True) # Make the player's node not an area of interest if it was one if not getattr(player_actor, '_previous_is_area_of_interest', False): @@ -90,8 +99,8 @@ def idle_check(current_activity: bs.Activity): if player_actor._previous_is_area_of_interest: player_node.is_area_of_interest = False - elif current_activity.customdata.get(str(player.id) +'_knockoutTimer') is not None: - current_activity.customdata.pop(str(player.id) +'_knockoutTimer') + elif current_activity.customdata.get(str(player.id) + '_knockoutTimer') is not None: + current_activity.customdata.pop(str(player.id) + '_knockoutTimer') # Restore the player's node area of interest if necessary if getattr(player_actor, '_previous_is_area_of_interest', False): @@ -100,17 +109,21 @@ def idle_check(current_activity: bs.Activity): if hasattr(player_actor, "_previous_is_area_of_interest"): delattr(player_actor, "_previous_is_area_of_interest") + # Setup new activity org_on_begin = bs.Activity.on_begin + def patched_on_begin(self, *args, **kwargs): idle_start(self) return org_on_begin(self, *args, **kwargs) + bs.Activity.on_begin = patched_on_begin # ba_meta export babase.Plugin + class Plugin(babase.Plugin): pass