[ci] apply-plugin-metadata-and-formatting

This commit is contained in:
DinoWattz 2026-02-23 08:18:30 +00:00 committed by github-actions[bot]
parent ebbd198628
commit e67d891716
5 changed files with 627 additions and 519 deletions

View file

@ -2426,6 +2426,62 @@
"md5sum": "d8002dfc93be74d71f8e6e4aacfe6fd1" "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
}
} }
} }
} }

View file

@ -26,356 +26,363 @@ plugman = dict(
) )
# Camera shake only on player impact # Camera shake only on player impact
def new__init__( def new__init__(
self, self,
*, *,
position: Sequence[float] = (0.0, 1.0, 0.0), position: Sequence[float] = (0.0, 1.0, 0.0),
velocity: Sequence[float] = (0.0, 0.0, 0.0), velocity: Sequence[float] = (0.0, 0.0, 0.0),
blast_radius: float = 2.0, blast_radius: float = 2.0,
blast_type: str = 'normal', blast_type: str = 'normal',
source_player: bs.Player | None = None, source_player: bs.Player | None = None,
hit_type: str = 'explosion', hit_type: str = 'explosion',
hit_subtype: str = 'normal', hit_subtype: str = 'normal',
): ):
"""Instantiate with given values.""" """Instantiate with given values."""
# bah; get off my lawn! # bah; get off my lawn!
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
super(type(self), self).__init__() super(type(self), self).__init__()
shared = SharedObjects.get() shared = SharedObjects.get()
factory = BombFactory.get() factory = BombFactory.get()
self.blast_type = blast_type self.blast_type = blast_type
self._source_player = source_player self._source_player = source_player
self.hit_type = hit_type self.hit_type = hit_type
self.hit_subtype = hit_subtype self.hit_subtype = hit_subtype
self.radius = blast_radius self.radius = blast_radius
# Set our position a bit lower so we throw more things upward. # Set our position a bit lower so we throw more things upward.
rmats = (factory.blast_material, shared.attack_material) rmats = (factory.blast_material, shared.attack_material)
self.node = bs.newnode( self.node = bs.newnode(
'region', 'region',
delegate=self, delegate=self,
attrs={ attrs={
'position': (position[0], position[1] - 0.1, position[2]), 'position': (position[0], position[1] - 0.1, position[2]),
'scale': (self.radius, self.radius, self.radius), 'scale': (self.radius, self.radius, self.radius),
'type': 'sphere', 'type': 'sphere',
'materials': rmats, 'materials': rmats,
}, },
) )
bs.timer(0.05, self.node.delete) bs.timer(0.05, self.node.delete)
# Throw in an explosion and flash. # Throw in an explosion and flash.
evel = (velocity[0], max(-1.0, velocity[1]), velocity[2]) evel = (velocity[0], max(-1.0, velocity[1]), velocity[2])
explosion = bs.newnode( explosion = bs.newnode(
'explosion', 'explosion',
attrs={ attrs={
'position': position, 'position': position,
'velocity': evel, 'velocity': evel,
'radius': self.radius, 'radius': self.radius,
'big': (self.blast_type == 'tnt'), 'big': (self.blast_type == 'tnt'),
}, },
) )
if self.blast_type == 'ice': if self.blast_type == 'ice':
explosion.color = (0, 0.05, 0.4) explosion.color = (0, 0.05, 0.4)
bs.timer(1.0, explosion.delete) bs.timer(1.0, explosion.delete)
if self.blast_type != 'ice': 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',
)
bs.emitfx( bs.emitfx(
position=position, position=position,
velocity=velocity, velocity=velocity,
count=int(4.0 + random.random() * 4), count=int(1.0 + random.random() * 4),
emit_type='tendrils', emit_type='tendrils',
tendril_type='ice' if self.blast_type == 'ice' else 'smoke', tendril_type='thin_smoke',
)
bs.emitfx(
position=position,
emit_type='distortion',
spread=1.0 if self.blast_type == 'tnt' else 2.0,
) )
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. # And emit some shrapnel.
if self.blast_type == 'ice': if self.blast_type == 'ice':
def emit() -> None: def emit() -> None:
bs.emitfx( bs.emitfx(
position=position, position=position,
velocity=velocity, velocity=velocity,
count=30, count=30,
spread=2.0, spread=2.0,
scale=0.4, scale=0.4,
chunk_type='ice', chunk_type='ice',
emit_type='stickers', emit_type='stickers',
) )
# It looks better if we delay a bit. # It looks better if we delay a bit.
bs.timer(0.05, emit) 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( bs.emitfx(
position=position, position=position,
velocity=velocity, velocity=velocity,
count=int(4.0 + random.random() * 8), count=int(4.0 + random.random() * 8),
spread=0.7, chunk_type='rock',
chunk_type='slime',
) )
bs.emitfx( bs.emitfx(
position=position, position=position,
velocity=velocity, velocity=velocity,
count=int(4.0 + random.random() * 8), count=int(4.0 + random.random() * 8),
scale=0.5, scale=0.5,
spread=0.7, chunk_type='rock',
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',
) )
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. # TNT throws splintery chunks.
bs.timer(0.05, emit) if self.blast_type == 'tnt':
elif self.blast_type == 'impact': def emit_splinters() -> None:
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( bs.emitfx(
position=position, position=position,
velocity=velocity, velocity=velocity,
count=int(4.0 + random.random() * 8), count=int(20.0 + random.random() * 25),
chunk_type='rock', 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( bs.emitfx(
position=position, position=position,
velocity=velocity, velocity=velocity,
count=int(4.0 + random.random() * 8), count=int(10.0 + random.random() * 20),
scale=0.5, scale=0.8,
chunk_type='rock', 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. bs.timer(0.02, emit_extra_sparks)
if self.blast_type == 'tnt':
def emit_splinters() -> None: # It looks better if we delay a bit.
bs.emitfx( bs.timer(0.05, emit)
position=position,
velocity=velocity,
count=int(20.0 + random.random() * 25),
scale=0.8,
spread=1.0,
chunk_type='splinter',
)
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. scl = random.uniform(0.6, 0.9)
if self.blast_type == 'tnt' or random.random() < 0.1: 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: iscale = 1.6
bs.emitfx( bs.animate(
position=position, light,
velocity=velocity, 'intensity',
count=int(10.0 + random.random() * 20), {
scale=0.8, 0: 2.0 * iscale,
spread=1.5, scl * 0.02: 0.1 * iscale,
chunk_type='spark', 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.animate(scorch, 'presence', {3.000: 1, 13.000: 0})
bs.timer(0.05, emit) bs.timer(13.0, scorch.delete)
lcolor = (0.6, 0.6, 1.0) if self.blast_type == 'ice' else (1, 0.3, 0.1) if self.blast_type == 'ice':
light = bs.newnode( factory.hiss_sound.play(position=light.position)
'light',
attrs={
'position': position,
'volume_intensity_scale': 10.0,
'color': lcolor,
},
)
scl = random.uniform(0.6, 0.9) lpos = light.position
scorch_radius = light_radius = self.radius factory.random_explode_sound().play(position=lpos)
if self.blast_type == 'tnt': factory.debris_fall_sound.play(position=lpos)
light_radius *= 1.4
scorch_radius *= 1.15
scl *= 3.0
iscale = 1.6 # ↓ We don't need any of this, we're fully trained professionals.
bs.animate( # bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0)
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)
# Make a scorch that fades over time. # TNT is more epic.
scorch = bs.newnode( if self.blast_type == 'tnt':
'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
factory.random_explode_sound().play(position=lpos) 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. def _extra_boom() -> None:
#bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0)
# TNT is more epic.
if self.blast_type == 'tnt':
factory.random_explode_sound().play(position=lpos) factory.random_explode_sound().play(position=lpos)
def _extra_boom() -> None: bs.timer(0.25, _extra_boom)
factory.random_explode_sound().play(position=lpos)
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: bs.timer(0.4, _extra_debris_sound)
factory.debris_fall_sound.play(position=lpos)
factory.wood_debris_fall_sound.play(position=lpos)
bs.timer(0.4, _extra_debris_sound)
bomb.Blast.__init__ = new__init__ bomb.Blast.__init__ = new__init__
org_handlemessage = bomb.Blast.handlemessage org_handlemessage = bomb.Blast.handlemessage
def new_handlemessage(self, msg: Any, *args, **kwargs) -> Any: def new_handlemessage(self, msg: Any, *args, **kwargs) -> Any:
org_handlemessage(self, msg, *args, **kwargs) org_handlemessage(self, msg, *args, **kwargs)
if isinstance(msg, ExplodeHitMessage): if isinstance(msg, ExplodeHitMessage):
node = bs.getcollision().opposingnode node = bs.getcollision().opposingnode
delegate = node.getdelegate(PlayerSpaz) delegate = node.getdelegate(PlayerSpaz)
has_used_camerashake = getattr(self, 'has_used_camerashake', False) has_used_camerashake = getattr(self, 'has_used_camerashake', False)
if not has_used_camerashake and (delegate and not delegate.shield) and not node.invincible: if not has_used_camerashake and (delegate and not delegate.shield) and not node.invincible:
self.has_used_camerashake = True self.has_used_camerashake = True
bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0) bs.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0)
bomb.Blast.handlemessage = new_handlemessage bomb.Blast.handlemessage = new_handlemessage
# ba_meta export babase.Plugin # ba_meta export babase.Plugin
class Plugin(babase.Plugin): class Plugin(babase.Plugin):
pass pass

View file

@ -23,8 +23,9 @@ plugman = dict(
version="1.0.0", version="1.0.0",
) )
PRINT_CHAT_MESSAGES = True # Prints chat messages to the console as "[Character's Name] Player 1/Player 2: Hello World!" # 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 PRINT_CHAT_MESSAGES = True
BUBBLE_DURATION: float = 4.7 # 4.7 matches the default onscreen message duration
SPEAK_VOLUME = 1.5 SPEAK_VOLUME = 1.5
SHOUT_VOLUME = 3.0 SHOUT_VOLUME = 3.0
@ -35,13 +36,13 @@ CELEBRATE_MESSAGE = [(bs.CelebrateMessage, {"duration": 1.0})]
EASTER_EGGS = [ EASTER_EGGS = [
{ {
"triggers": { "triggers": {
"hi", "hello", "hey", "hiya", "yo", "sup", "heya", "howdy", "hi", "hello", "hey", "hiya", "yo", "sup", "heya", "howdy",
"greetings", "salutations", "ahoy", "ahoyhoy", "bonjour", "greetings", "salutations", "ahoy", "ahoyhoy", "bonjour",
"hola", "ciao", "ola", "oi", "eae", "salve", "hola", "ciao", "ola", "oi", "eae", "salve",
"bye", "goodbye", "see ya", "see you", "cya", "bye", "goodbye", "see ya", "see you", "cya",
"adios", "chau", "chao", "tchau", "adios", "chau", "chao", "tchau",
"adeus", "ate mais", "falou", "flw" "adeus", "ate mais", "falou", "flw"
}, },
"node_messages": [("celebrate_r", 1000)], "node_messages": [("celebrate_r", 1000)],
}, },
{ {
@ -229,6 +230,8 @@ EASTER_EGGS = [
] ]
# Chat bubble functionality # Chat bubble functionality
def create_bubble(player: bs.SessionPlayer, text: str, name: str): def create_bubble(player: bs.SessionPlayer, text: str, name: str):
msg = normalize_message(text) msg = normalize_message(text)
is_caps = len(text) > 1 and text.isupper() 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(): def _fallback_logic():
nonlocal handled, activityplayer nonlocal handled, activityplayer
color = (1, 1, 0.4) 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: if activityplayer:
normal_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=1.15) ) normal_highlight = bs.normalized_color(
saturated_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=0.75) ) 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 color = saturated_highlight if is_caps else normal_highlight
image_icon = activityplayer.get_icon() image_icon = activityplayer.get_icon()
@ -312,23 +318,25 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str):
}) })
char_node.connectattr('torso_position', mnode, 'input2') char_node.connectattr('torso_position', mnode, 'input2')
normal_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=1.15) ) normal_highlight = bs.normalized_color(
saturated_highlight = bs.normalized_color( bs.safecolor(player.highlight, target_intensity=0.75) ) 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 bubble_color = saturated_highlight if is_caps else normal_highlight
textnode = bs.newnode('text', textnode = bs.newnode('text',
owner=char_node, owner=char_node,
attrs={ attrs={
'text': text, 'text': text,
'color': bubble_color, 'color': bubble_color,
'shadow': 0.5, 'shadow': 0.5,
'flatness': 0.5, 'flatness': 0.5,
'scale': scale, 'scale': scale,
'h_align': 'center', 'h_align': 'center',
'v_align': 'bottom', 'v_align': 'bottom',
'in_world': True, 'in_world': True,
'opacity': 1.0, 'opacity': 1.0,
}) })
mnode.connectattr('output', textnode, 'position') mnode.connectattr('output', textnode, 'position')
@ -366,7 +374,8 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str):
sound = random.choice(pickup_sounds) sound = random.choice(pickup_sounds)
if sound: 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 # Animation logic
if egg.get('messages') and actor.is_alive() and hasattr(actor, 'handlemessage'): if egg.get('messages') and actor.is_alive() and hasattr(actor, 'handlemessage'):
@ -423,6 +432,7 @@ def create_bubble(player: bs.SessionPlayer, text: str, name: str):
_fallback_logic() _fallback_logic()
return return
def update_bubble_offsets(bubbles): def update_bubble_offsets(bubbles):
for i, (tnode, mnode) in enumerate(reversed(bubbles)): for i, (tnode, mnode) in enumerate(reversed(bubbles)):
y_offset = 1.2 + 0.25 * i y_offset = 1.2 + 0.25 * i
@ -430,6 +440,8 @@ def update_bubble_offsets(bubbles):
mnode.input1 = (0, y_offset, 0) mnode.input1 = (0, y_offset, 0)
# Tools # Tools
def normalize_message(text): def normalize_message(text):
msg = text.strip().lower() msg = text.strip().lower()
msg = msg.translate(str.maketrans('', '', string.punctuation)) msg = msg.translate(str.maketrans('', '', string.punctuation))
@ -441,6 +453,8 @@ def normalize_message(text):
return msg return msg
# ba_meta export babase.Plugin # ba_meta export babase.Plugin
class Plugin(babase.Plugin): class Plugin(babase.Plugin):
def on_app_running(self) -> None: def on_app_running(self) -> None:
# Hook into chat message filtering # Hook into chat message filtering
@ -451,6 +465,7 @@ class Plugin(babase.Plugin):
import bascenev1._hooks as _hooks import bascenev1._hooks as _hooks
_org_filter_chat_message = _hooks.filter_chat_message _org_filter_chat_message = _hooks.filter_chat_message
def _chat_bubbles_hook(msg: str, client_id: int, *args, **kwargs) -> str | None: def _chat_bubbles_hook(msg: str, client_id: int, *args, **kwargs) -> str | None:
org_msg = _org_filter_chat_message(msg, client_id, *args, **kwargs) org_msg = _org_filter_chat_message(msg, client_id, *args, **kwargs)
@ -478,42 +493,43 @@ class Plugin(babase.Plugin):
player_list.append(player) player_list.append(player)
continue continue
if player_list: if player_list:
# Get combined name for multiple players on same client # Get combined name for multiple players on same client
name = '' name = ''
created_bubble = False created_bubble = False
for player in player_list: for player in player_list:
if name == '': if name == '':
name = player.getname() name = player.getname()
else: else:
name = name + '/' + player.getname() name = name + '/' + player.getname()
# Create chat bubble # Create chat bubble
for player in player_list: for player in player_list:
activityplayer: bs.Player = player.activityplayer activityplayer: bs.Player = player.activityplayer
# We have a session player but not an activity player # We have a session player but not an activity player
if not activityplayer: if not activityplayer:
if not dead_bubble: 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:
create_bubble(player, org_msg, name) create_bubble(player, org_msg, name)
created_bubble = dead_bubble = True created_bubble = dead_bubble = True
character = activityplayer.character if character is None else character continue
# elif we have an alive activity player # We have a dead activity player
elif activityplayer.is_alive(): if not activityplayer.is_alive() and not dead_bubble:
create_bubble(player, org_msg, name) create_bubble(player, org_msg, name)
created_bubble = True created_bubble = dead_bubble = True
character = activityplayer.character if character is None else character 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: if created_bubble:
matched = True matched = True
if PRINT_CHAT_MESSAGES: if PRINT_CHAT_MESSAGES:
print(f"[{character}] {name}: {org_msg}") print(f"[{character}] {name}: {org_msg}")
else: else:
bs.logging.warning("Failed to create a chat bubble, using non-session player method now.") 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) # Non-session players (in lobby or just spectating, this only works for servers)
if not matched and not dead_bubble: if not matched and not dead_bubble:
for client in roster: for client in roster:
@ -537,40 +553,41 @@ class Plugin(babase.Plugin):
class Focus(bs.Actor): class Focus(bs.Actor):
def __init__( def __init__(
self, self,
*, *,
owner: bs.Node | None = None, owner: bs.Node | None = None,
): ):
super().__init__() super().__init__()
self._focusnode_material = bs.Material() self._focusnode_material = bs.Material()
self._focusnode_material.add_actions( self._focusnode_material.add_actions(
actions=('modify_node_collision', 'collide', False), actions=('modify_node_collision', 'collide', False),
) )
if getattr(owner, 'owner', None): if getattr(owner, 'owner', None):
assert owner is not 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: else:
area_of_interest = False if getattr(owner, 'is_area_of_interest', False) else True 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 # 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. # actually it might work now that we don't spawn bubbles for dead players.
self.node = bs.newnode( self.node = bs.newnode(
'prop', 'prop',
delegate=self, delegate=self,
owner=owner, owner=owner,
attrs={ attrs={
'body': 'sphere', 'body': 'sphere',
'body_scale': 0.0, 'body_scale': 0.0,
'shadow_size': 0.0, 'shadow_size': 0.0,
'gravity_scale': 0.0, 'gravity_scale': 0.0,
'is_area_of_interest': area_of_interest, 'is_area_of_interest': area_of_interest,
'materials': (self._focusnode_material,), 'materials': (self._focusnode_material,),
}, },
) )
@override @override
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:

