bombsquad-plugin-manager/plugins/utilities/ragdoll_b_gone.py

123 lines
6.6 KiB
Python
Raw Normal View History

# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
2022-11-13 19:41:14 +01:00
"""
Ragdoll-B-Gone by TheMikirog
Removes ragdolls.
Thanos snaps those pesky feet-tripping body sacks out of existence.
Literally that's it.
Heavily commented for easy modding learning!
2022-11-13 20:44:55 +01:00
No Rights Reserved
2022-11-13 19:41:14 +01:00
"""
from __future__ import annotations
from typing import TYPE_CHECKING
# Let's import everything we need and nothing more.
import babase
import bauiv1 as bui
import bascenev1 as bs
import bascenev1lib
2022-11-13 19:41:14 +01:00
import random
from bascenev1lib.actor.spaz import Spaz
from bascenev1lib.actor.spazfactory import SpazFactory
2022-11-13 19:41:14 +01:00
if TYPE_CHECKING:
pass
# ba_meta export plugin
2022-11-13 20:06:02 +00:00
class RagdollBGone(babase.Plugin):
2022-11-13 19:41:14 +01:00
# We use a decorator to add extra code to existing code, increasing mod compatibility.
# Any gameplay altering mod should master the decorator!
# Here I'm defining a new handlemessage function that'll be replaced.
def new_handlemessage(func):
# This function will return our wrapper function, which is going to take the original function's base arguments.
# Yes, in Python functions are objects that can be passed as arguments. It's bonkers.
# arg[0] is "self", args[1] is "msg" in our original handlemessage function.
2022-11-13 20:06:02 +00:00
# We're working kind of blindly here, so it's good to have the original function
2022-11-13 19:41:14 +01:00
# open in a second window for argument reference.
def wrapper(*args, **kwargs):
if isinstance(args[1], bs.DieMessage): # Replace Spaz death behavior
2022-11-13 20:06:02 +00:00
2022-11-13 19:41:14 +01:00
# Here we play the gamey death noise in Co-op.
if not args[1].immediate:
if args[0].play_big_death_sound and not args[0]._dead:
SpazFactory.get().single_player_death_sound.play()
2022-11-13 20:06:02 +00:00
2022-11-13 19:41:14 +01:00
# If our Spaz dies by falling out of the map, we want to keep the ragdoll.
# Ragdolls don't impact gameplay if Spaz dies this way, so it's fine if we leave the behavior as is.
if args[1].how == bs.DeathType.FALL:
2022-11-13 20:06:02 +00:00
# The next two properties are all built-in, so their behavior can't be edited directly without touching the C++ layer.
# We can change their values though!
# "hurt" property is basically the health bar above the player and the blinking when low on health.
# 1.0 means empty health bar and the fastest blinking in the west.
2022-11-13 19:41:14 +01:00
args[0].node.hurt = 1.0
# Make our Spaz close their eyes permanently and then make their body disintegrate.
# Again, this behavior is built in. We can only trigger it by setting "dead" to True.
args[0].node.dead = True
# After the death animation ends (which is around 2 seconds) let's remove the Spaz our of existence.
bs.timer(2.0, args[0].node.delete)
2022-11-13 19:41:14 +01:00
else:
# Here's our new behavior!
# The idea is to remove the Spaz node and make some sparks for extra flair.
# First we see if that node even exists, just in case.
if args[0].node:
# Make sure Spaz isn't dead, so we can perform the removal.
if not args[0]._dead:
# Run this next piece of code 4 times.
# "i" will start at 0 and becomes higher each iteration until it reaches 3.
for i in range(4):
# XYZ position of our sparks, we'll take the Spaz position as a base.
pos = (args[0].node.position[0],
# Let's spread the sparks across the body, assuming Spaz is standing straight.
# We're gonna change the Y axis position, which is height.
args[0].node.position[1] + i * 0.2,
args[0].node.position[2])
# This function allows us to spawn particles like sparks and bomb shrapnel.
# We're gonna use sparks here.
bs.emitfx(position=pos, # Here we place our edited position.
2022-11-13 20:06:02 +00:00
velocity=args[0].node.velocity,
# Random amount of sparks between 2 and 5
count=random.randrange(2, 5),
scale=3.0,
spread=0.2,
chunk_type='spark')
2022-11-13 19:41:14 +01:00
# Make a Spaz death noise if we're not gibbed.
if not args[0].shattered:
2022-11-13 20:06:02 +00:00
# Get our Spaz's death noises, these change depending on character skins
death_sounds = args[0].node.death_sounds
# Pick a random death noise
sound = death_sounds[random.randrange(len(death_sounds))]
# Play the sound where our Spaz is
sound.play(position=args[0].node.position)
2022-11-13 19:41:14 +01:00
# Delete our Spaz node immediately.
# Removing stuff is weird and prone to errors, so we're gonna delay it.
bs.timer(0.001, args[0].node.delete)
2022-11-13 20:06:02 +00:00
2022-11-13 19:41:14 +01:00
# Let's mark our Spaz as dead, so he can't die again.
# Notice how we're targeting the Spaz and not it's node.
# "self.node" is a visual representation of the character while "self" is his game logic.
args[0]._dead = True
2022-11-13 20:06:02 +00:00
# Set his health to zero. This value is independent from the health bar above his head.
args[0].hitpoints = 0
2022-11-13 19:41:14 +01:00
return
2022-11-13 20:06:02 +00:00
2022-11-13 19:41:14 +01:00
# Worry no longer! We're not gonna remove all the base game code!
# Here's where we bring it all back.
# If I wanted to add extra code at the end of the base game's behavior, I would just put that at the beginning of my function.
func(*args, **kwargs)
return wrapper
# Finally we """travel through the game files""" to replace the function we want with our own version.
# We transplant the old function's arguments into our version.
bascenev1lib.actor.spaz.Spaz.handlemessage = new_handlemessage(bascenev1lib.actor.spaz.Spaz.handlemessage)