diff --git a/.efrocachemap b/.efrocachemap index e1ace103..e70c719a 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4132,16 +4132,16 @@ "assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c", "assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb", "assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe", - "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/95/a8/318c6db7a9c94989c601f9388211", - "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6c/53/dda3c28a824749358279d01d85a6", - "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/cb/5e/04efb608c6a0d3fc80baee7f2c0e", - "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/91/74/c92748de53d860aa936f969c4699", - "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b9/a0/202236991664bc72a33affee2911", - "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b3/1a/7f626564f3659f4cbd00d62cbd5a", - "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/94/b8/4f2c26af58e4386d58f2de2b8f3f", - "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/8e/f3/40da5e70872c27cb1716a0e7bc10", - "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/a0/a9/9ca7e5a2a62c7198f6dccf29a1e2", - "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/54/e7/fd4f9d4af81fed229a1fd2d486f1", - "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/54/5f/90a09221a6cb5ca24cf2a6b0f4e7", - "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/c2/14/82ced0d7340340cd09a73987c82f" + "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/29/c569224bc159225daed5cabdd517", + "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b5/52/a015232b381b5a361e26cc4e33d6", + "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b5/31/c229f5293e5ec5b3b8feb9308216", + "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3e/8c/2b05b2168897862e0eefc0d5ddaa", + "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a6/e5/923be95c40b9e7432f941bb98f79", + "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f5/3e/f929b7330662fc64f91e9613d6b3", + "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e9/3a/25571131b13d74f19150e8fdf786", + "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4f/b3/9627d8ee06297e66f7238fbc4838", + "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/fe/45/5646002baebd720592914c7f1c5b", + "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f3/ee/10a2c2eaf9783c9abd81141ee5e8", + "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8e/7b/e121ed5e35abf9cce71415fdecac", + "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/16/d7/b53476ad786d0b1636fcf1906578" } \ No newline at end of file diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index 16a51b7c..1586fccd 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -1642,6 +1642,7 @@ setbuild setlanguage setmusic + setsticky settingname setversion sgrn diff --git a/README.md b/README.md index bf2b990d..162a1e9d 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ The Ballistica project is the foundation for the next generation of [BombSquad]( ### Frequently Asked Questions * **Q: What's with this new name? Is BombSquad getting renamed?** -* A: No, BombSquad is still BombSquad. 'Ballistica' is simply the new name for the engine/app-framework. This way it can also be used for other game/app projects without causing confusion (though that is mostly theoretical at this point). As a modder, the biggest changes you will notice is 'ba' prefixes in the API instead of 'bs' and naming that follows Python PEP8 standards (underscores and lowercase instead of camel-case). So `bs.playSound(mySound)` in the old system might look like `ba.playsound(mysound)` in the new. You may also see the word 'BallisticaCore' show up various places, which in actual releases gets replaced by 'BombSquad'. +* A: No, BombSquad is still BombSquad. 'Ballistica' is simply the new name for the engine/app-framework. This way it can also be used for other game/app projects without causing confusion (though that is mostly theoretical at this point). As a modder, the biggest changes you will notice is 'ba' prefixes in the API instead of 'bs' and naming that follows Python PEP8 standards (underscores and lowercase instead of camel-case). So `bs.playSound(mySound)` in the old system might look like `ba.playsound(my_sound)` in the new. You may also see the word 'BallisticaCore' show up various places, which in actual releases gets replaced by 'BombSquad'. * **Q: Does this mean BombSquad is open source?** * A: Yes and no. All code contained in this repo is MIT licensed and free for use anywhere. This includes game scripts, pipeline tools, etc. Over time I hope to expand this to include at least some of the binary engine sources. Anything not directly contained in this repository, however, even if automatically downloaded by build scripts, is still proprietary and cannot be redistributed without explicit consent. This includes assets and game binaries. So in a nutshell: create and share mods to your heart's content, but please don't distribute your own complete copies of the game without permission. Please email support@froemling.net if you have any questions about this. * **Q: Will my existing BombSquad 1.4.x mods still work?** -* A: No. All mods will need to be explicitly updated to work with the new ballistica apis in 1.5+. This may or may not be a significant amount of work depending on the mod. I would highly suggest tinkering around with some of the new features in 1.5 such as type-safe Python and dynamic assets before attempting to port any old mods, as some things are done significantly differently now. You may also want to consider simply sticking with 1.4 builds for a while longer, especially for server duties, since they will remain fully compatible with clients running 1.5. The new ballistica APIs may be changing significantly for at least a while as the dust settles, but they will be worth switching to in the end, I promise! +* A: Not 'out of the box'. All mods will need to be explicitly updated to work with the new ballistica apis in 1.5+. This may or may not be a significant amount of work depending on the mod. I would highly suggest tinkering around with some of the new features in 1.5 such as type-safe Python and dynamic assets before attempting to port any old mods, as some things are done significantly differently now. You may also want to consider simply sticking with 1.4 builds for a while longer, especially for server duties, since they will remain fully compatible with clients running 1.5. The new ballistica APIs may be changing significantly for at least a while as the dust settles, but they will be worth switching to in the end, I promise! diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py index 10e4bbe4..796e7e29 100644 --- a/assets/src/ba_data/python/_ba.py +++ b/assets/src/ba_data/python/_ba.py @@ -34,7 +34,7 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand. """ # (hash we can use to see if this file is out of date) -# SOURCES_HASH=317793613698101603244532998583646381571 +# SOURCES_HASH=237466057120267570582079835997969754357 # I'm sorry Pylint. I know this file saddens you. Be strong. # pylint: disable=useless-suppression @@ -628,6 +628,7 @@ class Node: hold_position_pressed: bool = False knockout: float = 0.0 invincible: bool = False + stick_to_owner: bool = False damage: int = 0 run: float = 0.0 move_up_down: float = 0.0 diff --git a/assets/src/ba_data/python/ba/_actor.py b/assets/src/ba_data/python/ba/_actor.py index cfffb64b..c3e2a8cd 100644 --- a/assets/src/ba_data/python/ba/_actor.py +++ b/assets/src/ba_data/python/ba/_actor.py @@ -23,7 +23,7 @@ from __future__ import annotations import weakref -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING, TypeVar, overload from ba._messages import DieMessage, DeathType, OutOfBoundsMessage, UNHANDLED from ba._error import print_error, print_exception, ActivityNotFoundError @@ -31,6 +31,7 @@ import _ba if TYPE_CHECKING: from typing import Any, Optional + from typing_extensions import Literal import ba @@ -94,7 +95,7 @@ class Actor: def __del__(self) -> None: try: - # Non-expired Actors send themselves a DieMessage when going down. + # Unexpired Actors send themselves a DieMessage when going down. # That way we can treat DieMessage handling as the single # point-of-action for death. if not self.expired: @@ -214,6 +215,16 @@ class Actor: raise ActivityNotFoundError() return activity + # Overloads to convey our exact return type depending on 'doraise' value. + + @overload + def getactivity(self, doraise: Literal[True] = True) -> ba.Activity: + ... + + @overload + def getactivity(self, doraise: Literal[False]) -> Optional[ba.Activity]: + ... + def getactivity(self, doraise: bool = True) -> Optional[ba.Activity]: """Return the ba.Activity this Actor is associated with. diff --git a/assets/src/ba_data/python/bastd/actor/bomb.py b/assets/src/ba_data/python/bastd/actor/bomb.py index cb2e9025..ec6cf25e 100644 --- a/assets/src/ba_data/python/bastd/actor/bomb.py +++ b/assets/src/ba_data/python/bastd/actor/bomb.py @@ -863,16 +863,14 @@ class Bomb(ba.Actor): self.arm_timer = ba.Timer( 1.25, ba.WeakCall(self.handlemessage, ArmMessage())) - # once we've thrown a sticky bomb we can stick to it.. + # Once we've thrown a sticky bomb we can stick to it. elif self.bomb_type == 'sticky': - def _safesetattr(node: Optional[ba.Node], attr: str, - value: Any) -> None: + def _setsticky(node: ba.Node) -> None: if node: - setattr(node, attr, value) + node.stick_to_owner = True - ba.timer(0.25, - lambda: _safesetattr(self.node, 'stick_to_owner', True)) + ba.timer(0.25, lambda: _setsticky(self.node)) def _handle_splat(self) -> None: node = ba.getcollision().opposingnode @@ -896,8 +894,7 @@ class Bomb(ba.Actor): if self._exploded: return self._exploded = True - activity = self.getactivity() - if activity is not None and self.node: + if self.node: blast = Blast(position=self.node.position, velocity=self.node.velocity, blast_radius=self.blast_radius, diff --git a/assets/src/ba_data/python/bastd/actor/flag.py b/assets/src/ba_data/python/bastd/actor/flag.py index e06ea38a..f7df8bd3 100644 --- a/assets/src/ba_data/python/bastd/actor/flag.py +++ b/assets/src/ba_data/python/bastd/actor/flag.py @@ -325,7 +325,6 @@ class Flag(ba.Actor): 1.0, ba.WeakCall(self._hide_score_text)) def handlemessage(self, msg: Any) -> Any: - # pylint: disable=too-many-branches if __debug__: self._handlemessage_sanity_check() if isinstance(msg, ba.DieMessage): @@ -341,24 +340,16 @@ class Flag(ba.Actor): msg.velocity[1], msg.velocity[2], msg.magnitude, msg.velocity_magnitude, msg.radius, 0, msg.force_direction[0], msg.force_direction[1], msg.force_direction[2]) - elif isinstance(msg, ba.OutOfBoundsMessage): - # We just kill ourselves when out-of-bounds.. would we ever not - # want this?.. - self.handlemessage(ba.DieMessage(how=ba.DeathType.FALL)) elif isinstance(msg, ba.PickedUpMessage): self._held_count += 1 if self._held_count == 1 and self._counter is not None: self._counter.text = '' - activity = self.getactivity() - if activity is not None: - activity.handlemessage(FlagPickedUpMessage(self, msg.node)) + self.activity.handlemessage(FlagPickedUpMessage(self, msg.node)) elif isinstance(msg, ba.DroppedMessage): self._held_count -= 1 if self._held_count < 0: - print('Flag held count < 0') + print('Flag held count < 0.') self._held_count = 0 - activity = self.getactivity() - if activity is not None: - activity.handlemessage(FlagDroppedMessage(self, msg.node)) + self.activity.handlemessage(FlagDroppedMessage(self, msg.node)) else: super().handlemessage(msg) diff --git a/assets/src/ba_data/python/bastd/game/chosenone.py b/assets/src/ba_data/python/bastd/game/chosenone.py index 3100dd5c..256ea606 100644 --- a/assets/src/ba_data/python/bastd/game/chosenone.py +++ b/assets/src/ba_data/python/bastd/game/chosenone.py @@ -152,12 +152,18 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]): ba.timer(1.0, call=self._tick, repeat=True) mat = self._reset_region_material = ba.Material() - mat.add_actions(conditions=('they_have_material', - ba.sharedobj('player_material')), - actions=(('modify_part_collision', 'collide', True), - ('modify_part_collision', 'physical', False), - ('call', 'at_connect', - ba.WeakCall(self._handle_reset_collide)))) + mat.add_actions( + conditions=( + 'they_have_material', + ba.sharedobj('player_material'), + ), + actions=( + ('modify_part_collision', 'collide', True), + ('modify_part_collision', 'physical', False), + ('call', 'at_connect', + ba.WeakCall(self._handle_reset_collide)), + ), + ) self._reset_region = ba.newnode('region', attrs={ @@ -177,9 +183,15 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]): # If we have a chosen one, ignore these. if self._get_chosen_one_player() is not None: return - player = ba.getcollision().opposingnode.getdelegate( - PlayerSpaz, True).getplayer(Player) - if player is not None and player.is_alive(): + + # Attempt to get a Player controlling a Spaz that we hit. + try: + player = ba.getcollision().opposingnode.getdelegate( + PlayerSpaz, True).getplayer(Player, True) + except ba.NotFoundError: + return + + if player.is_alive(): self._set_chosen_one_player(player) def _flash_flag_spawn(self) -> None: diff --git a/docs/ba_module.md b/docs/ba_module.md index d1e9401b..844b2a5d 100644 --- a/docs/ba_module.md +++ b/docs/ba_module.md @@ -1,5 +1,5 @@ -

last updated on 2020-05-28 for Ballistica version 1.5.0 build 20031

+

last updated on 2020-05-28 for Ballistica version 1.5.0 build 20032

This page documents the Python classes and functions in the 'ba' module, which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please let me know. Happy modding!