View file

@ -25,92 +25,95 @@ plugman = dict(
# Transparent Background (kinda hacky, works best on pc with high/higher quality 'visuals' setting) # Transparent Background (kinda hacky, works best on pc with high/higher quality 'visuals' setting)
BACKGROUND_OPACITY = 0.5 BACKGROUND_OPACITY = 0.5
def __modified_background_init( def __modified_background_init(
self, self,
fade_time: float = 0.5, fade_time: float = 0.5,
start_faded: bool = False, start_faded: bool = False,
show_logo: bool = False, show_logo: bool = False,
): ):
super(type(self), self).__init__() super(type(self), self).__init__()
self._dying = False self._dying = False
self.fade_time = fade_time self.fade_time = fade_time
# We're special in that we create our node in the session # We're special in that we create our node in the session
# scene instead of the activity scene. # scene instead of the activity scene.
# This way we can overlap multiple activities for fades # This way we can overlap multiple activities for fades
# and whatnot. # and whatnot.
session = bs.getsession() session = bs.getsession()
self._session = weakref.ref(session) self._session = weakref.ref(session)
with session.context: with session.context:
self.node = bs.newnode( 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', 'image',
delegate=self, owner=self.node,
attrs={ attrs={
'fill_screen': True, 'texture': logo_texture,
'texture': bs.gettexture('bg'), 'mesh_opaque': logo_mesh,
'tilt_translate': -0.3, 'mesh_transparent': logo_mesh_transparent,
'has_alpha_channel': False, 'scale': (0.7, 0.7),
'color': (1, 1, 1), 'vr_depth': -250,
'opacity': BACKGROUND_OPACITY, 'color': (0.15, 0.15, 0.15),
'position': (0, 0),
'tilt_translate': -0.05,
'absolute_scale': False,
}, },
) )
if not start_faded: self.node.connectattr('opacity', self.logo, 'opacity')
bs.animate( # add jitter/pulse for a stop-motion-y look unless we're in VR
self.node, # in which case stillness is better
'opacity', if not bs.app.env.vr:
{0.0: 0.0, self.fade_time: BACKGROUND_OPACITY}, self.cmb = bs.newnode(
loop=False, 'combine', owner=self.node, attrs={'size': 2}
) )
if show_logo: for attr in ['input0', 'input1']:
logo_texture = bs.gettexture('logo') bs.animate(
logo_mesh = bs.getmesh('logo') self.cmb,
logo_mesh_transparent = bs.getmesh('logoTransparent') attr,
self.logo = bs.newnode( {0.0: 0.693, 0.05: 0.7, 0.5: 0.693},
'image', loop=True,
owner=self.node, )
attrs={ self.cmb.connectattr('output', self.logo, 'scale')
'texture': logo_texture, cmb = bs.newnode(
'mesh_opaque': logo_mesh, 'combine', owner=self.node, attrs={'size': 2}
'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,
},
) )
self.node.connectattr('opacity', self.logo, 'opacity') cmb.connectattr('output', self.logo, 'position')
# add jitter/pulse for a stop-motion-y look unless we're in VR # Gen some random keys for that stop-motion-y look.
# in which case stillness is better keys = {}
if not bs.app.env.vr: timeval = 0.0
self.cmb = bs.newnode( for _i in range(10):
'combine', owner=self.node, attrs={'size': 2} keys[timeval] = (random.random() - 0.5) * 0.0015
) timeval += random.random() * 0.1
for attr in ['input0', 'input1']: bs.animate(cmb, 'input0', keys, loop=True)
bs.animate( keys = {}
self.cmb, timeval = 0.0
attr, for _i in range(10):
{0.0: 0.693, 0.05: 0.7, 0.5: 0.693}, keys[timeval] = (random.random() - 0.5) * 0.0015 + 0.05
loop=True, timeval += random.random() * 0.1
) bs.animate(cmb, 'input1', keys, 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)
def _modified_background_die(self, immediate: bool = False) -> None: def _modified_background_die(self, immediate: bool = False) -> None:
session = self._session() 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) bs.timer(self.fade_time + 0.1, self.node.delete)
Background.__init__ = __modified_background_init Background.__init__ = __modified_background_init
Background._die = _modified_background_die Background._die = _modified_background_die
# Tools # Tools
def blend_toward( def blend_toward(
rgb: tuple[float, float, float], rgb: tuple[float, float, float],
target: 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) return tuple(clamp01(linear_to_srgb(c)) for c in blended_lin)
# ba_meta export babase.Plugin # ba_meta export babase.Plugin
class Plugin(babase.Plugin): class Plugin(babase.Plugin):
def on_app_running(self) -> None: def on_app_running(self) -> None:
from bascenev1lib.actor.bomb import Blast, ExplodeHitMessage from bascenev1lib.actor.bomb import Blast, ExplodeHitMessage
@ -190,7 +198,8 @@ class Plugin(babase.Plugin):
def equip_shields(self, decay: bool = False) -> None: def equip_shields(self, decay: bool = False) -> None:
org_equip_shields(self, decay) org_equip_shields(self, decay)
if self.shield is not None: 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) self.shield.color = bs.normalized_color(safe_highlight)
Spaz.equip_shields = equip_shields Spaz.equip_shields = equip_shields
@ -208,7 +217,8 @@ class Plugin(babase.Plugin):
# Pause # Pause
gnode.paused = True 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 delay = 0.06
@ -217,12 +227,14 @@ class Plugin(babase.Plugin):
# Slow Motion # Slow Motion
gnode.slow_motion = True 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 delay += 0.14
bs.displaytimer(delay, bs.CallPartial(setattr, self, '_slow_motion_timer', None)) 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 Blast.__init__ = new_init
@ -233,58 +245,61 @@ class Plugin(babase.Plugin):
org_handlemessage(self, msg, *args, **kwargs) org_handlemessage(self, msg, *args, **kwargs)
if isinstance(msg, ExplodeHitMessage): if isinstance(msg, ExplodeHitMessage):
node = bs.getcollision().opposingnode node = bs.getcollision().opposingnode
delegate = node.getdelegate(PlayerSpaz) or node.getdelegate(SpazBot) delegate = node.getdelegate(PlayerSpaz) or node.getdelegate(SpazBot)
if (delegate and not delegate.shield) and not node.invincible: if (delegate and not delegate.shield) and not node.invincible:
if not hasattr(delegate, '_original_color'): if not hasattr(delegate, '_original_color'):
delegate._original_color = node.color delegate._original_color = node.color
delegate._original_highlight = node.highlight delegate._original_highlight = node.highlight
original_color = delegate._original_color original_color = delegate._original_color
original_highlight = delegate._original_highlight 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 blend_time = 1.0
start_time = 6.0 start_time = 1.2
end_time = 7.0 end_time = 3.0
else:
explosion_color = (0.07, 0.03, 0.0)
bs.emitfx( blend_color = blend_toward(node.color, explosion_color,
position=node.position, explosion_intensity, True)
velocity=node.velocity, blend_highlight = blend_toward(
count=int(4.0 + random.random() * 4), node.highlight, explosion_color, explosion_intensity-0.03, True)
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 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
})
if self.blast_type == 'ice': delegate._color_timer = bs.Timer(
explosion_intensity = 0.91 end_time, bs.CallPartial(delattr, delegate, '_original_color'))
explosion_color = (0.07, 0.6, 1.5) delegate._highlight_timer = bs.Timer(
elif self.blast_type == 'sticky': end_time, bs.CallPartial(delattr, delegate, '_original_highlight'))
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)
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 Blast.handlemessage = new_handlemessage

