-
Notifications
You must be signed in to change notification settings - Fork 32
/
ragdoll_b_gone.py
123 lines (104 loc) · 6.66 KB
/
ragdoll_b_gone.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# Porting to api 8 made easier by baport.(https://github.com/bombsquad-community/baport)
# ba_meta require api 8
"""
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!
No Rights Reserved
"""
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
import random
from bascenev1lib.actor.spaz import Spaz
from bascenev1lib.actor.spazfactory import SpazFactory
if TYPE_CHECKING:
pass
# ba_meta export plugin
class RagdollBGone(babase.Plugin):
# 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.
# We're working kind of blindly here, so it's good to have the original function
# open in a second window for argument reference.
def wrapper(*args, **kwargs):
if isinstance(args[1], bs.DieMessage): # Replace Spaz death behavior
# 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()
# 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:
# 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.
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)
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.
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')
# Make a Spaz death noise if we're not gibbed.
if not args[0].shattered:
# 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
bs.Sound.play(sound, position=args[0].node.position)
# 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)
# 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
# Set his health to zero. This value is independent from the health bar above his head.
args[0].hitpoints = 0
return
# 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)