mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-24 07:53:30 +08:00
More modernizing and cleanup
This commit is contained in:
parent
72be5c0b8d
commit
808ea7dcdd
@ -420,7 +420,7 @@
|
||||
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/75/1d/868bb04cf691736035c917d02762",
|
||||
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/44/2a/8535b446284235cb503947ece074",
|
||||
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/f5/d3/8e941851c4310465646c4167afc1",
|
||||
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/e2/2c/e609bb55002f672528428db9306c",
|
||||
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/da/06/8e06488b46a0a213abde3cfeb364",
|
||||
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/b8/ed/e18bec56ff1d094aae86517a7854",
|
||||
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/49/5f/b29bb65369040892fe6601801637",
|
||||
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/0c/cd/798753aa6c55f3a4cdccda0b23ab",
|
||||
@ -442,7 +442,7 @@
|
||||
"assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/0a/84/bbb6ed2abf66509406f534cbbb52",
|
||||
"assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/c2/fc/dd1c15cf9ecb411d9defbd000c06",
|
||||
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/db/eb/324f86a4b714240ae50ffeeed2f8",
|
||||
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/6c/35/cc4d440d0c7a613860c3898e81d7",
|
||||
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/a9/bc/ea61ebd23066c685fb779e23d10f",
|
||||
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/f6/d0/335b952306d211d56172b5c72d8c",
|
||||
"assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/78/5e/d24967ddaa15e0a574bd274544db",
|
||||
"assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e7/d8/ace32888249fc8b8cca0e2edb48b",
|
||||
@ -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/5a/42/5238d5de1cf94a07a513ea2b3e1b",
|
||||
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/39/48/2f0e4350a080373301de625e2cc4",
|
||||
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c0/1d/dbc0a5e2ca05b626cbeffd6def6c",
|
||||
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ec/a8/5c58d6b5e1d844640334c35ad3af",
|
||||
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3d/0a/76b615f9bcb9bf4f0ca745060d40",
|
||||
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/01/6b/0f623ac481e3553e352c1cd3cb7e",
|
||||
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c4/a5/3cad1ced77551369dc56c6bca5fb",
|
||||
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3b/3f/55094817619cae66f941a9f6db8c",
|
||||
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/ec/6b/7aa29831a746672f9984cbb1dbf1",
|
||||
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/07/4c/00a6136d0bd5573946d10e294720",
|
||||
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8d/8d/af068e67244cbb28444b27ae66d7",
|
||||
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f4/62/eb7cb26c952df481b757dcf53f9c"
|
||||
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e4/f0/c3261a8a7391a2b654da82c35c21",
|
||||
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/33/e2/85a8cbf23404612e6761cf02348b",
|
||||
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5b/0d/d906be5fc2a75b5214a94cafe6ca",
|
||||
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/6b/53/72d665caf5dfed84312ae6862642",
|
||||
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/68/2d98f2d1ee67b6d54adbdf76c863",
|
||||
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/3a/33f8f311260542126acea44d05e2",
|
||||
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/18/eb/dd39bcfa33983864089d3fb7f666",
|
||||
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f8/1d/3440010fcbcf18c5a6b2966f7fca",
|
||||
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/9b/fa/b4ad4b8b82fefe1faad610065b9f",
|
||||
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/7f/b9/03af92db6140135ddfa467a1545d",
|
||||
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/69/b9/687fc5fa38c3faf7a47eb4241a3c",
|
||||
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/cf/2b/589db924b489972981e10850a4e3"
|
||||
}
|
||||
5
.idea/dictionaries/ericf.xml
generated
5
.idea/dictionaries/ericf.xml
generated
@ -416,6 +416,7 @@
|
||||
<w>deathmatch</w>
|
||||
<w>deek</w>
|
||||
<w>defs</w>
|
||||
<w>defsline</w>
|
||||
<w>deivit</w>
|
||||
<w>depcls</w>
|
||||
<w>depdata</w>
|
||||
@ -749,6 +750,7 @@
|
||||
<w>getclass</w>
|
||||
<w>getcollide</w>
|
||||
<w>getcollidemodel</w>
|
||||
<w>getcollision</w>
|
||||
<w>getconf</w>
|
||||
<w>getconfig</w>
|
||||
<w>getcurrency</w>
|
||||
@ -1285,6 +1287,7 @@
|
||||
<w>outname</w>
|
||||
<w>outpath</w>
|
||||
<w>ouya</w>
|
||||
<w>overloadsigs</w>
|
||||
<w>packagedir</w>
|
||||
<w>packagedirs</w>
|
||||
<w>packagename</w>
|
||||
@ -1888,6 +1891,7 @@
|
||||
<w>toplevel</w>
|
||||
<w>totaldudes</w>
|
||||
<w>totalpts</w>
|
||||
<w>totalwaves</w>
|
||||
<w>totype</w>
|
||||
<w>touchpad</w>
|
||||
<w>tournamententry</w>
|
||||
@ -2022,6 +2026,7 @@
|
||||
<w>waaah</w>
|
||||
<w>wanttype</w>
|
||||
<w>wasdead</w>
|
||||
<w>wavenum</w>
|
||||
<w>weakref</w>
|
||||
<w>weakrefs</w>
|
||||
<w>weakrefset</w>
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"ba_data/python/ba/__pycache__/_assetmanager.cpython-37.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_benchmark.cpython-37.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_campaign.cpython-37.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_collision.cpython-37.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_coopgame.cpython-37.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_coopsession.cpython-37.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_dependency.cpython-37.opt-1.pyc",
|
||||
@ -67,6 +68,7 @@
|
||||
"ba_data/python/ba/_assetmanager.py",
|
||||
"ba_data/python/ba/_benchmark.py",
|
||||
"ba_data/python/ba/_campaign.py",
|
||||
"ba_data/python/ba/_collision.py",
|
||||
"ba_data/python/ba/_coopgame.py",
|
||||
"ba_data/python/ba/_coopsession.py",
|
||||
"ba_data/python/ba/_dependency.py",
|
||||
|
||||
@ -153,6 +153,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
build/ba_data/python/ba/_coopgame.py \
|
||||
build/ba_data/python/ba/_meta.py \
|
||||
build/ba_data/python/ba/_math.py \
|
||||
build/ba_data/python/ba/_collision.py \
|
||||
build/ba_data/python/ba/_servermode.py \
|
||||
build/ba_data/python/ba/_appconfig.py \
|
||||
build/ba_data/python/ba/_gameresults.py \
|
||||
@ -380,6 +381,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
build/ba_data/python/ba/__pycache__/_coopgame.cpython-37.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_meta.cpython-37.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_math.cpython-37.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_collision.cpython-37.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_servermode.cpython-37.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_appconfig.cpython-37.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_gameresults.cpython-37.opt-1.pyc \
|
||||
@ -663,6 +665,11 @@ build/ba_data/python/ba/__pycache__/_math.cpython-37.opt-1.pyc: \
|
||||
@echo Compiling script: $^
|
||||
@rm -rf $@ && $(TOOLS_DIR)/snippets compile_python_files $^ && chmod 444 $@
|
||||
|
||||
build/ba_data/python/ba/__pycache__/_collision.cpython-37.opt-1.pyc: \
|
||||
build/ba_data/python/ba/_collision.py
|
||||
@echo Compiling script: $^
|
||||
@rm -rf $@ && $(TOOLS_DIR)/snippets compile_python_files $^ && chmod 444 $@
|
||||
|
||||
build/ba_data/python/ba/__pycache__/_servermode.cpython-37.opt-1.pyc: \
|
||||
build/ba_data/python/ba/_servermode.py
|
||||
@echo Compiling script: $^
|
||||
|
||||
@ -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=146544975806995156523304218095856323339
|
||||
# SOURCES_HASH=164420280597992494471294420110866243586
|
||||
|
||||
# I'm sorry Pylint. I know this file saddens you. Be strong.
|
||||
# pylint: disable=useless-suppression
|
||||
@ -51,16 +51,19 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, overload, Sequence
|
||||
from typing import TYPE_CHECKING, overload, Sequence, TypeVar
|
||||
|
||||
from ba._enums import TimeFormat, TimeType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import (Any, Dict, Callable, Tuple, List, Optional, Union,
|
||||
List, Type)
|
||||
from typing_extensions import Literal
|
||||
from ba._app import App
|
||||
import ba
|
||||
|
||||
_T = TypeVar('_T')
|
||||
|
||||
app: App
|
||||
|
||||
|
||||
@ -713,13 +716,26 @@ class Node:
|
||||
"""
|
||||
return str()
|
||||
|
||||
def getdelegate(self) -> Any:
|
||||
"""getdelegate() -> Any
|
||||
@overload
|
||||
def getdelegate(self,
|
||||
type: Type[_T],
|
||||
doraise: Literal[False] = False) -> Optional[_T]:
|
||||
...
|
||||
|
||||
Returns the node's current delegate, which is the Python object
|
||||
designated to handle the Node's messages.
|
||||
@overload
|
||||
def getdelegate(self, type: Type[_T], doraise: Literal[True]) -> _T:
|
||||
...
|
||||
|
||||
def getdelegate(self, type: Any, doraise: bool = False) -> Any:
|
||||
"""getdelegate(type: Type, doraise: bool = False) -> <varies>
|
||||
|
||||
Return the node's current delegate object if it matches a certain type.
|
||||
|
||||
If the node has no delegate or it is not an instance of the passed
|
||||
type, then None will be returned. If 'doraise' is True, then an
|
||||
Exception will be raised instead in such cases.
|
||||
"""
|
||||
return _uninferrable()
|
||||
return None
|
||||
|
||||
def getnodetype(self) -> str:
|
||||
"""getnodetype() -> str
|
||||
|
||||
@ -30,13 +30,13 @@ In some specific cases you may need to pull in individual submodules instead.
|
||||
from _ba import (CollideModel, Context, ContextCall, Data, InputDevice,
|
||||
Material, Model, Node, SessionPlayer, Sound, Texture, Timer,
|
||||
Vec3, Widget, buttonwidget, camerashake, checkboxwidget,
|
||||
columnwidget, containerwidget, do_once, emitfx,
|
||||
get_collision_info, getactivity, getcollidemodel, getmodel,
|
||||
getnodes, getsession, getsound, gettexture, hscrollwidget,
|
||||
imagewidget, log, new_activity, newnode, playsound,
|
||||
printnodes, printobjects, pushcall, quit, rowwidget,
|
||||
safecolor, screenmessage, scrollwidget, set_analytics_screen,
|
||||
charstr, textwidget, time, timer, open_url, widget)
|
||||
columnwidget, containerwidget, do_once, emitfx, getactivity,
|
||||
getcollidemodel, getmodel, getnodes, getsession, getsound,
|
||||
gettexture, hscrollwidget, imagewidget, log, new_activity,
|
||||
newnode, playsound, printnodes, printobjects, pushcall, quit,
|
||||
rowwidget, safecolor, screenmessage, scrollwidget,
|
||||
set_analytics_screen, charstr, textwidget, time, timer,
|
||||
open_url, widget)
|
||||
from ba._activity import Activity
|
||||
from ba._actor import Actor
|
||||
from ba._player import Player, playercast, playercast_o
|
||||
@ -87,6 +87,7 @@ from ba._music import setmusic, MusicPlayer, MusicType, MusicPlayMode
|
||||
from ba._powerup import PowerupMessage, PowerupAcceptMessage
|
||||
from ba._multiteamsession import MultiTeamSession
|
||||
from ba.ui import Window, UIController, uicleanupcheck
|
||||
from ba._collision import Collision, getcollision
|
||||
|
||||
app: App
|
||||
|
||||
|
||||
@ -343,7 +343,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
||||
raise TypeError('non-actor passed to retain_actor')
|
||||
if (self.has_transitioned_in()
|
||||
and _ba.time() - self._last_prune_dead_actors_time > 10.0):
|
||||
print_error('it looks like nodes/actors are not'
|
||||
print_error('It looks like nodes/actors are not'
|
||||
' being pruned in your activity;'
|
||||
' did you call Activity.on_transition_in()'
|
||||
' from your subclass?; ' + str(self) + ' (loc. a)')
|
||||
@ -775,12 +775,12 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
||||
|
||||
# Send expire notices to all remaining actors.
|
||||
for actor_ref in self._actor_weak_refs:
|
||||
try:
|
||||
actor = actor_ref()
|
||||
if actor is not None:
|
||||
actor = actor_ref()
|
||||
if actor is not None:
|
||||
try:
|
||||
actor.on_expire()
|
||||
except Exception:
|
||||
print_exception(f'Error expiring Actor {actor_ref()}')
|
||||
except Exception:
|
||||
print_exception(f'Error expiring Actor {actor_ref()}')
|
||||
|
||||
# Reset all Players.
|
||||
# (releases any attached actors, clears game-data, etc)
|
||||
@ -804,7 +804,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
||||
sessionteam.reset_gamedata()
|
||||
except SessionTeamNotFoundError:
|
||||
pass
|
||||
# print_exception(f'Error resetting Team {team}')
|
||||
except Exception:
|
||||
print_exception(f'Error resetting Team {team}')
|
||||
|
||||
@ -819,6 +818,12 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
||||
'Error during ba.Activity._expire() destroying data:')
|
||||
|
||||
def _prune_dead_actors(self) -> None:
|
||||
self._actor_refs = [a for a in self._actor_refs if a]
|
||||
self._actor_weak_refs = [a for a in self._actor_weak_refs if a()]
|
||||
self._last_prune_dead_actors_time = _ba.time()
|
||||
|
||||
# Prune our strong refs when the Actor's exists() call gives False
|
||||
self._actor_refs = [a for a in self._actor_refs if a.exists()]
|
||||
|
||||
# Prune our weak refs once the Actor object has been freed.
|
||||
self._actor_weak_refs = [
|
||||
a for a in self._actor_weak_refs if a() is not None
|
||||
]
|
||||
|
||||
84
assets/src/ba_data/python/ba/_collision.py
Normal file
84
assets/src/ba_data/python/ba/_collision.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2011-2020 Eric Froemling
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# -----------------------------------------------------------------------------
|
||||
"""Collision related functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
from ba._error import NodeNotFoundError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ba
|
||||
|
||||
|
||||
class Collision:
|
||||
"""A class providing info about occurring collisions."""
|
||||
|
||||
@property
|
||||
def position(self) -> ba.Vec3:
|
||||
"""The position of the current collision."""
|
||||
return _ba.Vec3(_ba.get_collision_info('position'))
|
||||
|
||||
@property
|
||||
def source_node(self) -> ba.Node:
|
||||
"""The node containing the material triggering the current callback.
|
||||
|
||||
Throws a ba.NodeNotFoundError if the node does not exist, though
|
||||
the node should always exist (at least at the start of the collision
|
||||
callback).
|
||||
"""
|
||||
node = _ba.get_collision_info('source_node')
|
||||
assert isinstance(node, (_ba.Node, type(None)))
|
||||
if not node:
|
||||
raise NodeNotFoundError()
|
||||
return node
|
||||
|
||||
@property
|
||||
def opposing_node(self) -> ba.Node:
|
||||
"""The node the current callback material node is hitting.
|
||||
|
||||
Throws a ba.NodeNotFoundError if the node does not exist.
|
||||
This can be expected in some cases such as in 'disconnect'
|
||||
callbacks triggered by deleting a currently-colliding node.
|
||||
"""
|
||||
node = _ba.get_collision_info('opposing_node')
|
||||
assert isinstance(node, (_ba.Node, type(None)))
|
||||
if not node:
|
||||
raise NodeNotFoundError()
|
||||
return node
|
||||
|
||||
@property
|
||||
def opposing_body(self) -> int:
|
||||
"""The body index on the opposing node in the current collision."""
|
||||
body = _ba.get_collision_info('opposing_body')
|
||||
assert isinstance(body, int)
|
||||
return body
|
||||
|
||||
|
||||
# Simply recycle one instance...
|
||||
_collision = Collision()
|
||||
|
||||
|
||||
def getcollision() -> Collision:
|
||||
"""Return the in-progress collision."""
|
||||
return _collision
|
||||
@ -447,7 +447,6 @@ def cameraflash(duration: float = 999.0) -> None:
|
||||
# Store this on the current activity so we only have one at a time.
|
||||
# FIXME: Need a type safe way to do this.
|
||||
activity = _ba.getactivity()
|
||||
# noinspection PyTypeHints
|
||||
activity.camera_flash_data = [] # type: ignore
|
||||
for i in range(6):
|
||||
light = NodeActor(
|
||||
|
||||
@ -226,6 +226,5 @@ def playercast_o(totype: Type[PlayerType],
|
||||
|
||||
Category: Gameplay Functions
|
||||
"""
|
||||
# noinspection PyTypeHints
|
||||
assert isinstance(player, (totype, type(None)))
|
||||
return player
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
# SOFTWARE.
|
||||
# -----------------------------------------------------------------------------
|
||||
"""Powerup related functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@ -265,7 +265,6 @@ class BombFactory:
|
||||
actions=('message', 'our_node', 'at_connect', SplatMessage()))
|
||||
|
||||
|
||||
# noinspection PyTypeHints
|
||||
def get_factory() -> BombFactory:
|
||||
"""Get/create a shared bastd.actor.bomb.BombFactory object."""
|
||||
activity = ba.getactivity()
|
||||
@ -602,34 +601,28 @@ class Blast(ba.Actor):
|
||||
self.node.delete()
|
||||
|
||||
elif isinstance(msg, ExplodeHitMessage):
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
if node:
|
||||
assert self.node
|
||||
nodepos = self.node.position
|
||||
node = ba.getcollision().opposing_node
|
||||
assert self.node
|
||||
nodepos = self.node.position
|
||||
mag = 2000.0
|
||||
if self.blast_type == 'ice':
|
||||
mag *= 0.5
|
||||
elif self.blast_type == 'land_mine':
|
||||
mag *= 2.5
|
||||
elif self.blast_type == 'tnt':
|
||||
mag *= 2.0
|
||||
|
||||
# new
|
||||
mag = 2000.0
|
||||
if self.blast_type == 'ice':
|
||||
mag *= 0.5
|
||||
elif self.blast_type == 'land_mine':
|
||||
mag *= 2.5
|
||||
elif self.blast_type == 'tnt':
|
||||
mag *= 2.0
|
||||
|
||||
node.handlemessage(
|
||||
ba.HitMessage(pos=nodepos,
|
||||
velocity=(0, 0, 0),
|
||||
magnitude=mag,
|
||||
hit_type=self.hit_type,
|
||||
hit_subtype=self.hit_subtype,
|
||||
radius=self.radius,
|
||||
source_player=ba.existing(
|
||||
self.source_player)))
|
||||
if self.blast_type == 'ice':
|
||||
ba.playsound(get_factory().freeze_sound,
|
||||
10,
|
||||
position=nodepos)
|
||||
node.handlemessage(ba.FreezeMessage())
|
||||
node.handlemessage(
|
||||
ba.HitMessage(pos=nodepos,
|
||||
velocity=(0, 0, 0),
|
||||
magnitude=mag,
|
||||
hit_type=self.hit_type,
|
||||
hit_subtype=self.hit_subtype,
|
||||
radius=self.radius,
|
||||
source_player=ba.existing(self.source_player)))
|
||||
if self.blast_type == 'ice':
|
||||
ba.playsound(get_factory().freeze_sound, 10, position=nodepos)
|
||||
node.handlemessage(ba.FreezeMessage())
|
||||
|
||||
else:
|
||||
super().handlemessage(msg)
|
||||
@ -850,14 +843,13 @@ class Bomb(ba.Actor):
|
||||
self.handlemessage(ba.DieMessage())
|
||||
|
||||
def _handle_impact(self) -> None:
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
# if we're an impact bomb and we came from this node, don't explode...
|
||||
node = ba.getcollision().opposing_node
|
||||
|
||||
# If we're an impact bomb and we came from this node, don't explode...
|
||||
# alternately if we're hitting another impact-bomb from the same
|
||||
# source, don't explode...
|
||||
try:
|
||||
node_delegate = node.getdelegate()
|
||||
except Exception:
|
||||
node_delegate = None
|
||||
# try:
|
||||
node_delegate = node.getdelegate(object)
|
||||
if node:
|
||||
if (self.bomb_type == 'impact' and
|
||||
(node is self.owner or
|
||||
@ -883,7 +875,7 @@ class Bomb(ba.Actor):
|
||||
lambda: _safesetattr(self.node, 'stick_to_owner', True))
|
||||
|
||||
def _handle_splat(self) -> None:
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
node = ba.getcollision().opposing_node
|
||||
if (node is not self.owner
|
||||
and ba.time() - self._last_sticky_sound_time > 1.0):
|
||||
self._last_sticky_sound_time = ba.time()
|
||||
|
||||
@ -105,19 +105,16 @@ class FlagFactory:
|
||||
|
||||
self.flag_texture = ba.gettexture('flagColor')
|
||||
|
||||
|
||||
# noinspection PyTypeHints
|
||||
def get_factory() -> FlagFactory:
|
||||
"""Get/create a shared bastd.actor.flag.FlagFactory object."""
|
||||
activity = ba.getactivity()
|
||||
factory: FlagFactory
|
||||
try:
|
||||
# FIXME: Find elegant way to handle shared data like this.
|
||||
factory = activity.shared_flag_factory # type: ignore
|
||||
except Exception:
|
||||
factory = activity.shared_flag_factory = FlagFactory() # type: ignore
|
||||
assert isinstance(factory, FlagFactory)
|
||||
return factory
|
||||
@staticmethod
|
||||
def get() -> FlagFactory:
|
||||
"""Get/create a shared FlagFactory instance."""
|
||||
activity = ba.getactivity()
|
||||
factory = getattr(activity, 'shared_flag_factory', None)
|
||||
if factory is None:
|
||||
factory = FlagFactory()
|
||||
setattr(activity, 'shared_flag_factory', factory)
|
||||
assert isinstance(factory, FlagFactory)
|
||||
return factory
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -201,7 +198,7 @@ class Flag(ba.Actor):
|
||||
|
||||
self._initial_position: Optional[Sequence[float]] = None
|
||||
self._has_moved = False
|
||||
factory = get_factory()
|
||||
factory = FlagFactory.get()
|
||||
|
||||
if materials is None:
|
||||
materials = []
|
||||
|
||||
@ -305,11 +305,9 @@ class PowerupBox(ba.Actor):
|
||||
|
||||
elif isinstance(msg, _TouchedMessage):
|
||||
if not self._powersgiven:
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
if node:
|
||||
node.handlemessage(
|
||||
ba.PowerupMessage(self.poweruptype,
|
||||
source_node=self.node))
|
||||
node = ba.getcollision().opposing_node
|
||||
node.handlemessage(
|
||||
ba.PowerupMessage(self.poweruptype, source_node=self.node))
|
||||
|
||||
elif isinstance(msg, ba.DieMessage):
|
||||
if self.node:
|
||||
|
||||
@ -62,7 +62,6 @@ def get_factory() -> SpazFactory:
|
||||
activity = ba.getactivity()
|
||||
factory = getattr(activity, 'shared_spaz_factory', None)
|
||||
if factory is None:
|
||||
# noinspection PyTypeHints
|
||||
factory = activity.shared_spaz_factory = SpazFactory() # type: ignore
|
||||
assert isinstance(factory, SpazFactory)
|
||||
return factory
|
||||
@ -1144,7 +1143,7 @@ class Spaz(ba.Actor):
|
||||
elif isinstance(msg, PunchHitMessage):
|
||||
if not self.node:
|
||||
return None
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
node = ba.getcollision().opposing_node
|
||||
|
||||
# Only allow one hit per node per punch.
|
||||
if node and (node not in self._punched_nodes):
|
||||
@ -1203,10 +1202,11 @@ class Spaz(ba.Actor):
|
||||
if not self.node:
|
||||
return None
|
||||
|
||||
opposing_node, opposing_body = ba.get_collision_info(
|
||||
'opposing_node', 'opposing_body')
|
||||
|
||||
if opposing_node is None or not opposing_node:
|
||||
try:
|
||||
collision = ba.getcollision()
|
||||
opposing_node = collision.opposing_node
|
||||
opposing_body = collision.opposing_body
|
||||
except ba.NotFoundError:
|
||||
return True
|
||||
|
||||
# Don't allow picking up of invincible dudes.
|
||||
|
||||
@ -62,7 +62,7 @@ class SpazBotPunchedMessage:
|
||||
self.damage = damage
|
||||
|
||||
|
||||
class SpazBotDeathMessage:
|
||||
class SpazBotDiedMessage:
|
||||
"""A message saying a ba.SpazBot has died.
|
||||
|
||||
category: Message Classes
|
||||
@ -98,7 +98,7 @@ class SpazBot(Spaz):
|
||||
navigate obstacles and so should only be used
|
||||
on wide-open maps.
|
||||
|
||||
When a SpazBot is killed, it delivers a ba.SpazBotDeathMessage
|
||||
When a SpazBot is killed, it delivers a ba.SpazBotDiedMessage
|
||||
to the current activity.
|
||||
|
||||
When a SpazBot is punched, it delivers a ba.SpazBotPunchedMessage
|
||||
@ -569,7 +569,7 @@ class SpazBot(Spaz):
|
||||
killerplayer = None
|
||||
if activity is not None:
|
||||
activity.handlemessage(
|
||||
SpazBotDeathMessage(self, killerplayer, msg.how))
|
||||
SpazBotDiedMessage(self, killerplayer, msg.how))
|
||||
super().handlemessage(msg) # Augment standard behavior.
|
||||
|
||||
# Keep track of the player who last hit us for point rewarding.
|
||||
@ -894,7 +894,7 @@ class ExplodeyBotShielded(ExplodeyBot):
|
||||
points_mult = 5
|
||||
|
||||
|
||||
class BotSet:
|
||||
class SpazBotSet:
|
||||
"""A container/controller for one or more ba.SpazBots.
|
||||
|
||||
category: Bot Classes
|
||||
@ -963,9 +963,7 @@ class BotSet:
|
||||
def _update(self) -> None:
|
||||
|
||||
# Update one of our bot lists each time through.
|
||||
# First off, remove dead bots from the list. Note that we check
|
||||
# exists() here via the bool operator instead of dead; we want to
|
||||
# keep them around even if they're just a corpse.
|
||||
# First off, remove no-longer-existing bots from the list.
|
||||
try:
|
||||
bot_list = self._bot_lists[self._bot_update_list] = ([
|
||||
b for b in self._bot_lists[self._bot_update_list] if b
|
||||
@ -982,6 +980,9 @@ class BotSet:
|
||||
for player in ba.getactivity().players:
|
||||
assert isinstance(player, ba.Player)
|
||||
try:
|
||||
# TODO: could use abstracted player.position here so we
|
||||
# don't have to assume their actor type, but we have no
|
||||
# abstracted velocity as of yet.
|
||||
if player.is_alive():
|
||||
assert isinstance(player.actor, Spaz)
|
||||
assert player.actor.node
|
||||
|
||||
@ -179,16 +179,9 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
|
||||
ba.timer(length, light.delete)
|
||||
|
||||
def _handle_base_collide(self, team: Team) -> None:
|
||||
|
||||
# Attempt to pull a living ba.Player from what we hit.
|
||||
cnode = ba.get_collision_info('opposing_node')
|
||||
assert isinstance(cnode, ba.Node)
|
||||
actor = cnode.getdelegate()
|
||||
if not isinstance(actor, PlayerSpaz):
|
||||
return
|
||||
|
||||
player = actor.getplayer(Player)
|
||||
if not player or not player.actor:
|
||||
player = ba.getcollision().opposing_node.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player)
|
||||
if not player or not player.is_alive():
|
||||
return
|
||||
|
||||
# If its another team's player, they scored.
|
||||
|
||||
@ -28,15 +28,16 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.actor import flag as stdflag
|
||||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.flag import (FlagFactory, Flag, FlagPickedUpMessage,
|
||||
FlagDroppedMessage, FlagDiedMessage)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Type, List, Dict, Sequence, Union, Optional
|
||||
|
||||
|
||||
class CTFFlag(stdflag.Flag):
|
||||
class CTFFlag(Flag):
|
||||
"""Special flag type for CTF games."""
|
||||
|
||||
activity: CaptureTheFlagGame
|
||||
@ -73,10 +74,7 @@ class CTFFlag(stdflag.Flag):
|
||||
@classmethod
|
||||
def from_node(cls, node: Optional[ba.Node]) -> Optional[CTFFlag]:
|
||||
"""Attempt to get a CTFFlag from a flag node."""
|
||||
if not node:
|
||||
return None
|
||||
delegate = node.getdelegate()
|
||||
return delegate if isinstance(delegate, CTFFlag) else None
|
||||
return node.getdelegate(CTFFlag) if node else None
|
||||
|
||||
|
||||
class Player(ba.Player['Team']):
|
||||
@ -246,8 +244,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
||||
|
||||
# We wanna know when *any* flag enters/leaves our base.
|
||||
base_region_mat.add_actions(
|
||||
conditions=('they_have_material',
|
||||
stdflag.get_factory().flagmaterial),
|
||||
conditions=('they_have_material', FlagFactory.get().flagmaterial),
|
||||
actions=(('modify_part_collision', 'collide',
|
||||
True), ('modify_part_collision', 'physical', False),
|
||||
('call', 'at_connect',
|
||||
@ -277,9 +274,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
||||
ba.playsound(self._swipsound, position=team.flag.node.position)
|
||||
|
||||
def _handle_flag_entered_base(self, team: Team) -> None:
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
assert isinstance(node, (ba.Node, type(None)))
|
||||
flag = CTFFlag.from_node(node)
|
||||
flag = CTFFlag.from_node(ba.getcollision().opposing_node)
|
||||
if not flag:
|
||||
print('Unable to get flag in _handle_flag_entered_base')
|
||||
return
|
||||
@ -389,9 +384,12 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
||||
|
||||
def _handle_flag_left_base(self, team: Team) -> None:
|
||||
cur_time = ba.time()
|
||||
op_node = ba.get_collision_info('opposing_node')
|
||||
assert isinstance(op_node, (ba.Node, type(None)))
|
||||
flag = CTFFlag.from_node(op_node)
|
||||
try:
|
||||
flag = CTFFlag.from_node(ba.getcollision().opposing_node)
|
||||
except ba.NodeNotFoundError:
|
||||
# We still get this call even if the flag stopped touching us
|
||||
# because it was deleted; that's ok.
|
||||
flag = None
|
||||
if not flag:
|
||||
return
|
||||
if flag.team is team:
|
||||
@ -445,19 +443,15 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
||||
"""Return a player if given a node that is part of one's actor."""
|
||||
if not node:
|
||||
return None
|
||||
delegate = node.getdelegate()
|
||||
if not isinstance(delegate, PlayerSpaz):
|
||||
return None
|
||||
return delegate.getplayer(Player)
|
||||
delegate = node.getdelegate(PlayerSpaz)
|
||||
return None if delegate is None else delegate.getplayer(Player)
|
||||
|
||||
def _handle_hit_own_flag(self, team: Team, val: int) -> None:
|
||||
"""
|
||||
keep track of when each player is touching their
|
||||
own flag so we can award points when returned
|
||||
"""
|
||||
srcnode = ba.get_collision_info('source_node')
|
||||
assert isinstance(srcnode, (ba.Node, type(None)))
|
||||
player = self._player_from_node(srcnode)
|
||||
player = self._player_from_node(ba.getcollision().source_node)
|
||||
if player:
|
||||
player.touching_own_flag += (1 if val else -1)
|
||||
|
||||
@ -470,10 +464,9 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
||||
# Use a node message to kill the flag instead of just killing
|
||||
# our team's. (avoids redundantly killing new flags if
|
||||
# multiple body parts generate callbacks in one step).
|
||||
node = ba.get_collision_info('opposing_node')
|
||||
if node:
|
||||
self._award_players_touching_own_flag(team)
|
||||
node.handlemessage(ba.DieMessage())
|
||||
node = ba.getcollision().opposing_node
|
||||
self._award_players_touching_own_flag(team)
|
||||
node.handlemessage(ba.DieMessage())
|
||||
|
||||
# Takes a non-zero amount of time to return.
|
||||
else:
|
||||
@ -547,16 +540,17 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
||||
# Augment standard behavior.
|
||||
super().handlemessage(msg)
|
||||
self.respawn_player(msg.getplayer(Player))
|
||||
elif isinstance(msg, stdflag.FlagDiedMessage):
|
||||
elif isinstance(msg, FlagDiedMessage):
|
||||
assert isinstance(msg.flag, CTFFlag)
|
||||
ba.timer(0.1, ba.Call(self._spawn_flag_for_team, msg.flag.team))
|
||||
elif isinstance(msg, stdflag.FlagPickedUpMessage):
|
||||
elif isinstance(msg, FlagPickedUpMessage):
|
||||
# Store the last player to hold the flag for scoring purposes.
|
||||
assert isinstance(msg.flag, CTFFlag)
|
||||
msg.flag.last_player_to_hold = msg.node.getdelegate().getplayer()
|
||||
msg.flag.last_player_to_hold = msg.node.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player)
|
||||
msg.flag.held_count += 1
|
||||
msg.flag.reset_return_times()
|
||||
elif isinstance(msg, stdflag.FlagDroppedMessage):
|
||||
elif isinstance(msg, FlagDroppedMessage):
|
||||
# Store the last player to hold the flag for scoring purposes.
|
||||
assert isinstance(msg.flag, CTFFlag)
|
||||
msg.flag.held_count -= 1
|
||||
|
||||
@ -177,11 +177,10 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
|
||||
# If we have a chosen one, ignore these.
|
||||
if self._get_chosen_one_player() is not None:
|
||||
return
|
||||
delegate = ba.get_collision_info('opposing_node').getdelegate()
|
||||
if isinstance(delegate, PlayerSpaz):
|
||||
player = delegate.getplayer(Player)
|
||||
if player is not None and player.is_alive():
|
||||
self._set_chosen_one_player(player)
|
||||
player = ba.getcollision().opposing_node.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player)
|
||||
if player is not None and player.is_alive():
|
||||
self._set_chosen_one_player(player)
|
||||
|
||||
def _flash_flag_spawn(self) -> None:
|
||||
light = ba.newnode('light',
|
||||
|
||||
@ -31,6 +31,7 @@ from typing import TYPE_CHECKING
|
||||
import ba
|
||||
from bastd.actor.flag import Flag
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Optional, Type, List, Dict, Sequence, Union
|
||||
@ -233,15 +234,12 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
|
||||
ba.timer(length, light.delete)
|
||||
|
||||
def _handle_flag_player_collide(self) -> None:
|
||||
flagnode, playernode = ba.get_collision_info('source_node',
|
||||
'opposing_node')
|
||||
try:
|
||||
player = playernode.getdelegate().getplayer()
|
||||
flag = flagnode.getdelegate()
|
||||
except Exception:
|
||||
return # Player may have left and his body hit the flag.
|
||||
assert isinstance(player, Player)
|
||||
assert isinstance(flag, ConquestFlag)
|
||||
collision = ba.getcollision()
|
||||
flag = collision.source_node.getdelegate(ConquestFlag)
|
||||
player = collision.opposing_node.getdelegate(PlayerSpaz,
|
||||
True).getplayer(Player)
|
||||
if not flag or not player:
|
||||
return
|
||||
assert flag.light
|
||||
|
||||
if flag.team is not player.team:
|
||||
|
||||
@ -31,7 +31,7 @@ from typing import TYPE_CHECKING
|
||||
import ba
|
||||
from bastd.actor.bomb import Bomb
|
||||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
from bastd.actor.spazbot import BotSet, BouncyBot, SpazBotDeathMessage
|
||||
from bastd.actor.spazbot import SpazBotSet, BouncyBot, SpazBotDiedMessage
|
||||
from bastd.actor.onscreencountdown import OnScreenCountdown
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.respawnicon import RespawnIcon
|
||||
@ -94,7 +94,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._eggs: List[Egg] = []
|
||||
self._update_timer: Optional[ba.Timer] = None
|
||||
self._countdown: Optional[OnScreenCountdown] = None
|
||||
self._bots: Optional[BotSet] = None
|
||||
self._bots: Optional[SpazBotSet] = None
|
||||
|
||||
# Base class overrides
|
||||
self.default_music = ba.MusicType.FORWARD_MARCH
|
||||
@ -117,7 +117,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._update_timer = ba.Timer(0.25, self._update, repeat=True)
|
||||
self._countdown = OnScreenCountdown(60, endcall=self.end_game)
|
||||
ba.timer(4.0, self._countdown.start)
|
||||
self._bots = BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
|
||||
# Spawn evil bunny in co-op only.
|
||||
if isinstance(self.session, ba.CoopSession) and self._pro_mode:
|
||||
@ -134,49 +134,44 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._bots.spawn_bot(BouncyBot, pos=(6, 4, -7.8), spawn_time=10.0)
|
||||
|
||||
def _on_egg_player_collide(self) -> None:
|
||||
if not self.has_ended():
|
||||
egg_node, playernode = ba.get_collision_info(
|
||||
'source_node', 'opposing_node')
|
||||
if egg_node is not None and playernode is not None:
|
||||
egg = egg_node.getdelegate()
|
||||
assert isinstance(egg, Egg)
|
||||
spaz = playernode.getdelegate()
|
||||
assert isinstance(spaz, PlayerSpaz)
|
||||
player = spaz.getplayer(Player)
|
||||
if player and egg:
|
||||
player.team.score += 1
|
||||
if self.has_ended():
|
||||
return
|
||||
collision = ba.getcollision()
|
||||
egg = collision.source_node.getdelegate(Egg)
|
||||
player = collision.opposing_node.getdelegate(PlayerSpaz,
|
||||
True).getplayer(Player)
|
||||
if player and egg:
|
||||
player.team.score += 1
|
||||
|
||||
# Displays a +1 (and adds to individual player score in
|
||||
# teams mode).
|
||||
self.stats.player_scored(player, 1, screenmessage=False)
|
||||
if self._max_eggs < 5:
|
||||
self._max_eggs += 1.0
|
||||
elif self._max_eggs < 10:
|
||||
self._max_eggs += 0.5
|
||||
elif self._max_eggs < 30:
|
||||
self._max_eggs += 0.3
|
||||
self._update_scoreboard()
|
||||
ba.playsound(self._collect_sound,
|
||||
0.5,
|
||||
position=egg.node.position)
|
||||
# Displays a +1 (and adds to individual player score in
|
||||
# teams mode).
|
||||
self.stats.player_scored(player, 1, screenmessage=False)
|
||||
if self._max_eggs < 5:
|
||||
self._max_eggs += 1.0
|
||||
elif self._max_eggs < 10:
|
||||
self._max_eggs += 0.5
|
||||
elif self._max_eggs < 30:
|
||||
self._max_eggs += 0.3
|
||||
self._update_scoreboard()
|
||||
ba.playsound(self._collect_sound, 0.5, position=egg.node.position)
|
||||
|
||||
# Create a flash.
|
||||
light = ba.newnode('light',
|
||||
attrs={
|
||||
'position': egg_node.position,
|
||||
'height_attenuated': False,
|
||||
'radius': 0.1,
|
||||
'color': (1, 1, 0)
|
||||
})
|
||||
ba.animate(light,
|
||||
'intensity', {
|
||||
0: 0,
|
||||
0.1: 1.0,
|
||||
0.2: 0
|
||||
},
|
||||
loop=False)
|
||||
ba.timer(0.200, light.delete)
|
||||
egg.handlemessage(ba.DieMessage())
|
||||
# Create a flash.
|
||||
light = ba.newnode('light',
|
||||
attrs={
|
||||
'position': egg.node.position,
|
||||
'height_attenuated': False,
|
||||
'radius': 0.1,
|
||||
'color': (1, 1, 0)
|
||||
})
|
||||
ba.animate(light,
|
||||
'intensity', {
|
||||
0: 0,
|
||||
0.1: 1.0,
|
||||
0.2: 0
|
||||
},
|
||||
loop=False)
|
||||
ba.timer(0.200, light.delete)
|
||||
egg.handlemessage(ba.DieMessage())
|
||||
|
||||
def _update(self) -> None:
|
||||
# Misc. periodic updating.
|
||||
@ -219,7 +214,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
|
||||
player.respawn_icon = RespawnIcon(player, respawn_time)
|
||||
|
||||
# Whenever our evil bunny dies, respawn him and spew some eggs.
|
||||
elif isinstance(msg, SpazBotDeathMessage):
|
||||
elif isinstance(msg, SpazBotDiedMessage):
|
||||
self._spawn_evil_bunny()
|
||||
assert msg.badguy.node
|
||||
pos = msg.badguy.node.position
|
||||
|
||||
@ -30,20 +30,26 @@ from typing import TYPE_CHECKING
|
||||
import math
|
||||
|
||||
import ba
|
||||
from bastd.actor import spazbot
|
||||
from bastd.actor import flag as stdflag
|
||||
from bastd.actor.bomb import TNTSpawner
|
||||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.respawnicon import RespawnIcon
|
||||
from bastd.actor.powerupbox import PowerupBoxFactory, PowerupBox
|
||||
from bastd.actor.flag import (FlagFactory, Flag, FlagPickedUpMessage,
|
||||
FlagDroppedMessage, FlagDiedMessage)
|
||||
from bastd.actor.spazbot import (SpazBotDiedMessage, SpazBotPunchedMessage,
|
||||
SpazBotSet, BrawlerBotLite, BrawlerBot,
|
||||
BomberBotLite, BomberBot, TriggerBot,
|
||||
ChargerBot, TriggerBotPro, BrawlerBotPro,
|
||||
StickyBot, ExplodeyBot)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, List, Type, Dict, Sequence, Optional, Union
|
||||
from bastd.actor.spaz import Spaz
|
||||
from bastd.actor.spazbot import SpazBot
|
||||
|
||||
|
||||
class FootballFlag(stdflag.Flag):
|
||||
class FootballFlag(Flag):
|
||||
"""Custom flag class for football games."""
|
||||
|
||||
def __init__(self, position: Sequence[float]):
|
||||
@ -129,8 +135,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._whistle_sound = ba.getsound('refWhistle')
|
||||
self._score_region_material = ba.Material()
|
||||
self._score_region_material.add_actions(
|
||||
conditions=('they_have_material',
|
||||
stdflag.get_factory().flagmaterial),
|
||||
conditions=('they_have_material', FlagFactory.get().flagmaterial),
|
||||
actions=(
|
||||
('modify_part_collision', 'collide', True),
|
||||
('modify_part_collision', 'physical', False),
|
||||
@ -204,7 +209,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
|
||||
assert self._flag is not None
|
||||
if self._flag.scored:
|
||||
return
|
||||
region = ba.get_collision_info('source_node')
|
||||
region = ba.getcollision().source_node
|
||||
i = None
|
||||
for i in range(len(self._score_regions)):
|
||||
if region == self._score_regions[i].node:
|
||||
@ -238,7 +243,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
|
||||
ba.timer(1.0, self._kill_flag)
|
||||
light = ba.newnode('light',
|
||||
attrs={
|
||||
'position': ba.get_collision_info('position'),
|
||||
'position': ba.getcollision().position,
|
||||
'height_attenuated': False,
|
||||
'color': (1, 0, 0)
|
||||
})
|
||||
@ -260,18 +265,14 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._score_to_win)
|
||||
|
||||
def handlemessage(self, msg: Any) -> Any:
|
||||
if isinstance(msg, stdflag.FlagPickedUpMessage):
|
||||
if isinstance(msg, FlagPickedUpMessage):
|
||||
assert isinstance(msg.flag, FootballFlag)
|
||||
try:
|
||||
player = msg.node.getdelegate().getplayer()
|
||||
if player:
|
||||
msg.flag.last_holding_player = player
|
||||
msg.flag.held_count += 1
|
||||
except Exception:
|
||||
ba.print_exception('exception in Football FlagPickedUpMessage;'
|
||||
" this shouldn't happen")
|
||||
player = msg.node.getdelegate(PlayerSpaz, True).getplayer(Player)
|
||||
if player:
|
||||
msg.flag.last_holding_player = player
|
||||
msg.flag.held_count += 1
|
||||
|
||||
elif isinstance(msg, stdflag.FlagDroppedMessage):
|
||||
elif isinstance(msg, FlagDroppedMessage):
|
||||
assert isinstance(msg.flag, FootballFlag)
|
||||
msg.flag.held_count -= 1
|
||||
|
||||
@ -282,7 +283,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
|
||||
self.respawn_player(msg.getplayer(Player))
|
||||
|
||||
# Respawn dead flags.
|
||||
elif isinstance(msg, stdflag.FlagDiedMessage):
|
||||
elif isinstance(msg, FlagDiedMessage):
|
||||
if not self.has_ended():
|
||||
self._flag_respawn_timer = ba.Timer(3.0, self._spawn_flag)
|
||||
self._flag_respawn_light = ba.NodeActor(
|
||||
@ -368,8 +369,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._score_to_win = 21
|
||||
self._score_region_material = ba.Material()
|
||||
self._score_region_material.add_actions(
|
||||
conditions=('they_have_material',
|
||||
stdflag.get_factory().flagmaterial),
|
||||
conditions=('they_have_material', FlagFactory.get().flagmaterial),
|
||||
actions=(
|
||||
('modify_part_collision', 'collide', True),
|
||||
('modify_part_collision', 'physical', False),
|
||||
@ -384,15 +384,15 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._score_regions: List[ba.NodeActor] = []
|
||||
self._exclude_powerups: List[str] = []
|
||||
self._have_tnt = False
|
||||
self._bot_types_initial: Optional[List[Type[spazbot.SpazBot]]] = None
|
||||
self._bot_types_7: Optional[List[Type[spazbot.SpazBot]]] = None
|
||||
self._bot_types_14: Optional[List[Type[spazbot.SpazBot]]] = None
|
||||
self._bot_types_initial: Optional[List[Type[SpazBot]]] = None
|
||||
self._bot_types_7: Optional[List[Type[SpazBot]]] = None
|
||||
self._bot_types_14: Optional[List[Type[SpazBot]]] = None
|
||||
self._bot_team: Optional[Team] = None
|
||||
self._starttime_ms: Optional[int] = None
|
||||
self._time_text: Optional[ba.NodeActor] = None
|
||||
self._time_text_input: Optional[ba.NodeActor] = None
|
||||
self._tntspawner: Optional[TNTSpawner] = None
|
||||
self._bots = spazbot.BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
self._bot_spawn_timer: Optional[ba.Timer] = None
|
||||
self._powerup_drop_timer: Optional[ba.Timer] = None
|
||||
self._scoring_team: Optional[Team] = None
|
||||
@ -440,64 +440,56 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
controlsguide.ControlsGuide(delay=3.0, lifespan=10.0,
|
||||
bright=True).autoretain()
|
||||
assert self.initial_player_info is not None
|
||||
abot: Type[spazbot.SpazBot]
|
||||
bbot: Type[spazbot.SpazBot]
|
||||
cbot: Type[spazbot.SpazBot]
|
||||
abot: Type[SpazBot]
|
||||
bbot: Type[SpazBot]
|
||||
cbot: Type[SpazBot]
|
||||
if self._preset in ['rookie', 'rookie_easy']:
|
||||
self._exclude_powerups = ['curse']
|
||||
self._have_tnt = False
|
||||
abot = (spazbot.BrawlerBotLite
|
||||
if self._preset == 'rookie_easy' else spazbot.BrawlerBot)
|
||||
abot = (BrawlerBotLite
|
||||
if self._preset == 'rookie_easy' else BrawlerBot)
|
||||
self._bot_types_initial = [abot] * len(self.initial_player_info)
|
||||
bbot = (spazbot.BomberBotLite
|
||||
if self._preset == 'rookie_easy' else spazbot.BomberBot)
|
||||
bbot = (BomberBotLite
|
||||
if self._preset == 'rookie_easy' else BomberBot)
|
||||
self._bot_types_7 = (
|
||||
[bbot] * (1 if len(self.initial_player_info) < 3 else 2))
|
||||
cbot = (spazbot.BomberBot
|
||||
if self._preset == 'rookie_easy' else spazbot.TriggerBot)
|
||||
cbot = (BomberBot if self._preset == 'rookie_easy' else TriggerBot)
|
||||
self._bot_types_14 = (
|
||||
[cbot] * (1 if len(self.initial_player_info) < 3 else 2))
|
||||
elif self._preset == 'tournament':
|
||||
self._exclude_powerups = []
|
||||
self._have_tnt = True
|
||||
self._bot_types_initial = (
|
||||
[spazbot.BrawlerBot] *
|
||||
(1 if len(self.initial_player_info) < 2 else 2))
|
||||
[BrawlerBot] * (1 if len(self.initial_player_info) < 2 else 2))
|
||||
self._bot_types_7 = (
|
||||
[spazbot.TriggerBot] *
|
||||
(1 if len(self.initial_player_info) < 3 else 2))
|
||||
[TriggerBot] * (1 if len(self.initial_player_info) < 3 else 2))
|
||||
self._bot_types_14 = (
|
||||
[spazbot.ChargerBot] *
|
||||
(1 if len(self.initial_player_info) < 4 else 2))
|
||||
[ChargerBot] * (1 if len(self.initial_player_info) < 4 else 2))
|
||||
elif self._preset in ['pro', 'pro_easy', 'tournament_pro']:
|
||||
self._exclude_powerups = ['curse']
|
||||
self._have_tnt = True
|
||||
self._bot_types_initial = [spazbot.ChargerBot] * len(
|
||||
self._bot_types_initial = [ChargerBot] * len(
|
||||
self.initial_player_info)
|
||||
abot = (spazbot.BrawlerBot
|
||||
if self._preset == 'pro' else spazbot.BrawlerBotLite)
|
||||
typed_bot_list: List[Type[spazbot.SpazBot]] = []
|
||||
abot = (BrawlerBot if self._preset == 'pro' else BrawlerBotLite)
|
||||
typed_bot_list: List[Type[SpazBot]] = []
|
||||
self._bot_types_7 = (
|
||||
typed_bot_list + [abot] + [spazbot.BomberBot] *
|
||||
typed_bot_list + [abot] + [BomberBot] *
|
||||
(1 if len(self.initial_player_info) < 3 else 2))
|
||||
bbot = (spazbot.TriggerBotPro
|
||||
if self._preset == 'pro' else spazbot.TriggerBot)
|
||||
bbot = (TriggerBotPro if self._preset == 'pro' else TriggerBot)
|
||||
self._bot_types_14 = (
|
||||
[bbot] * (1 if len(self.initial_player_info) < 3 else 2))
|
||||
elif self._preset in ['uber', 'uber_easy']:
|
||||
self._exclude_powerups = []
|
||||
self._have_tnt = True
|
||||
abot = (spazbot.BrawlerBotPro
|
||||
if self._preset == 'uber' else spazbot.BrawlerBot)
|
||||
bbot = (spazbot.TriggerBotPro
|
||||
if self._preset == 'uber' else spazbot.TriggerBot)
|
||||
typed_bot_list_2: List[Type[spazbot.SpazBot]] = []
|
||||
self._bot_types_initial = (typed_bot_list_2 + [spazbot.StickyBot] +
|
||||
abot = (BrawlerBotPro if self._preset == 'uber' else BrawlerBot)
|
||||
bbot = (TriggerBotPro if self._preset == 'uber' else TriggerBot)
|
||||
typed_bot_list_2: List[Type[SpazBot]] = []
|
||||
self._bot_types_initial = (typed_bot_list_2 + [StickyBot] +
|
||||
[abot] * len(self.initial_player_info))
|
||||
self._bot_types_7 = (
|
||||
[bbot] * (1 if len(self.initial_player_info) < 3 else 2))
|
||||
self._bot_types_14 = (
|
||||
[spazbot.ExplodeyBot] *
|
||||
[ExplodeyBot] *
|
||||
(1 if len(self.initial_player_info) < 3 else 2))
|
||||
else:
|
||||
raise Exception()
|
||||
@ -549,7 +541,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
if self._have_tnt:
|
||||
self._tntspawner = TNTSpawner(position=(0, 1, -1))
|
||||
|
||||
self._bots = spazbot.BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
self._bot_spawn_timer = ba.Timer(1.0, self._update_bots, repeat=True)
|
||||
|
||||
for bottype in self._bot_types_initial:
|
||||
@ -558,12 +550,12 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
def _on_got_scores_to_beat(self, scores: List[Dict[str, Any]]) -> None:
|
||||
self._show_standard_scores_to_beat_ui(scores)
|
||||
|
||||
def _on_bot_spawn(self, spaz: spazbot.SpazBot) -> None:
|
||||
def _on_bot_spawn(self, spaz: SpazBot) -> None:
|
||||
# We want to move to the left by default.
|
||||
spaz.target_point_default = ba.Vec3(0, 0, 0)
|
||||
|
||||
def _spawn_bot(self,
|
||||
spaz_type: Type[spazbot.SpazBot],
|
||||
spaz_type: Type[SpazBot],
|
||||
immediate: bool = False) -> None:
|
||||
assert self._bot_team is not None
|
||||
pos = self.map.get_start_position(self._bot_team.id)
|
||||
@ -594,7 +586,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
return
|
||||
|
||||
flagpos = ba.Vec3(self._flag.node.position)
|
||||
closest_bot: Optional[spazbot.SpazBot] = None
|
||||
closest_bot: Optional[SpazBot] = None
|
||||
closest_dist = 0.0 # Always gets assigned first time through.
|
||||
for bot in bots:
|
||||
# If a bot is picked up, he should forget about the flag.
|
||||
@ -662,7 +654,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
return
|
||||
|
||||
# See which score region it was.
|
||||
region = ba.get_collision_info('source_node')
|
||||
region = ba.getcollision().source_node
|
||||
i = None
|
||||
for i in range(len(self._score_regions)):
|
||||
if region == self._score_regions[i].node:
|
||||
@ -707,7 +699,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
self.update_scores()
|
||||
light = ba.newnode('light',
|
||||
attrs={
|
||||
'position': ba.get_collision_info('position'),
|
||||
'position': ba.getcollision().position,
|
||||
'height_attenuated': False,
|
||||
'color': (1, 0, 0)
|
||||
})
|
||||
@ -821,12 +813,12 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
# Augment standard behavior.
|
||||
super().handlemessage(msg)
|
||||
|
||||
elif isinstance(msg, spazbot.SpazBotDeathMessage):
|
||||
elif isinstance(msg, SpazBotDiedMessage):
|
||||
|
||||
# Every time a bad guy dies, spawn a new one.
|
||||
ba.timer(3.0, ba.Call(self._spawn_bot, (type(msg.badguy))))
|
||||
|
||||
elif isinstance(msg, spazbot.SpazBotPunchedMessage):
|
||||
elif isinstance(msg, SpazBotPunchedMessage):
|
||||
if self._preset in ['rookie', 'rookie_easy']:
|
||||
if msg.damage >= 500:
|
||||
self._award_achievement('Super Punch')
|
||||
@ -835,7 +827,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._award_achievement('Super Mega Punch')
|
||||
|
||||
# Respawn dead flags.
|
||||
elif isinstance(msg, stdflag.FlagDiedMessage):
|
||||
elif isinstance(msg, FlagDiedMessage):
|
||||
assert isinstance(msg.flag, FootballFlag)
|
||||
msg.flag.respawn_timer = ba.Timer(3.0, self._spawn_flag)
|
||||
self._flag_respawn_light = ba.NodeActor(
|
||||
|
||||
@ -28,6 +28,7 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.powerupbox import PowerupBoxFactory
|
||||
|
||||
@ -243,15 +244,10 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._update_scoreboard()
|
||||
|
||||
def _handle_puck_player_collide(self) -> None:
|
||||
try:
|
||||
pucknode, playernode = ba.get_collision_info(
|
||||
'source_node', 'opposing_node')
|
||||
puck = pucknode.getdelegate()
|
||||
player = playernode.getdelegate().getplayer()
|
||||
except Exception:
|
||||
player = puck = None
|
||||
assert isinstance(player, Player)
|
||||
assert isinstance(puck, Puck)
|
||||
collision = ba.getcollision()
|
||||
puck = collision.source_node.getdelegate(Puck)
|
||||
player = collision.opposing_node.getdelegate(PlayerSpaz,
|
||||
True).getplayer(Player)
|
||||
if player and puck:
|
||||
puck.last_players_to_touch[player.team.id] = player
|
||||
|
||||
@ -269,7 +265,7 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
|
||||
if self._puck.scored:
|
||||
return
|
||||
|
||||
region = ba.get_collision_info('source_node')
|
||||
region = ba.getcollision().source_node
|
||||
index = 0
|
||||
for index in range(len(self._score_regions)):
|
||||
if region == self._score_regions[index].node:
|
||||
@ -308,7 +304,7 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
|
||||
|
||||
light = ba.newnode('light',
|
||||
attrs={
|
||||
'position': ba.get_collision_info('position'),
|
||||
'position': ba.getcollision().position,
|
||||
'height_attenuated': False,
|
||||
'color': (1, 0, 0)
|
||||
})
|
||||
|
||||
@ -247,10 +247,8 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
|
||||
ba.playsound(self._swipsound)
|
||||
|
||||
def _handle_player_flag_region_collide(self, colliding: bool) -> None:
|
||||
delegate = ba.get_collision_info('opposing_node').getdelegate()
|
||||
if not isinstance(delegate, PlayerSpaz):
|
||||
return
|
||||
player = delegate.getplayer(Player)
|
||||
player = ba.getcollision().opposing_node.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player)
|
||||
if not player:
|
||||
return
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ import random
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.actor.spazbot import BotSet, ChargerBot, SpazBotDeathMessage
|
||||
from bastd.actor.spazbot import SpazBotSet, ChargerBot, SpazBotDiedMessage
|
||||
from bastd.actor.onscreentimer import OnScreenTimer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -76,7 +76,7 @@ class NinjaFightGame(ba.TeamGameActivity[Player, Team]):
|
||||
self._winsound = ba.getsound('score')
|
||||
self._won = False
|
||||
self._timer: Optional[OnScreenTimer] = None
|
||||
self._bots = BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
|
||||
# Called when our game is transitioning in but not ready to begin;
|
||||
# we can go ahead and start creating stuff, playing music, etc.
|
||||
@ -150,7 +150,7 @@ class NinjaFightGame(ba.TeamGameActivity[Player, Team]):
|
||||
self.respawn_player(msg.getplayer(Player))
|
||||
|
||||
# A spaz-bot has died.
|
||||
elif isinstance(msg, SpazBotDeathMessage):
|
||||
elif isinstance(msg, SpazBotDiedMessage):
|
||||
# Unfortunately the bot-set will always tell us there are living
|
||||
# bots if we ask here (the currently-dying bot isn't officially
|
||||
# marked dead yet) ..so lets push a call into the event loop to
|
||||
|
||||
@ -39,7 +39,7 @@ from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.controlsguide import ControlsGuide
|
||||
from bastd.actor.powerupbox import PowerupBox, PowerupBoxFactory
|
||||
from bastd.actor.spazbot import (
|
||||
SpazBotDeathMessage, BotSet, ChargerBot, StickyBot, BomberBot,
|
||||
SpazBotDiedMessage, SpazBotSet, ChargerBot, StickyBot, BomberBot,
|
||||
BomberBotLite, BrawlerBot, BrawlerBotLite, TriggerBot, BomberBotStaticLite,
|
||||
TriggerBotStatic, BomberBotProStatic, TriggerBotPro, ExplodeyBot,
|
||||
BrawlerBotProShielded, ChargerBotProShielded, BomberBotPro,
|
||||
@ -189,7 +189,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
raise Exception('Unsupported map: ' + str(settings['map']))
|
||||
self._scoreboard: Optional[Scoreboard] = None
|
||||
self._game_over = False
|
||||
self._wave = 0
|
||||
self._wavenum = 0
|
||||
self._can_end_wave = True
|
||||
self._score = 0
|
||||
self._time_bonus = 0
|
||||
@ -200,7 +200,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._excluded_powerups: Optional[List[str]] = None
|
||||
self._waves: List[Wave] = []
|
||||
self._tntspawner: Optional[TNTSpawner] = None
|
||||
self._bots: Optional[BotSet] = None
|
||||
self._bots: Optional[SpazBotSet] = None
|
||||
self._powerup_drop_timer: Optional[ba.Timer] = None
|
||||
self._time_bonus_timer: Optional[ba.Timer] = None
|
||||
self._time_bonus_text: Optional[ba.NodeActor] = None
|
||||
@ -214,6 +214,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
def on_transition_in(self) -> None:
|
||||
super().on_transition_in()
|
||||
session = ba.getsession()
|
||||
|
||||
# Show special landmine tip on rookie preset.
|
||||
if self._preset in {Preset.ROOKIE, Preset.ROOKIE_EASY}:
|
||||
# Show once per session only (then we revert to regular tips).
|
||||
@ -550,33 +551,28 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
|
||||
self.setup_low_life_warning_sound()
|
||||
self._update_scores()
|
||||
self._bots = BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
ba.timer(4.0, self._start_updating_waves)
|
||||
|
||||
def _on_got_scores_to_beat(self, scores: List[Dict[str, Any]]) -> None:
|
||||
self._show_standard_scores_to_beat_ui(scores)
|
||||
|
||||
def _get_dist_grp_totals(self, grps: List[Any]) -> Tuple[int, int]:
|
||||
totalpts = 0
|
||||
totaldudes = 0
|
||||
for grp in grps:
|
||||
for grpentry in grp:
|
||||
dudes = grpentry[1]
|
||||
totalpts += grpentry[0] * dudes
|
||||
totaldudes += dudes
|
||||
return totalpts, totaldudes
|
||||
|
||||
def _get_distribution(self, target_points: int, min_dudes: int,
|
||||
max_dudes: int, group_count: int,
|
||||
max_level: int) -> List[List[Tuple[int, int]]]:
|
||||
""" calculate a distribution of bad guys given some params """
|
||||
# FIXME; This method wears the cone of shame
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
"""Calculate a distribution of bad guys given some params."""
|
||||
max_iterations = 10 + max_dudes * 2
|
||||
|
||||
def _get_totals(grps: List[Any]) -> Tuple[int, int]:
|
||||
totalpts = 0
|
||||
totaldudes = 0
|
||||
for grp in grps:
|
||||
for grpentry in grp:
|
||||
dudes = grpentry[1]
|
||||
totalpts += grpentry[0] * dudes
|
||||
totaldudes += dudes
|
||||
return totalpts, totaldudes
|
||||
|
||||
groups: List[List[Tuple[int, int]]] = []
|
||||
for _g in range(group_count):
|
||||
groups.append([])
|
||||
@ -588,83 +584,29 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
if max_level > 3:
|
||||
types.append(4)
|
||||
for iteration in range(max_iterations):
|
||||
diff = self._add_dist_entry_if_possible(groups, max_dudes,
|
||||
target_points, types)
|
||||
|
||||
# See how much we're off our target by.
|
||||
total_points, total_dudes = _get_totals(groups)
|
||||
diff = target_points - total_points
|
||||
dudes_diff = max_dudes - total_dudes
|
||||
|
||||
# Add an entry if one will fit.
|
||||
value = types[random.randrange(len(types))]
|
||||
group = groups[random.randrange(len(groups))]
|
||||
if not group:
|
||||
max_count = random.randint(1, 6)
|
||||
else:
|
||||
max_count = 2 * random.randint(1, 3)
|
||||
max_count = min(max_count, dudes_diff)
|
||||
count = min(max_count, diff // value)
|
||||
if count > 0:
|
||||
group.append((value, count))
|
||||
total_points += value * count
|
||||
total_dudes += count
|
||||
diff = target_points - total_points
|
||||
|
||||
total_points, total_dudes = _get_totals(groups)
|
||||
total_points, total_dudes = self._get_dist_grp_totals(groups)
|
||||
full = (total_points >= target_points)
|
||||
|
||||
if full:
|
||||
# Every so often, delete a random entry just to
|
||||
# shake up our distribution.
|
||||
if random.random() < 0.2 and iteration != max_iterations - 1:
|
||||
entry_count = 0
|
||||
for group in groups:
|
||||
for _ in group:
|
||||
entry_count += 1
|
||||
if entry_count > 1:
|
||||
del_entry = random.randrange(entry_count)
|
||||
entry_count = 0
|
||||
for group in groups:
|
||||
for entry in group:
|
||||
if entry_count == del_entry:
|
||||
group.remove(entry)
|
||||
break
|
||||
entry_count += 1
|
||||
self._delete_random_dist_entry(groups)
|
||||
|
||||
# If we don't have enough dudes, kill the group with
|
||||
# the biggest point value.
|
||||
elif (total_dudes < min_dudes
|
||||
and iteration != max_iterations - 1):
|
||||
biggest_value = 9999
|
||||
biggest_entry = None
|
||||
biggest_entry_group = None
|
||||
for group in groups:
|
||||
for entry in group:
|
||||
if (entry[0] > biggest_value
|
||||
or biggest_entry is None):
|
||||
biggest_value = entry[0]
|
||||
biggest_entry = entry
|
||||
biggest_entry_group = group
|
||||
if biggest_entry is not None:
|
||||
assert biggest_entry_group is not None
|
||||
biggest_entry_group.remove(biggest_entry)
|
||||
self._delete_biggest_dist_entry(groups)
|
||||
|
||||
# If we've got too many dudes, kill the group with the
|
||||
# smallest point value.
|
||||
elif (total_dudes > max_dudes
|
||||
and iteration != max_iterations - 1):
|
||||
smallest_value = 9999
|
||||
smallest_entry = None
|
||||
smallest_entry_group = None
|
||||
for group in groups:
|
||||
for entry in group:
|
||||
if (entry[0] < smallest_value
|
||||
or smallest_entry is None):
|
||||
smallest_value = entry[0]
|
||||
smallest_entry = entry
|
||||
smallest_entry_group = group
|
||||
assert smallest_entry is not None
|
||||
assert smallest_entry_group is not None
|
||||
smallest_entry_group.remove(smallest_entry)
|
||||
self._delete_smallest_dist_entry(groups)
|
||||
|
||||
# Close enough.. we're done.
|
||||
else:
|
||||
@ -673,6 +615,76 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
|
||||
return groups
|
||||
|
||||
def _add_dist_entry_if_possible(self, groups: List[List[Tuple[int, int]]],
|
||||
max_dudes: int, target_points: int,
|
||||
types: List[int]) -> int:
|
||||
# See how much we're off our target by.
|
||||
total_points, total_dudes = self._get_dist_grp_totals(groups)
|
||||
diff = target_points - total_points
|
||||
dudes_diff = max_dudes - total_dudes
|
||||
|
||||
# Add an entry if one will fit.
|
||||
value = types[random.randrange(len(types))]
|
||||
group = groups[random.randrange(len(groups))]
|
||||
if not group:
|
||||
max_count = random.randint(1, 6)
|
||||
else:
|
||||
max_count = 2 * random.randint(1, 3)
|
||||
max_count = min(max_count, dudes_diff)
|
||||
count = min(max_count, diff // value)
|
||||
if count > 0:
|
||||
group.append((value, count))
|
||||
total_points += value * count
|
||||
total_dudes += count
|
||||
diff = target_points - total_points
|
||||
return diff
|
||||
|
||||
def _delete_smallest_dist_entry(
|
||||
self, groups: List[List[Tuple[int, int]]]) -> None:
|
||||
smallest_value = 9999
|
||||
smallest_entry = None
|
||||
smallest_entry_group = None
|
||||
for group in groups:
|
||||
for entry in group:
|
||||
if entry[0] < smallest_value or smallest_entry is None:
|
||||
smallest_value = entry[0]
|
||||
smallest_entry = entry
|
||||
smallest_entry_group = group
|
||||
assert smallest_entry is not None
|
||||
assert smallest_entry_group is not None
|
||||
smallest_entry_group.remove(smallest_entry)
|
||||
|
||||
def _delete_biggest_dist_entry(
|
||||
self, groups: List[List[Tuple[int, int]]]) -> None:
|
||||
biggest_value = 9999
|
||||
biggest_entry = None
|
||||
biggest_entry_group = None
|
||||
for group in groups:
|
||||
for entry in group:
|
||||
if entry[0] > biggest_value or biggest_entry is None:
|
||||
biggest_value = entry[0]
|
||||
biggest_entry = entry
|
||||
biggest_entry_group = group
|
||||
if biggest_entry is not None:
|
||||
assert biggest_entry_group is not None
|
||||
biggest_entry_group.remove(biggest_entry)
|
||||
|
||||
def _delete_random_dist_entry(self,
|
||||
groups: List[List[Tuple[int, int]]]) -> None:
|
||||
entry_count = 0
|
||||
for group in groups:
|
||||
for _ in group:
|
||||
entry_count += 1
|
||||
if entry_count > 1:
|
||||
del_entry = random.randrange(entry_count)
|
||||
entry_count = 0
|
||||
for group in groups:
|
||||
for entry in group:
|
||||
if entry_count == del_entry:
|
||||
group.remove(entry)
|
||||
break
|
||||
entry_count += 1
|
||||
|
||||
def spawn_player(self, player: Player) -> ba.Actor:
|
||||
|
||||
# We keep track of who got hurt each wave for score purposes.
|
||||
@ -734,7 +746,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
if outcome == 'defeat':
|
||||
self.fade_to_red()
|
||||
score: Optional[int]
|
||||
if self._wave >= 2:
|
||||
if self._wavenum >= 2:
|
||||
score = self._score
|
||||
fail_message = None
|
||||
else:
|
||||
@ -777,7 +789,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
if self._preset in {Preset.ENDLESS, Preset.ENDLESS_TOURNAMENT}:
|
||||
won = False
|
||||
else:
|
||||
won = (self._wave == len(self._waves))
|
||||
won = (self._wavenum == len(self._waves))
|
||||
|
||||
base_delay = 4.0 if won else 0.0
|
||||
|
||||
@ -789,7 +801,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
base_delay += 1.0
|
||||
|
||||
# Reward flawless bonus.
|
||||
if self._wave > 0:
|
||||
if self._wavenum > 0:
|
||||
have_flawless = False
|
||||
for player in self.players:
|
||||
if player.is_alive() and not player.has_been_hurt:
|
||||
@ -806,9 +818,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
scale=1.0,
|
||||
duration=4.0)
|
||||
self.celebrate(20.0)
|
||||
|
||||
self._award_completion_achievements()
|
||||
|
||||
ba.timer(base_delay, ba.WeakCall(self._award_completion_bonus))
|
||||
base_delay += 0.85
|
||||
ba.playsound(self._winsound)
|
||||
@ -822,10 +832,10 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
ba.timer(base_delay, ba.WeakCall(self.do_end, 'victory'))
|
||||
return
|
||||
|
||||
self._wave += 1
|
||||
self._wavenum += 1
|
||||
|
||||
# Short celebration after waves.
|
||||
if self._wave > 1:
|
||||
if self._wavenum > 1:
|
||||
self.celebrate(0.5)
|
||||
ba.timer(base_delay, ba.WeakCall(self._start_next_wave))
|
||||
|
||||
@ -903,17 +913,17 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
|
||||
def _respawn_players_for_wave(self) -> None:
|
||||
# Respawn applicable players.
|
||||
if self._wave > 1 and not self.is_waiting_for_continue():
|
||||
if self._wavenum > 1 and not self.is_waiting_for_continue():
|
||||
for player in self.players:
|
||||
if (not player.is_alive()
|
||||
and player.respawn_wave == self._wave):
|
||||
and player.respawn_wave == self._wavenum):
|
||||
self.spawn_player(player)
|
||||
self._update_player_spawn_info()
|
||||
|
||||
def _setup_wave_spawns(self, wave: Wave) -> None:
|
||||
tval = 0.0
|
||||
dtime = 0.2
|
||||
if self._wave == 1:
|
||||
if self._wavenum == 1:
|
||||
spawn_time = 3.973
|
||||
tval += 0.5
|
||||
else:
|
||||
@ -970,7 +980,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
if self._preset in {Preset.ENDLESS, Preset.ENDLESS_TOURNAMENT}:
|
||||
wave = self._generate_random_wave()
|
||||
else:
|
||||
wave = self._waves[self._wave - 1]
|
||||
wave = self._waves[self._wavenum - 1]
|
||||
self._setup_wave_spawns(wave)
|
||||
self._update_wave_ui_and_bonuses()
|
||||
ba.timer(0.4, ba.Call(ba.playsound, self._new_wave_sound))
|
||||
@ -980,7 +990,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
self.show_zoom_message(ba.Lstr(value='${A} ${B}',
|
||||
subs=[('${A}',
|
||||
ba.Lstr(resource='waveText')),
|
||||
('${B}', str(self._wave))]),
|
||||
('${B}', str(self._wavenum))]),
|
||||
scale=1.0,
|
||||
duration=1.0,
|
||||
trail=True)
|
||||
@ -1012,7 +1022,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
wttxt = ba.Lstr(
|
||||
value='${A} ${B}',
|
||||
subs=[('${A}', ba.Lstr(resource='waveText')),
|
||||
('${B}', str(self._wave) +
|
||||
('${B}', str(self._wavenum) +
|
||||
('' if self._preset
|
||||
in [Preset.ENDLESS, Preset.ENDLESS_TOURNAMENT] else
|
||||
('/' + str(len(self._waves)))))])
|
||||
@ -1032,7 +1042,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
}))
|
||||
|
||||
def _bot_levels_for_wave(self) -> List[List[Type[SpazBot]]]:
|
||||
level = self._wave
|
||||
level = self._wavenum
|
||||
bot_types = [
|
||||
BomberBot, BrawlerBot, TriggerBot, ChargerBot, BomberBotPro,
|
||||
BrawlerBotPro, TriggerBotPro, BomberBotProShielded, ExplodeyBot,
|
||||
@ -1067,6 +1077,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
[b for b in bot_types if b.points_mult == 2],
|
||||
[b for b in bot_types if b.points_mult == 3],
|
||||
[b for b in bot_types if b.points_mult == 4]]
|
||||
|
||||
# Make sure all lists have something in them
|
||||
if not all(bot_levels):
|
||||
raise RuntimeError('Got empty bot level')
|
||||
@ -1099,7 +1110,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
Spacing(40.0 if random.random() < 0.5 else 80.0))
|
||||
|
||||
def _generate_random_wave(self) -> Wave:
|
||||
level = self._wave
|
||||
level = self._wavenum
|
||||
bot_levels = self._bot_levels_for_wave()
|
||||
|
||||
target_points = level * 3 - 2
|
||||
@ -1199,20 +1210,19 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._a_player_has_been_hurt = True
|
||||
|
||||
# Make note with the player when they can respawn:
|
||||
if self._wave < 10:
|
||||
player.respawn_wave = max(2, self._wave + 1)
|
||||
elif self._wave < 15:
|
||||
player.respawn_wave = max(2, self._wave + 2)
|
||||
if self._wavenum < 10:
|
||||
player.respawn_wave = max(2, self._wavenum + 1)
|
||||
elif self._wavenum < 15:
|
||||
player.respawn_wave = max(2, self._wavenum + 2)
|
||||
else:
|
||||
player.respawn_wave = max(2, self._wave + 3)
|
||||
player.respawn_wave = max(2, self._wavenum + 3)
|
||||
ba.timer(0.1, self._update_player_spawn_info)
|
||||
ba.timer(0.1, self._checkroundover)
|
||||
|
||||
elif isinstance(msg, SpazBotDeathMessage):
|
||||
elif isinstance(msg, SpazBotDiedMessage):
|
||||
pts, importance = msg.badguy.get_death_points(msg.how)
|
||||
if msg.killerplayer is not None:
|
||||
self._handle_kill_achievements(msg)
|
||||
|
||||
target: Optional[Sequence[float]]
|
||||
try:
|
||||
assert msg.badguy.node
|
||||
@ -1242,47 +1252,57 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
else:
|
||||
super().handlemessage(msg)
|
||||
|
||||
def _handle_kill_achievements(self, msg: SpazBotDeathMessage) -> None:
|
||||
# pylint: disable=too-many-branches
|
||||
# Toss-off-map achievement:
|
||||
def _handle_kill_achievements(self, msg: SpazBotDiedMessage) -> None:
|
||||
if self._preset in {Preset.TRAINING, Preset.TRAINING_EASY}:
|
||||
if msg.badguy.last_attacked_type == ('picked_up', 'default'):
|
||||
self._throw_off_kills += 1
|
||||
if self._throw_off_kills >= 3:
|
||||
self._award_achievement('Off You Go Then')
|
||||
|
||||
# Land-mine achievement:
|
||||
self._handle_training_kill_achievements(msg)
|
||||
elif self._preset in {Preset.ROOKIE, Preset.ROOKIE_EASY}:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'land_mine'):
|
||||
self._land_mine_kills += 1
|
||||
if self._land_mine_kills >= 3:
|
||||
self._award_achievement('Mine Games')
|
||||
self._handle_rookie_kill_achievements(msg)
|
||||
elif self._preset in {Preset.PRO, Preset.PRO_EASY}:
|
||||
self._handle_pro_kill_achievements(msg)
|
||||
elif self._preset in {Preset.UBER, Preset.UBER_EASY}:
|
||||
self._handle_uber_kill_achievements(msg)
|
||||
|
||||
def _handle_uber_kill_achievements(self, msg: SpazBotDiedMessage) -> None:
|
||||
|
||||
# Uber mine achievement:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'land_mine'):
|
||||
self._land_mine_kills += 1
|
||||
if self._land_mine_kills >= 6:
|
||||
self._award_achievement('Gold Miner')
|
||||
|
||||
# Uber tnt achievement:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'tnt'):
|
||||
self._tnt_kills += 1
|
||||
if self._tnt_kills >= 6:
|
||||
ba.timer(0.5, ba.WeakCall(self._award_achievement,
|
||||
'TNT Terror'))
|
||||
|
||||
def _handle_pro_kill_achievements(self, msg: SpazBotDiedMessage) -> None:
|
||||
|
||||
# TNT achievement:
|
||||
elif self._preset in {Preset.PRO, Preset.PRO_EASY}:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'tnt'):
|
||||
self._tnt_kills += 1
|
||||
if self._tnt_kills >= 3:
|
||||
ba.timer(
|
||||
0.5,
|
||||
ba.WeakCall(self._award_achievement,
|
||||
'Boom Goes the Dynamite'))
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'tnt'):
|
||||
self._tnt_kills += 1
|
||||
if self._tnt_kills >= 3:
|
||||
ba.timer(
|
||||
0.5,
|
||||
ba.WeakCall(self._award_achievement,
|
||||
'Boom Goes the Dynamite'))
|
||||
|
||||
elif self._preset in {Preset.UBER, Preset.UBER_EASY}:
|
||||
def _handle_rookie_kill_achievements(self,
|
||||
msg: SpazBotDiedMessage) -> None:
|
||||
# Land-mine achievement:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'land_mine'):
|
||||
self._land_mine_kills += 1
|
||||
if self._land_mine_kills >= 3:
|
||||
self._award_achievement('Mine Games')
|
||||
|
||||
# Uber mine achievement:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'land_mine'):
|
||||
self._land_mine_kills += 1
|
||||
if self._land_mine_kills >= 6:
|
||||
self._award_achievement('Gold Miner')
|
||||
|
||||
# Uber tnt achievement:
|
||||
if msg.badguy.last_attacked_type == ('explosion', 'tnt'):
|
||||
self._tnt_kills += 1
|
||||
if self._tnt_kills >= 6:
|
||||
ba.timer(
|
||||
0.5, ba.WeakCall(self._award_achievement,
|
||||
'TNT Terror'))
|
||||
def _handle_training_kill_achievements(self,
|
||||
msg: SpazBotDiedMessage) -> None:
|
||||
# Toss-off-map achievement:
|
||||
if msg.badguy.last_attacked_type == ('picked_up', 'default'):
|
||||
self._throw_off_kills += 1
|
||||
if self._throw_off_kills >= 3:
|
||||
self._award_achievement('Off You Go Then')
|
||||
|
||||
def _set_can_end_wave(self) -> None:
|
||||
self._can_end_wave = True
|
||||
@ -1308,7 +1328,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
|
||||
return
|
||||
if not any(player.is_alive() for player in self.teams[0].players):
|
||||
# Allow continuing after wave 1.
|
||||
if self._wave > 1:
|
||||
if self._wavenum > 1:
|
||||
self.continue_or_end_game()
|
||||
else:
|
||||
self.end_game()
|
||||
|
||||
@ -226,17 +226,13 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
region_node, playernode = ba.get_collision_info(
|
||||
'source_node', 'opposing_node')
|
||||
try:
|
||||
player = playernode.getdelegate().getplayer()
|
||||
except Exception:
|
||||
player = None
|
||||
region = region_node.getdelegate()
|
||||
collision = ba.getcollision()
|
||||
region = collision.source_node.getdelegate(RaceRegion)
|
||||
playerspaz = collision.opposing_node.getdelegate(PlayerSpaz,
|
||||
doraise=False)
|
||||
player = playerspaz.getplayer(Player) if playerspaz else None
|
||||
if not player or not region:
|
||||
return
|
||||
assert isinstance(player, Player)
|
||||
assert isinstance(region, RaceRegion)
|
||||
|
||||
last_region = player.last_region
|
||||
this_region = region.index
|
||||
|
||||
@ -29,11 +29,16 @@ import random
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.actor import spazbot
|
||||
from bastd.actor.popuptext import PopupText
|
||||
from bastd.actor.bomb import TNTSpawner
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.respawnicon import RespawnIcon
|
||||
from bastd.actor.powerupbox import PowerupBox, PowerupBoxFactory
|
||||
from bastd.actor.spazbot import (
|
||||
SpazBotSet, SpazBot, SpazBotDiedMessage, BomberBot, BrawlerBot, TriggerBot,
|
||||
TriggerBotPro, BomberBotProShielded, TriggerBotProShielded, ChargerBot,
|
||||
ChargerBotProShielded, StickyBot, ExplodeyBot, BrawlerBotProShielded,
|
||||
BomberBotPro, BrawlerBotPro)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type, Any, List, Dict, Tuple, Sequence, Optional
|
||||
@ -57,22 +62,23 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
'No, you can\'t get up on the ledge. You have to throw bombs.',
|
||||
'Whip back and forth to get more distance on your throws..'
|
||||
]
|
||||
default_music = ba.MusicType.MARCHING
|
||||
|
||||
# How fast our various bot types walk.
|
||||
_bot_speed_map: Dict[Type[spazbot.SpazBot], float] = {
|
||||
spazbot.BomberBot: 0.48,
|
||||
spazbot.BomberBotPro: 0.48,
|
||||
spazbot.BomberBotProShielded: 0.48,
|
||||
spazbot.BrawlerBot: 0.57,
|
||||
spazbot.BrawlerBotPro: 0.57,
|
||||
spazbot.BrawlerBotProShielded: 0.57,
|
||||
spazbot.TriggerBot: 0.73,
|
||||
spazbot.TriggerBotPro: 0.78,
|
||||
spazbot.TriggerBotProShielded: 0.78,
|
||||
spazbot.ChargerBot: 1.0,
|
||||
spazbot.ChargerBotProShielded: 1.0,
|
||||
spazbot.ExplodeyBot: 1.0,
|
||||
spazbot.StickyBot: 0.5
|
||||
_bot_speed_map: Dict[Type[SpazBot], float] = {
|
||||
BomberBot: 0.48,
|
||||
BomberBotPro: 0.48,
|
||||
BomberBotProShielded: 0.48,
|
||||
BrawlerBot: 0.57,
|
||||
BrawlerBotPro: 0.57,
|
||||
BrawlerBotProShielded: 0.57,
|
||||
TriggerBot: 0.73,
|
||||
TriggerBotPro: 0.78,
|
||||
TriggerBotProShielded: 0.78,
|
||||
ChargerBot: 1.0,
|
||||
ChargerBotProShielded: 1.0,
|
||||
ExplodeyBot: 1.0,
|
||||
StickyBot: 0.5
|
||||
}
|
||||
|
||||
def __init__(self, settings: Dict[str, Any]):
|
||||
@ -108,7 +114,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._player_has_picked_up_powerup = False
|
||||
self._scoreboard: Optional[Scoreboard] = None
|
||||
self._game_over = False
|
||||
self._wave = 0
|
||||
self._wavenum = 0
|
||||
self._can_end_wave = True
|
||||
self._score = 0
|
||||
self._time_bonus = 0
|
||||
@ -118,7 +124,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._exclude_powerups: Optional[List[str]] = None
|
||||
self._have_tnt: Optional[bool] = None
|
||||
self._waves: Optional[List[Dict[str, Any]]] = None
|
||||
self._bots = spazbot.BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
self._tntspawner: Optional[TNTSpawner] = None
|
||||
self._lives_bg: Optional[ba.NodeActor] = None
|
||||
self._start_lives = 10
|
||||
@ -133,7 +139,6 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._wave_update_timer: Optional[ba.Timer] = None
|
||||
|
||||
def on_transition_in(self) -> None:
|
||||
self.default_music = ba.MusicType.MARCHING
|
||||
super().on_transition_in()
|
||||
self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'),
|
||||
score_split=0.5)
|
||||
@ -157,110 +162,110 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._have_tnt = True
|
||||
self._waves = [
|
||||
{'entries': [
|
||||
{'type': spazbot.BomberBot, 'path': 3 if hard else 2},
|
||||
{'type': spazbot.BomberBot, 'path': 2},
|
||||
{'type': spazbot.BomberBot, 'path': 2} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 2} if player_count > 1
|
||||
{'type': BomberBot, 'path': 3 if hard else 2},
|
||||
{'type': BomberBot, 'path': 2},
|
||||
{'type': BomberBot, 'path': 2} if hard else None,
|
||||
{'type': BomberBot, 'path': 2} if player_count > 1
|
||||
else None,
|
||||
{'type': spazbot.BomberBot, 'path': 1} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 1} if player_count > 2
|
||||
{'type': BomberBot, 'path': 1} if hard else None,
|
||||
{'type': BomberBot, 'path': 1} if player_count > 2
|
||||
else None,
|
||||
{'type': spazbot.BomberBot, 'path': 1} if player_count > 3
|
||||
{'type': BomberBot, 'path': 1} if player_count > 3
|
||||
else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.BomberBot, 'path': 1} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 2} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 2},
|
||||
{'type': spazbot.BomberBot, 'path': 2},
|
||||
{'type': spazbot.BomberBot, 'path': 2} if player_count > 3
|
||||
{'type': BomberBot, 'path': 1} if hard else None,
|
||||
{'type': BomberBot, 'path': 2} if hard else None,
|
||||
{'type': BomberBot, 'path': 2},
|
||||
{'type': BomberBot, 'path': 2},
|
||||
{'type': BomberBot, 'path': 2} if player_count > 3
|
||||
else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 3},
|
||||
{'type': spazbot.BrawlerBot, 'path': 3},
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if hard else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if player_count > 1
|
||||
{'type': BrawlerBot, 'path': 3},
|
||||
{'type': BrawlerBot, 'path': 3},
|
||||
{'type': BrawlerBot, 'path': 3} if hard else None,
|
||||
{'type': BrawlerBot, 'path': 3} if player_count > 1
|
||||
else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if player_count > 2
|
||||
{'type': BrawlerBot, 'path': 3} if player_count > 2
|
||||
else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.ChargerBot, 'path': 2} if hard else None,
|
||||
{'type': spazbot.ChargerBot, 'path': 2} if player_count > 2
|
||||
{'type': ChargerBot, 'path': 2} if hard else None,
|
||||
{'type': ChargerBot, 'path': 2} if player_count > 2
|
||||
else None,
|
||||
{'type': spazbot.TriggerBot, 'path': 2},
|
||||
{'type': spazbot.TriggerBot, 'path': 2} if player_count > 1
|
||||
{'type': TriggerBot, 'path': 2},
|
||||
{'type': TriggerBot, 'path': 2} if player_count > 1
|
||||
else None,
|
||||
{'type': 'spacing', 'duration': 3.0},
|
||||
{'type': spazbot.BomberBot, 'path': 2} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 2} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 2},
|
||||
{'type': spazbot.BomberBot, 'path': 3} if hard else None,
|
||||
{'type': spazbot.BomberBot, 'path': 3},
|
||||
{'type': spazbot.BomberBot, 'path': 3},
|
||||
{'type': spazbot.BomberBot, 'path': 3} if player_count > 3
|
||||
{'type': BomberBot, 'path': 2} if hard else None,
|
||||
{'type': BomberBot, 'path': 2} if hard else None,
|
||||
{'type': BomberBot, 'path': 2},
|
||||
{'type': BomberBot, 'path': 3} if hard else None,
|
||||
{'type': BomberBot, 'path': 3},
|
||||
{'type': BomberBot, 'path': 3},
|
||||
{'type': BomberBot, 'path': 3} if player_count > 3
|
||||
else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': 'spacing', 'duration': 1.0} if hard else None,
|
||||
{'type': spazbot.TriggerBot, 'path': 2},
|
||||
{'type': TriggerBot, 'path': 2},
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 3},
|
||||
{'type': TriggerBot, 'path': 3},
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': 'spacing', 'duration': 1.0} if hard else None,
|
||||
{'type': spazbot.TriggerBot, 'path': 2},
|
||||
{'type': TriggerBot, 'path': 2},
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 3},
|
||||
{'type': TriggerBot, 'path': 3},
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 1}
|
||||
{'type': TriggerBot, 'path': 1}
|
||||
if (player_count > 1 and hard) else None,
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 2} if player_count > 2
|
||||
{'type': TriggerBot, 'path': 2} if player_count > 2
|
||||
else None,
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 3} if player_count > 3
|
||||
{'type': TriggerBot, 'path': 3} if player_count > 3
|
||||
else None,
|
||||
{'type': 'spacing', 'duration': 1.0},
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.ChargerBotProShielded if hard
|
||||
else spazbot.ChargerBot, 'path': 1},
|
||||
{'type': spazbot.BrawlerBot, 'path': 2} if hard else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 2},
|
||||
{'type': spazbot.BrawlerBot, 'path': 2},
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if hard else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 3},
|
||||
{'type': spazbot.BrawlerBot, 'path': 3},
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if player_count > 1
|
||||
{'type': ChargerBotProShielded if hard
|
||||
else ChargerBot, 'path': 1},
|
||||
{'type': BrawlerBot, 'path': 2} if hard else None,
|
||||
{'type': BrawlerBot, 'path': 2},
|
||||
{'type': BrawlerBot, 'path': 2},
|
||||
{'type': BrawlerBot, 'path': 3} if hard else None,
|
||||
{'type': BrawlerBot, 'path': 3},
|
||||
{'type': BrawlerBot, 'path': 3},
|
||||
{'type': BrawlerBot, 'path': 3} if player_count > 1
|
||||
else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if player_count > 2
|
||||
{'type': BrawlerBot, 'path': 3} if player_count > 2
|
||||
else None,
|
||||
{'type': spazbot.BrawlerBot, 'path': 3} if player_count > 3
|
||||
{'type': BrawlerBot, 'path': 3} if player_count > 3
|
||||
else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 3},
|
||||
{'type': BomberBotProShielded, 'path': 3},
|
||||
{'type': 'spacing', 'duration': 1.5},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': 'spacing', 'duration': 1.5},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 1} if hard
|
||||
{'type': BomberBotProShielded, 'path': 1} if hard
|
||||
else None,
|
||||
{'type': 'spacing', 'duration': 1.0} if hard else None,
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 3},
|
||||
{'type': BomberBotProShielded, 'path': 3},
|
||||
{'type': 'spacing', 'duration': 1.5},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': 'spacing', 'duration': 1.5},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 1} if hard
|
||||
{'type': BomberBotProShielded, 'path': 1} if hard
|
||||
else None,
|
||||
{'type': 'spacing', 'duration': 1.5} if hard else None,
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 3}
|
||||
{'type': BomberBotProShielded, 'path': 3}
|
||||
if player_count > 1 else None,
|
||||
{'type': 'spacing', 'duration': 1.5},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2}
|
||||
{'type': BomberBotProShielded, 'path': 2}
|
||||
if player_count > 2 else None,
|
||||
{'type': 'spacing', 'duration': 1.5},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 1}
|
||||
{'type': BomberBotProShielded, 'path': 1}
|
||||
if player_count > 3 else None,
|
||||
]},
|
||||
] # yapf: disable
|
||||
@ -269,84 +274,84 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._have_tnt = True
|
||||
self._waves = [
|
||||
{'entries': [
|
||||
{'type': spazbot.TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': spazbot.TriggerBot, 'path': 2},
|
||||
{'type': spazbot.TriggerBot, 'path': 2},
|
||||
{'type': spazbot.TriggerBot, 'path': 3},
|
||||
{'type': spazbot.BrawlerBotPro if hard
|
||||
else spazbot.BrawlerBot, 'point': 'bottom_left'},
|
||||
{'type': spazbot.BrawlerBotPro, 'point': 'bottom_right'}
|
||||
{'type': TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': TriggerBot, 'path': 2},
|
||||
{'type': TriggerBot, 'path': 2},
|
||||
{'type': TriggerBot, 'path': 3},
|
||||
{'type': BrawlerBotPro if hard
|
||||
else BrawlerBot, 'point': 'bottom_left'},
|
||||
{'type': BrawlerBotPro, 'point': 'bottom_right'}
|
||||
if player_count > 2 else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.ChargerBot, 'path': 2},
|
||||
{'type': spazbot.ChargerBot, 'path': 3},
|
||||
{'type': spazbot.ChargerBot, 'path': 1} if hard else None,
|
||||
{'type': spazbot.ChargerBot, 'path': 2},
|
||||
{'type': spazbot.ChargerBot, 'path': 3},
|
||||
{'type': spazbot.ChargerBot, 'path': 1} if player_count > 2
|
||||
{'type': ChargerBot, 'path': 2},
|
||||
{'type': ChargerBot, 'path': 3},
|
||||
{'type': ChargerBot, 'path': 1} if hard else None,
|
||||
{'type': ChargerBot, 'path': 2},
|
||||
{'type': ChargerBot, 'path': 3},
|
||||
{'type': ChargerBot, 'path': 1} if player_count > 2
|
||||
else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 1} if hard
|
||||
{'type': BomberBotProShielded, 'path': 1} if hard
|
||||
else None,
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 3},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 3},
|
||||
{'type': spazbot.ChargerBot, 'point': 'bottom_right'},
|
||||
{'type': spazbot.ChargerBot, 'point': 'bottom_left'}
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 3},
|
||||
{'type': BomberBotProShielded, 'path': 3},
|
||||
{'type': ChargerBot, 'point': 'bottom_right'},
|
||||
{'type': ChargerBot, 'point': 'bottom_left'}
|
||||
if player_count > 2 else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1}
|
||||
{'type': TriggerBotPro, 'path': 1}
|
||||
if hard else None,
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2}
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2},
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2}
|
||||
if player_count > 1 else None,
|
||||
{'type': spazbot.TriggerBotPro, 'path': 1 if hard else 2}
|
||||
{'type': TriggerBotPro, 'path': 1 if hard else 2}
|
||||
if player_count > 3 else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.TriggerBotProShielded if hard
|
||||
else spazbot.TriggerBotPro, 'point': 'bottom_left'},
|
||||
{'type': spazbot.TriggerBotProShielded,
|
||||
{'type': TriggerBotProShielded if hard
|
||||
else TriggerBotPro, 'point': 'bottom_left'},
|
||||
{'type': TriggerBotProShielded,
|
||||
'point': 'bottom_right'}
|
||||
if hard else None,
|
||||
{'type': spazbot.TriggerBotProShielded,
|
||||
{'type': TriggerBotProShielded,
|
||||
'point': 'bottom_right'}
|
||||
if player_count > 2 else None,
|
||||
{'type': spazbot.BomberBot, 'path': 3},
|
||||
{'type': spazbot.BomberBot, 'path': 3},
|
||||
{'type': BomberBot, 'path': 3},
|
||||
{'type': BomberBot, 'path': 3},
|
||||
{'type': 'spacing', 'duration': 5.0},
|
||||
{'type': spazbot.BrawlerBot, 'path': 2},
|
||||
{'type': spazbot.BrawlerBot, 'path': 2},
|
||||
{'type': BrawlerBot, 'path': 2},
|
||||
{'type': BrawlerBot, 'path': 2},
|
||||
{'type': 'spacing', 'duration': 5.0},
|
||||
{'type': spazbot.TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': spazbot.TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': TriggerBot, 'path': 1} if hard else None,
|
||||
{'type': TriggerBot, 'path': 1} if hard else None,
|
||||
]},
|
||||
{'entries': [
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2} if hard
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 2} if hard
|
||||
else None,
|
||||
{'type': spazbot.StickyBot, 'point': 'bottom_right'},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.StickyBot, 'point': 'bottom_right'}
|
||||
{'type': StickyBot, 'point': 'bottom_right'},
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': StickyBot, 'point': 'bottom_right'}
|
||||
if player_count > 2 else None,
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.ExplodeyBot, 'point': 'bottom_left'},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2},
|
||||
{'type': spazbot.BomberBotProShielded, 'path': 2}
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': ExplodeyBot, 'point': 'bottom_left'},
|
||||
{'type': BomberBotProShielded, 'path': 2},
|
||||
{'type': BomberBotProShielded, 'path': 2}
|
||||
if player_count > 1 else None,
|
||||
{'type': 'spacing', 'duration': 5.0},
|
||||
{'type': spazbot.StickyBot, 'point': 'bottom_left'},
|
||||
{'type': StickyBot, 'point': 'bottom_left'},
|
||||
{'type': 'spacing', 'duration': 2.0},
|
||||
{'type': spazbot.ExplodeyBot, 'point': 'bottom_right'},
|
||||
{'type': ExplodeyBot, 'point': 'bottom_right'},
|
||||
]},
|
||||
] # yapf: disable
|
||||
elif self._preset in ['endless', 'endless_tournament']:
|
||||
@ -401,9 +406,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
ba.timer(2.0, self._start_updating_waves)
|
||||
|
||||
def _handle_reached_end(self) -> None:
|
||||
oppnode = ba.get_collision_info('opposing_node')
|
||||
spaz = oppnode.getdelegate()
|
||||
|
||||
spaz = ba.getcollision().opposing_node.getdelegate(SpazBot, True)
|
||||
if not spaz.is_alive():
|
||||
return # Ignore bodies flying in.
|
||||
|
||||
@ -495,7 +498,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
def _drop_powerups(self,
|
||||
standard_points: bool = False,
|
||||
force_first: str = None) -> None:
|
||||
""" Generic powerup drop """
|
||||
"""Generic powerup drop."""
|
||||
|
||||
# If its been a minute since our last wave finished emerging, stop
|
||||
# giving out land-mine powerups. (prevents players from waiting
|
||||
@ -528,14 +531,6 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
extra_excludes)).autoretain()
|
||||
|
||||
def end_game(self) -> None:
|
||||
|
||||
# FIXME: If we don't start our bots moving again we get stuck. This
|
||||
# is because the bot-set never prunes itself while movement is off
|
||||
# and on_expire() never gets called for some bots because
|
||||
# _prune_dead_objects() saw them as dead and pulled them off the
|
||||
# weak-ref lists. this is an architectural issue; can hopefully fix
|
||||
# this by having _actor_weak_refs not look at exists().
|
||||
self._bots.start_moving()
|
||||
ba.pushcall(ba.Call(self.do_end, 'defeat'))
|
||||
ba.setmusic(None)
|
||||
ba.playsound(self._player_death_sound)
|
||||
@ -550,7 +545,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
delay = 0
|
||||
|
||||
score: Optional[int]
|
||||
if self._wave >= 2:
|
||||
if self._wavenum >= 2:
|
||||
score = self._score
|
||||
fail_message = None
|
||||
else:
|
||||
@ -583,7 +578,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
won = False
|
||||
else:
|
||||
assert self._waves is not None
|
||||
won = (self._wave == len(self._waves))
|
||||
won = (self._wavenum == len(self._waves))
|
||||
|
||||
# Reward time bonus.
|
||||
base_delay = 4.0 if won else 0
|
||||
@ -594,7 +589,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
base_delay += 1.0
|
||||
|
||||
# Reward flawless bonus.
|
||||
if self._wave > 0 and self._flawless:
|
||||
if self._wavenum > 0 and self._flawless:
|
||||
ba.timer(base_delay, self._award_flawless_bonus)
|
||||
base_delay += 1.0
|
||||
|
||||
@ -636,69 +631,60 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
ba.timer(base_delay, ba.Call(self.do_end, 'victory'))
|
||||
return
|
||||
|
||||
self._wave += 1
|
||||
self._wavenum += 1
|
||||
|
||||
# Short celebration after waves.
|
||||
if self._wave > 1:
|
||||
if self._wavenum > 1:
|
||||
self.celebrate(0.5)
|
||||
|
||||
ba.timer(base_delay, self._start_next_wave)
|
||||
|
||||
def _award_completion_bonus(self) -> None:
|
||||
from bastd.actor import popuptext
|
||||
bonus = 200
|
||||
ba.playsound(self._cashregistersound)
|
||||
popuptext.PopupText(ba.Lstr(
|
||||
value='+${A} ${B}',
|
||||
subs=[('${A}', str(bonus)),
|
||||
('${B}', ba.Lstr(resource='completionBonusText'))]),
|
||||
color=(0.7, 0.7, 1.0, 1),
|
||||
scale=1.6,
|
||||
position=(0, 1.5, -1)).autoretain()
|
||||
PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(bonus)),
|
||||
('${B}',
|
||||
ba.Lstr(resource='completionBonusText'))]),
|
||||
color=(0.7, 0.7, 1.0, 1),
|
||||
scale=1.6,
|
||||
position=(0, 1.5, -1)).autoretain()
|
||||
self._score += bonus
|
||||
self._update_scores()
|
||||
|
||||
def _award_lives_bonus(self) -> None:
|
||||
from bastd.actor import popuptext
|
||||
bonus = self._lives * 30
|
||||
ba.playsound(self._cashregistersound)
|
||||
popuptext.PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(bonus)),
|
||||
('${B}',
|
||||
ba.Lstr(resource='livesBonusText'))
|
||||
]),
|
||||
color=(0.7, 1.0, 0.3, 1),
|
||||
scale=1.3,
|
||||
position=(0, 1, -1)).autoretain()
|
||||
PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(bonus)),
|
||||
('${B}', ba.Lstr(resource='livesBonusText'))]),
|
||||
color=(0.7, 1.0, 0.3, 1),
|
||||
scale=1.3,
|
||||
position=(0, 1, -1)).autoretain()
|
||||
self._score += bonus
|
||||
self._update_scores()
|
||||
|
||||
def _award_time_bonus(self, bonus: int) -> None:
|
||||
from bastd.actor import popuptext
|
||||
ba.playsound(self._cashregistersound)
|
||||
popuptext.PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(bonus)),
|
||||
('${B}',
|
||||
ba.Lstr(resource='timeBonusText'))
|
||||
]),
|
||||
color=(1, 1, 0.5, 1),
|
||||
scale=1.0,
|
||||
position=(0, 3, -1)).autoretain()
|
||||
PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(bonus)),
|
||||
('${B}', ba.Lstr(resource='timeBonusText'))]),
|
||||
color=(1, 1, 0.5, 1),
|
||||
scale=1.0,
|
||||
position=(0, 3, -1)).autoretain()
|
||||
|
||||
self._score += self._time_bonus
|
||||
self._update_scores()
|
||||
|
||||
def _award_flawless_bonus(self) -> None:
|
||||
from bastd.actor import popuptext
|
||||
ba.playsound(self._cashregistersound)
|
||||
popuptext.PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(self._flawless_bonus)),
|
||||
('${B}',
|
||||
ba.Lstr(resource='perfectWaveText'))
|
||||
]),
|
||||
color=(1, 1, 0.2, 1),
|
||||
scale=1.2,
|
||||
position=(0, 2, -1)).autoretain()
|
||||
PopupText(ba.Lstr(value='+${A} ${B}',
|
||||
subs=[('${A}', str(self._flawless_bonus)),
|
||||
('${B}', ba.Lstr(resource='perfectWaveText'))
|
||||
]),
|
||||
color=(1, 1, 0.2, 1),
|
||||
scale=1.2,
|
||||
position=(0, 2, -1)).autoretain()
|
||||
|
||||
assert self._flawless_bonus is not None
|
||||
self._score += self._flawless_bonus
|
||||
@ -717,7 +703,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self.show_zoom_message(ba.Lstr(value='${A} ${B}',
|
||||
subs=[('${A}',
|
||||
ba.Lstr(resource='waveText')),
|
||||
('${B}', str(self._wave))]),
|
||||
('${B}', str(self._wavenum))]),
|
||||
scale=1.0,
|
||||
duration=1.0,
|
||||
trail=True)
|
||||
@ -728,52 +714,49 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
bot_types: List[Optional[Dict[str, Any]]] = []
|
||||
|
||||
if self._preset in ['endless', 'endless_tournament']:
|
||||
level = self._wave
|
||||
level = self._wavenum
|
||||
target_points = (level + 1) * 8.0
|
||||
group_count = random.randint(1, 3)
|
||||
entries = []
|
||||
spaz_types: List[Tuple[Type[spazbot.SpazBot], float]] = []
|
||||
spaz_types: List[Tuple[Type[SpazBot], float]] = []
|
||||
if level < 6:
|
||||
spaz_types += [(spazbot.BomberBot, 5.0)]
|
||||
spaz_types += [(BomberBot, 5.0)]
|
||||
if level < 10:
|
||||
spaz_types += [(spazbot.BrawlerBot, 5.0)]
|
||||
spaz_types += [(BrawlerBot, 5.0)]
|
||||
if level < 15:
|
||||
spaz_types += [(spazbot.TriggerBot, 6.0)]
|
||||
spaz_types += [(TriggerBot, 6.0)]
|
||||
if level > 5:
|
||||
spaz_types += [(spazbot.TriggerBotPro, 7.5)
|
||||
] * (1 + (level - 5) // 7)
|
||||
spaz_types += [(TriggerBotPro, 7.5)] * (1 + (level - 5) // 7)
|
||||
if level > 2:
|
||||
spaz_types += [(spazbot.BomberBotProShielded, 8.0)
|
||||
spaz_types += [(BomberBotProShielded, 8.0)
|
||||
] * (1 + (level - 2) // 6)
|
||||
if level > 6:
|
||||
spaz_types += [(spazbot.TriggerBotProShielded, 12.0)
|
||||
spaz_types += [(TriggerBotProShielded, 12.0)
|
||||
] * (1 + (level - 6) // 5)
|
||||
if level > 1:
|
||||
spaz_types += ([(spazbot.ChargerBot, 10.0)] *
|
||||
(1 + (level - 1) // 4))
|
||||
spaz_types += ([(ChargerBot, 10.0)] * (1 + (level - 1) // 4))
|
||||
if level > 7:
|
||||
spaz_types += [(spazbot.ChargerBotProShielded, 15.0)
|
||||
spaz_types += [(ChargerBotProShielded, 15.0)
|
||||
] * (1 + (level - 7) // 3)
|
||||
|
||||
# Bot type, their effect on target points.
|
||||
defender_types: List[Tuple[Type[spazbot.SpazBot], float]] = [
|
||||
(spazbot.BomberBot, 0.9),
|
||||
(spazbot.BrawlerBot, 0.9),
|
||||
(spazbot.TriggerBot, 0.85),
|
||||
defender_types: List[Tuple[Type[SpazBot], float]] = [
|
||||
(BomberBot, 0.9),
|
||||
(BrawlerBot, 0.9),
|
||||
(TriggerBot, 0.85),
|
||||
]
|
||||
if level > 2:
|
||||
defender_types += [(spazbot.ChargerBot, 0.75)]
|
||||
defender_types += [(ChargerBot, 0.75)]
|
||||
if level > 4:
|
||||
defender_types += ([(spazbot.StickyBot, 0.7)] *
|
||||
(1 + (level - 5) // 6))
|
||||
defender_types += ([(StickyBot, 0.7)] * (1 + (level - 5) // 6))
|
||||
if level > 6:
|
||||
defender_types += ([(spazbot.ExplodeyBot, 0.7)] *
|
||||
(1 + (level - 5) // 5))
|
||||
defender_types += ([(ExplodeyBot, 0.7)] * (1 +
|
||||
(level - 5) // 5))
|
||||
if level > 8:
|
||||
defender_types += ([(spazbot.BrawlerBotProShielded, 0.65)] *
|
||||
defender_types += ([(BrawlerBotProShielded, 0.65)] *
|
||||
(1 + (level - 5) // 4))
|
||||
if level > 10:
|
||||
defender_types += ([(spazbot.TriggerBotProShielded, 0.6)] *
|
||||
defender_types += ([(TriggerBotProShielded, 0.6)] *
|
||||
(1 + (level - 6) // 3))
|
||||
|
||||
for group in range(group_count):
|
||||
@ -821,8 +804,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
elif path == 6:
|
||||
this_target_point_s *= 0.7
|
||||
|
||||
def _add_defender(defender_type: Tuple[Type[spazbot.SpazBot],
|
||||
float],
|
||||
def _add_defender(defender_type: Tuple[Type[SpazBot], float],
|
||||
pnt: str) -> Tuple[float, Dict[str, Any]]:
|
||||
# FIXME: should look into this warning
|
||||
# pylint: disable=cell-var-from-loop
|
||||
@ -884,7 +866,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
|
||||
else:
|
||||
assert self._waves is not None
|
||||
wave = self._waves[self._wave - 1]
|
||||
wave = self._waves[self._wavenum - 1]
|
||||
|
||||
bot_types += wave['entries']
|
||||
self._time_bonus_mult = 1.0
|
||||
@ -965,15 +947,13 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
# dropping land-mines powerups at some point (otherwise a crafty
|
||||
# player could fill the whole map with them)
|
||||
self._last_wave_end_time = ba.time() + t_sec
|
||||
assert self._waves is not None
|
||||
totalwaves = str(len(self._waves)) if self._waves is not None else '??'
|
||||
txtval = ba.Lstr(
|
||||
value='${A} ${B}',
|
||||
subs=[
|
||||
('${A}', ba.Lstr(resource='waveText')),
|
||||
('${B}', str(self._wave) +
|
||||
('' if self._preset in ['endless', 'endless_tournament'] else
|
||||
('/' + str(len(self._waves)))))
|
||||
])
|
||||
subs=[('${A}', ba.Lstr(resource='waveText')),
|
||||
('${B}', str(self._wavenum) +
|
||||
('' if self._preset in ['endless', 'endless_tournament']
|
||||
else f'/{totalwaves}'))])
|
||||
self._wave_text = ba.NodeActor(
|
||||
ba.newnode('text',
|
||||
attrs={
|
||||
@ -989,16 +969,16 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
'text': txtval
|
||||
}))
|
||||
|
||||
# noinspection PyTypeHints
|
||||
def _on_bot_spawn(self, path: int, spaz: spazbot.SpazBot) -> None:
|
||||
def _on_bot_spawn(self, path: int, spaz: SpazBot) -> None:
|
||||
|
||||
# Add our custom update callback and set some info for this bot.
|
||||
spaz_type = type(spaz)
|
||||
assert spaz is not None
|
||||
spaz.update_callback = self._update_bot
|
||||
|
||||
# FIXME: Do this in a type-safe way.
|
||||
spaz.r_walk_row = path # type: ignore
|
||||
spaz.r_walk_speed = self._get_bot_speed(spaz_type) # type: ignore
|
||||
# Tack some custom attrs onto the spaz.
|
||||
setattr(spaz, 'r_walk_row', path)
|
||||
setattr(spaz, 'r_walk_speed', self._get_bot_speed(spaz_type))
|
||||
|
||||
def add_bot_at_point(self,
|
||||
point: str,
|
||||
@ -1047,7 +1027,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
assert self._scoreboard is not None
|
||||
self._scoreboard.set_team_value(self.teams[0], score, max_score=None)
|
||||
|
||||
def _update_bot(self, bot: spazbot.SpazBot) -> bool:
|
||||
def _update_bot(self, bot: SpazBot) -> bool:
|
||||
# Yup; that's a lot of return statements right there.
|
||||
# pylint: disable=too-many-return-statements
|
||||
|
||||
@ -1057,8 +1037,8 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
assert bot.node
|
||||
|
||||
# FIXME: Do this in a type safe way.
|
||||
r_walk_speed: float = bot.r_walk_speed # type: ignore
|
||||
r_walk_row: int = bot.r_walk_row # type: ignore
|
||||
r_walk_speed: float = getattr(bot, 'r_walk_speed')
|
||||
r_walk_row: int = getattr(bot, 'r_walk_row')
|
||||
|
||||
speed = r_walk_speed
|
||||
pos = bot.node.position
|
||||
@ -1109,6 +1089,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
if ((ba.is_point_in_box(pos, boxes['b8'])
|
||||
and not ba.is_point_in_box(pos, boxes['b9']))
|
||||
or pos == (0.0, 0.0, 0.0)):
|
||||
|
||||
# Default to walking right if we're still in the walking area.
|
||||
bot.node.move_left_right = speed
|
||||
bot.node.move_up_down = 0
|
||||
@ -1136,7 +1117,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
respawn_time, ba.Call(self.spawn_player_if_exists, player))
|
||||
player.gamedata['respawn_icon'] = RespawnIcon(player, respawn_time)
|
||||
|
||||
elif isinstance(msg, spazbot.SpazBotDeathMessage):
|
||||
elif isinstance(msg, SpazBotDiedMessage):
|
||||
if msg.how is ba.DeathType.REACHED_GOAL:
|
||||
return
|
||||
pts, importance = msg.badguy.get_death_points(msg.how)
|
||||
@ -1161,7 +1142,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._dingsoundhigh,
|
||||
volume=0.6)
|
||||
except Exception as exc:
|
||||
print('EXC in Runaround on SpazBotDeathMessage:', exc)
|
||||
print('EXC in Runaround on SpazBotDiedMessage:', exc)
|
||||
|
||||
# Normally we pull scores from the score-set, but if there's no
|
||||
# player lets be explicit.
|
||||
@ -1172,7 +1153,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
|
||||
else:
|
||||
super().handlemessage(msg)
|
||||
|
||||
def _get_bot_speed(self, bot_type: Type[spazbot.SpazBot]) -> float:
|
||||
def _get_bot_speed(self, bot_type: Type[SpazBot]) -> float:
|
||||
speed = self._bot_speed_map.get(bot_type)
|
||||
if speed is None:
|
||||
raise TypeError('Invalid bot type to _get_bot_speed(): ' +
|
||||
|
||||
@ -26,14 +26,20 @@ import random
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
from bastd.actor import spazbot
|
||||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
from bastd.actor.bomb import TNTSpawner
|
||||
from bastd.actor.scoreboard import Scoreboard
|
||||
from bastd.actor.powerupbox import PowerupBoxFactory, PowerupBox
|
||||
from bastd.actor.spazbot import (SpazBotSet, SpazBotDiedMessage, BomberBot,
|
||||
BomberBotPro, BomberBotProShielded,
|
||||
BrawlerBot, BrawlerBotPro,
|
||||
BrawlerBotProShielded, TriggerBot,
|
||||
TriggerBotPro, TriggerBotProShielded,
|
||||
ChargerBot, StickyBot, ExplodeyBot)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Dict, Type, List, Optional, Sequence
|
||||
from bastd.actor.spazbot import SpazBot
|
||||
|
||||
|
||||
class Player(ba.Player['Team']):
|
||||
@ -76,7 +82,7 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._excludepowerups: List[str] = []
|
||||
self._scoreboard: Optional[Scoreboard] = None
|
||||
self._score = 0
|
||||
self._bots = spazbot.BotSet()
|
||||
self._bots = SpazBotSet()
|
||||
self._dingsound = ba.getsound('dingSmall')
|
||||
self._dingsoundhigh = ba.getsound('dingSmallHigh')
|
||||
self._tntspawner: Optional[TNTSpawner] = None
|
||||
@ -86,18 +92,18 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
|
||||
|
||||
# For each bot type: [spawn-rate, increase, d_increase]
|
||||
self._bot_spawn_types = {
|
||||
spazbot.BomberBot: [1.00, 0.00, 0.000],
|
||||
spazbot.BomberBotPro: [0.00, 0.05, 0.001],
|
||||
spazbot.BomberBotProShielded: [0.00, 0.02, 0.002],
|
||||
spazbot.BrawlerBot: [1.00, 0.00, 0.000],
|
||||
spazbot.BrawlerBotPro: [0.00, 0.05, 0.001],
|
||||
spazbot.BrawlerBotProShielded: [0.00, 0.02, 0.002],
|
||||
spazbot.TriggerBot: [0.30, 0.00, 0.000],
|
||||
spazbot.TriggerBotPro: [0.00, 0.05, 0.001],
|
||||
spazbot.TriggerBotProShielded: [0.00, 0.02, 0.002],
|
||||
spazbot.ChargerBot: [0.30, 0.05, 0.000],
|
||||
spazbot.StickyBot: [0.10, 0.03, 0.001],
|
||||
spazbot.ExplodeyBot: [0.05, 0.02, 0.002]
|
||||
BomberBot: [1.00, 0.00, 0.000],
|
||||
BomberBotPro: [0.00, 0.05, 0.001],
|
||||
BomberBotProShielded: [0.00, 0.02, 0.002],
|
||||
BrawlerBot: [1.00, 0.00, 0.000],
|
||||
BrawlerBotPro: [0.00, 0.05, 0.001],
|
||||
BrawlerBotProShielded: [0.00, 0.02, 0.002],
|
||||
TriggerBot: [0.30, 0.00, 0.000],
|
||||
TriggerBotPro: [0.00, 0.05, 0.001],
|
||||
TriggerBotProShielded: [0.00, 0.02, 0.002],
|
||||
ChargerBot: [0.30, 0.05, 0.000],
|
||||
StickyBot: [0.10, 0.03, 0.001],
|
||||
ExplodeyBot: [0.05, 0.02, 0.002]
|
||||
} # yapf: disable
|
||||
|
||||
def on_transition_in(self) -> None:
|
||||
@ -206,9 +212,7 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
|
||||
for i in range(3):
|
||||
for playerpt in playerpts:
|
||||
dists[i] += abs(playerpt[0] - botspawnpts[i][0])
|
||||
|
||||
# Little random variation.
|
||||
dists[i] += random.random() * 5.0
|
||||
dists[i] += random.random() * 5.0 # Minor random variation.
|
||||
if dists[0] > dists[1] and dists[0] > dists[2]:
|
||||
spawnpt = botspawnpts[0]
|
||||
elif dists[1] > dists[2]:
|
||||
@ -227,7 +231,7 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
|
||||
|
||||
# Now go back through and see where this value falls.
|
||||
total = 0
|
||||
bottype: Optional[Type[spazbot.SpazBot]] = None
|
||||
bottype: Optional[Type[SpazBot]] = None
|
||||
for spawntype in self._bot_spawn_types.items():
|
||||
total += spawntype[1][0]
|
||||
if randval <= total:
|
||||
@ -244,8 +248,9 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
|
||||
spawntype[1][1] += spawntype[1][2] # incr spawn rate incr rate
|
||||
|
||||
def _update_scores(self) -> None:
|
||||
# Do achievements in default preset only.
|
||||
score = self._score
|
||||
|
||||
# Achievements apply to the default preset only.
|
||||
if self._preset == 'default':
|
||||
if score >= 250:
|
||||
self._award_achievement('Last Stand Master')
|
||||
@ -266,28 +271,21 @@ class TheLastStandGame(ba.CoopGameActivity[Player, Team]):
|
||||
self._score += msg.score
|
||||
self._update_scores()
|
||||
|
||||
elif isinstance(msg, spazbot.SpazBotDeathMessage):
|
||||
elif isinstance(msg, SpazBotDiedMessage):
|
||||
pts, importance = msg.badguy.get_death_points(msg.how)
|
||||
target: Optional[Sequence[float]]
|
||||
if msg.killerplayer:
|
||||
try:
|
||||
assert msg.badguy.node
|
||||
target = msg.badguy.node.position
|
||||
except Exception:
|
||||
ba.print_exception()
|
||||
target = None
|
||||
try:
|
||||
self.stats.player_scored(msg.killerplayer,
|
||||
pts,
|
||||
target=target,
|
||||
kill=True,
|
||||
screenmessage=False,
|
||||
importance=importance)
|
||||
ba.playsound(self._dingsound
|
||||
if importance == 1 else self._dingsoundhigh,
|
||||
volume=0.6)
|
||||
except Exception as exc:
|
||||
print('EXC on last-stand SpazBotDeathMessage', exc)
|
||||
assert msg.badguy.node
|
||||
target = msg.badguy.node.position
|
||||
self.stats.player_scored(msg.killerplayer,
|
||||
pts,
|
||||
target=target,
|
||||
kill=True,
|
||||
screenmessage=False,
|
||||
importance=importance)
|
||||
ba.playsound(self._dingsound
|
||||
if importance == 1 else self._dingsoundhigh,
|
||||
volume=0.6)
|
||||
|
||||
# Normally we pull scores from the score-set, but if there's no
|
||||
# player lets be explicit.
|
||||
|
||||
@ -906,8 +906,8 @@ def _preload4() -> None:
|
||||
ba.getmodel(mname)
|
||||
for sname in ['metalHit', 'metalSkid', 'refWhistle', 'achievement']:
|
||||
ba.getsound(sname)
|
||||
from bastd.actor.flag import get_factory
|
||||
get_factory()
|
||||
from bastd.actor.flag import FlagFactory
|
||||
FlagFactory.get()
|
||||
|
||||
|
||||
class MainMenuSession(ba.Session):
|
||||
|
||||
@ -464,7 +464,6 @@ def handle_party_invite(name: str, invite_id: str) -> None:
|
||||
# FIXME: Ugly.
|
||||
# Let's store the invite-id away on the confirm window so we know if
|
||||
# we need to kill it later.
|
||||
# noinspection PyTypeHints
|
||||
conf.party_invite_id = invite_id # type: ignore
|
||||
|
||||
# store a weak-ref so we can get at this later
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
||||
<h4><em>last updated on 2020-05-24 for Ballistica version 1.5.0 build 20026</em></h4>
|
||||
<h4><em>last updated on 2020-05-25 for Ballistica version 1.5.0 build 20027</em></h4>
|
||||
<p>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 <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
|
||||
<hr>
|
||||
@ -51,7 +51,6 @@
|
||||
<li><a href="#function_ba_camerashake">ba.camerashake()</a></li>
|
||||
<li><a href="#function_ba_emitfx">ba.emitfx()</a></li>
|
||||
<li><a href="#function_ba_existing">ba.existing()</a></li>
|
||||
<li><a href="#function_ba_get_collision_info">ba.get_collision_info()</a></li>
|
||||
<li><a href="#function_ba_getactivity">ba.getactivity()</a></li>
|
||||
<li><a href="#function_ba_getnodes">ba.getnodes()</a></li>
|
||||
<li><a href="#function_ba_getsession">ba.getsession()</a></li>
|
||||
@ -200,6 +199,14 @@
|
||||
<li><a href="#class_ba_WidgetNotFoundError">ba.WidgetNotFoundError</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
<h4><a name="class_category_Misc_Classes">Misc Classes</a></h4>
|
||||
<ul>
|
||||
<li><a href="#class_ba_Collision">ba.Collision</a></li>
|
||||
</ul>
|
||||
<h4><a name="function_category_Misc_Functions">Misc Functions</a></h4>
|
||||
<ul>
|
||||
<li><a href="#function_ba_getcollision">ba.getcollision()</a></li>
|
||||
</ul>
|
||||
<h4><a name="class_category_Protocols">Protocols</a></h4>
|
||||
<ul>
|
||||
<li><a href="#class_ba_Existable">ba.Existable</a></li>
|
||||
@ -1368,6 +1375,44 @@ mycall()</pre>
|
||||
|
||||
<p>Use <a href="#function_ba_getcollidemodel">ba.getcollidemodel</a>() to instantiate one.</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_Collision">ba.Collision</a></strong></h3>
|
||||
<p><em><top level class></em>
|
||||
</p>
|
||||
<p>A class providing info about occurring collisions.</p>
|
||||
|
||||
<h3>Attributes:</h3>
|
||||
<h5><a href="#attr_ba_Collision__opposing_body">opposing_body</a>, <a href="#attr_ba_Collision__opposing_node">opposing_node</a>, <a href="#attr_ba_Collision__position">position</a>, <a href="#attr_ba_Collision__source_node">source_node</a></h5>
|
||||
<dl>
|
||||
<dt><h4><a name="attr_ba_Collision__opposing_body">opposing_body</a></h4></dt><dd>
|
||||
<p><span>int</span></p>
|
||||
<p>The body index on the opposing node in the current collision.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_Collision__opposing_node">opposing_node</a></h4></dt><dd>
|
||||
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
|
||||
<p>The node the current callback material node is hitting.</p>
|
||||
|
||||
<p> Throws a <a href="#class_ba_NodeNotFoundError">ba.NodeNotFoundError</a> if the node does not exist.
|
||||
This can be expected in some cases such as in 'disconnect'
|
||||
callbacks triggered by deleting a currently-colliding node.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_Collision__position">position</a></h4></dt><dd>
|
||||
<p><span><a href="#class_ba_Vec3">ba.Vec3</a></span></p>
|
||||
<p>The position of the current collision.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="attr_ba_Collision__source_node">source_node</a></h4></dt><dd>
|
||||
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
|
||||
<p>The node containing the material triggering the current callback.</p>
|
||||
|
||||
<p> Throws a <a href="#class_ba_NodeNotFoundError">ba.NodeNotFoundError</a> if the node does not exist, though
|
||||
the node should always exist (at least at the start of the collision
|
||||
callback).</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
<h2><strong><a name="class_ba_Context">ba.Context</a></strong></h3>
|
||||
<p><em><top level class></em>
|
||||
@ -3547,10 +3592,13 @@ the right thing both for Node objects and values of None.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_Node__getdelegate">getdelegate()</a></dt></h4><dd>
|
||||
<p><span>getdelegate() -> Any</span></p>
|
||||
<p><span>getdelegate(type: Type, doraise: bool = False) -> <varies></span></p>
|
||||
|
||||
<p>Returns the node's current delegate, which is the Python object
|
||||
designated to handle the Node's messages.</p>
|
||||
<p>Return the node's current delegate object if it matches a certain type.</p>
|
||||
|
||||
<p>If the node has no delegate or it is not an instance of the passed
|
||||
type, then None will be returned. If 'doraise' is True, then an
|
||||
Exception will be raised instead in such cases.</p>
|
||||
|
||||
</dd>
|
||||
<dt><h4><a name="method_ba_Node__getnodetype">getnodetype()</a></dt></h4><dd>
|
||||
@ -5732,18 +5780,6 @@ method) and will convert it to a None value if it does not exist.
|
||||
For more info, see notes on 'existables' here:
|
||||
https://ballistica.net/wiki/Coding-Style-Guide</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_get_collision_info">ba.get_collision_info()</a></strong></h3>
|
||||
<p><span>get_collision_info(*args: Any) -> Any</span></p>
|
||||
|
||||
<p>Return collision related values</p>
|
||||
|
||||
<p>Category: <a href="#function_category_Gameplay_Functions">Gameplay Functions</a></p>
|
||||
|
||||
<p>Returns a single collision value or tuple of values such as location,
|
||||
depth, nodes involved, etc. Only call this in the handler of a
|
||||
collision-triggered callback or message</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_get_valid_languages">ba.get_valid_languages()</a></strong></h3>
|
||||
<p><span>get_valid_languages() -> List[str]</span></p>
|
||||
@ -5785,6 +5821,12 @@ to be loaded. To avoid hitches, instantiate your media objects in
|
||||
advance of when you will be using them, allowing time for them to load
|
||||
in the background if necessary.</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_getcollision">ba.getcollision()</a></strong></h3>
|
||||
<p><span>getcollision() -> Collision</span></p>
|
||||
|
||||
<p>Return the in-progress collision.</p>
|
||||
|
||||
<hr>
|
||||
<h2><strong><a name="function_ba_getmaps">ba.getmaps()</a></strong></h3>
|
||||
<p><span>getmaps(playtype: str) -> List[str]</span></p>
|
||||
|
||||
@ -149,7 +149,6 @@ def test_validate() -> None:
|
||||
dataclass_validate(tclass)
|
||||
|
||||
# No longer valid.
|
||||
# noinspection PyTypeHints
|
||||
tclass.ival = None # type: ignore
|
||||
with pytest.raises(TypeError):
|
||||
dataclass_validate(tclass)
|
||||
|
||||
@ -80,7 +80,6 @@ class EntityTest(entity.Entity):
|
||||
fval2 = entity.Field('f2', entity.Float3Value())
|
||||
|
||||
|
||||
# noinspection PyTypeHints
|
||||
def test_entity_values() -> None:
|
||||
"""Test various entity assigns for value and type correctness."""
|
||||
ent = EntityTest()
|
||||
@ -134,7 +133,6 @@ def test_entity_values() -> None:
|
||||
assert dict(ent.str_int_dict.items()) == {'foo': 123}
|
||||
|
||||
|
||||
# noinspection PyTypeHints
|
||||
def test_entity_values_2() -> None:
|
||||
"""Test various entity assigns for value and type correctness."""
|
||||
|
||||
@ -191,7 +189,6 @@ def test_entity_values_2() -> None:
|
||||
assert static_type_equals(ent.grp.compoundlist[0].subval, bool)
|
||||
|
||||
|
||||
# noinspection PyTypeHints
|
||||
def test_field_copies() -> None:
|
||||
"""Test copying various values between fields."""
|
||||
ent1 = EntityTest()
|
||||
|
||||
@ -121,7 +121,6 @@ class EntityMixin:
|
||||
self.d_data = target.d_data
|
||||
|
||||
# Make sure target blows up if someone tries to use it.
|
||||
# noinspection PyTypeHints
|
||||
target.d_data = None # type: ignore
|
||||
|
||||
def pruned_data(self) -> Dict[str, Any]:
|
||||
|
||||
@ -99,7 +99,7 @@ class DispatchMethodWrapper(Generic[TARG, TRET]):
|
||||
registry: Dict[Any, Callable]
|
||||
|
||||
|
||||
# noinspection PyTypeHints, PyProtectedMember
|
||||
# noinspection PyProtectedMember
|
||||
def dispatchmethod(
|
||||
func: Callable[[Any, TARG],
|
||||
TRET]) -> DispatchMethodWrapper[TARG, TRET]:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user