2024-01-16 15:06:45 +03:00
|
|
|
# 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.
|
2024-01-16 15:06:45 +03:00
|
|
|
import babase
|
|
|
|
|
import bauiv1 as bui
|
|
|
|
|
import bascenev1 as bs
|
|
|
|
|
import bascenev1lib
|
2022-11-13 19:41:14 +01:00
|
|
|
import random
|
2024-01-16 15:06:45 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2024-01-16 15:06:45 +03: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):
|
2024-01-16 15:06:45 +03:00
|
|
|
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:
|
2024-01-16 15:06:45 +03:00
|
|
|
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.
|
2024-01-16 15:06:45 +03:00
|
|
|
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.
|
2024-01-16 15:06:45 +03:00
|
|
|
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.
|
2024-01-16 15:06:45 +03:00
|
|
|
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
|
2024-01-16 15:06:45 +03:00
|
|
|
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.
|
2024-01-16 15:06:45 +03:00
|
|
|
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.
|
2024-01-16 15:06:45 +03:00
|
|
|
bascenev1lib.actor.spaz.Spaz.handlemessage = new_handlemessage(bascenev1lib.actor.spaz.Spaz.handlemessage)
|