-
Notifications
You must be signed in to change notification settings - Fork 109
Modding Guide Episode 4: Custom Powerups
In this tutorial, we're gonna learn how to make a custom powerup that makes spaz invincible for 1/4th of the default powerup time!
Now, remember Episode 2? where we learned how to create a plugin for modding purposes? Well, let's throw that all out of the window because making powerups using plugins is currently a VERY difficult task, so instead, we're going to edit the game files (sorry android folks 🙁). In the future, hopefully this sort of thing will become easier with plugins.
Let's open our Python scripts folder, this should be located under ba_data/python
in your BombSquad directory, this is where all the magic happens
Now we have 3 things to do:
- Make PowerupBox and PowerupBoxFactory aware of this new powerup type
- Put this new powerup in the default powerup distribution roster
- Make Spaz react to this new powerup
So let's begin, shall we?
PowerupBox and PowerupBoxFactory are located under bascenev1lib/actor/powerupbox.py
, first find this line in the PowerupBoxFactory class
tex_curse: bs.Texture
"""Curse powerup bs.Texture."""
and add this line under it
tex_inv: bs.Texture
that is going to be our texture, now find this line in the factory's init
self.tex_curse = bs.gettexture('powerupCurse')
add a line under it and use it to describe the texture of it using bs.gettexture
, we're going to use the 'levelIcon' in this tutorial
self.tex_inv = bs.gettexture('levelIcon')
now we tell this to the PowerupBox class, find this line in it's init
elif poweruptype == 'curse':
tex = factory.tex_curse
right under it, add
elif poweruptype == 'inv':
tex = factory.tex_inv
WARNING: Make sure the new line gets added before the else
statement
elif poweruptype == 'curse':
tex = factory.tex_curse
elif poweruptype == 'inv':
tex = factory.tex_inv
else:
raise ValueError('invalid poweruptype: ' + str(poweruptype))
We're done with this part, now let's add this powerup box to the default powerup distribution roster.
Head on to bascenev1/_powerup.py
, this file contains some message classes regarding powerup functionality, and the default powerup distribution rosters which is exactly what we need; find this line
def get_default_powerup_distribution() -> Sequence[tuple[str, int]]:
"""Standard set of powerups."""
return (
('triple_bombs', 3),
('ice_bombs', 3),
('punch', 3),
('impact_bombs', 3),
('land_mines', 2),
('sticky_bombs', 3),
('shield', 2),
('health', 1),
('curse', 1),
)
add ('inv', 1),
under the curse, it should now look like this now
def get_default_powerup_distribution() -> Sequence[tuple[str, int]]:
"""Standard set of powerups."""
return (
('triple_bombs', 3),
('ice_bombs', 3),
('punch', 3),
('impact_bombs', 3),
('land_mines', 2),
('sticky_bombs', 3),
('shield', 2),
('health', 1),
('curse', 1),
('inv', 1),
)
this adds the "inv" powerup in the default powerup roster, "1" here is the probability of the powerup dropping. The greater the number is, the higher the chance of the powerup dropping is; try different numbers to see how the gameplay is affected.
Tip: Always set a high probability number for your custom powerup or remove all other powerups from the roster when testing your powerup to reduce the time spent testing
That's done now, so let's head over to bascenev1lib/actor/spaz.py
and add some actual function to our powerup.
First let's add a timer variable in Spaz's init, find
self._curse_timer: bs.Timer | None = None
now add this under it
self._inv_wear_off_timer: bs.Timer | None = None
self._inv_wear_off_flash_timer: bs.Timer | None = None
now let's handle "wearing off" the powerup
def _inv_wear_off_flash(self) -> None:
if self.node:
self.node.billboard_texture = PowerupBoxFactory.get().tex_inv
self.node.billboard_opacity = 1.0
self.node.billboard_cross_out = True
def _inv_wear_off(self) -> None:
if self.node:
self.node.invincible = False
PowerupBoxFactory.get().powerdown_sound.play(
position=self.node.position
)
self.node.billboard_opacity = 0.0
_inv_wear_off_flash
"flashes" the powerup texture before it runs out, and _inv_wear_off
handles the actual functionality, now let's actually give the powerup to our poor spaz, inside Spaz's handlemessage, under
elif isinstance(msg, bs.PowerupMessage):
find
elif msg.poweruptype == 'health':
if self._cursed:
self._cursed = False
# Remove cursed material.
factory = SpazFactory.get()
for attr in ['materials', 'roller_materials']:
materials = getattr(self.node, attr)
if factory.curse_material in materials:
setattr(
self.node,
attr,
tuple(
m
for m in materials
if m != factory.curse_material
),
)
self.node.curse_death_time = 0
self.hitpoints = self.hitpoints_max
self._flash_billboard(PowerupBoxFactory.get().tex_health)
self.node.hurt = 0
self._last_hit_time = None
self._num_times_hit = 0
right under that, write this
elif msg.poweruptype == 'inv':
tex = PowerupBoxFactory.get().tex_inv
self._flash_billboard(tex)
self.node.invincible = True
self._inv_wear_off_flash_timer = (bs.Timer(
((POWERUP_WEAR_OFF_TIME/4000) - 2),
bs.WeakCall(self._inv_wear_off_flash))
self._inv_wear_off_timer = (bs.Timer(
POWERUP_WEAR_OFF_TIME/4000,
bs.WeakCall(self._inv_wear_off))
in this function, we flash the powerup texture, then we make spaz invincible, and after that, we set a bs.Timer
to reverse the actions of the powerup after 1/4th of the default powerup time, and we call the flash timer to flash the powerup running out texture 2 seconds beforehand.
Well, that should do it (hopefully), I hope you learned something new, also, if you end up making something cool with this and you'd like to share it with everyone, you can head over to The Official BombSquad/Ballistica Discord Server and share it with all of us! Happy modding!
ballistica.net • discord • blog • support