View file

@ -16,27 +16,34 @@ plugman = dict(
version="1.0.0", version="1.0.0",
) )
INGAME_TIME = 40 # (in seconds) INGAME_TIME = 40 # (in seconds)
# Spaz Changes # Spaz Changes
def _afk_sleep_knockout(self, value: float) -> None: def _afk_sleep_knockout(self, value: float) -> None:
if not self.node: if not self.node:
return return
if not self.is_alive() and hasattr(self.getplayer(self), 'sessionplayer'): if not self.is_alive() and hasattr(self.getplayer(self), 'sessionplayer'):
current_activity = self.getactivity() current_activity = self.getactivity()
player = self.getplayer(self).sessionplayer player = self.getplayer(self).sessionplayer
#Pop timer data if the player is dead # Pop timer data if the player is dead
if current_activity.customdata.get(str(player.id) +'_knockoutTimer') is not None: if current_activity.customdata.get(str(player.id) + '_knockoutTimer') is not None:
current_activity.customdata.pop(str(player.id) +'_knockoutTimer') current_activity.customdata.pop(str(player.id) + '_knockoutTimer')
return 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 # Idle Checker
def idle_start(activity: bs.Activity): 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): def idle_check(current_activity: bs.Activity):
current_session = current_activity.session current_session = current_activity.session
@ -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: if player_node.move_up_down != 0.0 or player_node.move_left_right != 0.0:
player_data['last_input'] = current player_data['last_input'] = current
elif player_turbo_times: 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): 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) player_data['last_input'] = player_data.get('last_input', current)
last_input = max(0, player_data['last_input']) last_input = max(0, player_data['last_input'])
afk_time = int((current - last_input) / 1000) afk_time = int((current - last_input) / 1000)
#print(player_data) # print(player_data)
#print(last_input) # print(last_input)
#print(current) # print(current)
#print(player.getname() + ": " + str(afk_time)) # print(player.getname() + ": " + str(afk_time))
#print(wait_time) # print(wait_time)
if afk_time >= 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 # 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): 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: if player_actor._previous_is_area_of_interest:
player_node.is_area_of_interest = False player_node.is_area_of_interest = False
elif current_activity.customdata.get(str(player.id) +'_knockoutTimer') is not None: elif current_activity.customdata.get(str(player.id) + '_knockoutTimer') is not None:
current_activity.customdata.pop(str(player.id) +'_knockoutTimer') current_activity.customdata.pop(str(player.id) + '_knockoutTimer')
# Restore the player's node area of interest if necessary # Restore the player's node area of interest if necessary
if getattr(player_actor, '_previous_is_area_of_interest', False): 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"): if hasattr(player_actor, "_previous_is_area_of_interest"):
delattr(player_actor, "_previous_is_area_of_interest") delattr(player_actor, "_previous_is_area_of_interest")
# Setup new activity # Setup new activity
org_on_begin = bs.Activity.on_begin org_on_begin = bs.Activity.on_begin
def patched_on_begin(self, *args, **kwargs): def patched_on_begin(self, *args, **kwargs):
idle_start(self) idle_start(self)
return org_on_begin(self, *args, **kwargs) return org_on_begin(self, *args, **kwargs)
bs.Activity.on_begin = patched_on_begin bs.Activity.on_begin = patched_on_begin
# ba_meta export babase.Plugin # ba_meta export babase.Plugin
class Plugin(babase.Plugin): class Plugin(babase.Plugin):
pass pass