update examples

This commit is contained in:
Roman Trapeznikov 2022-02-11 14:10:47 +03:00
parent 7673e01b9b
commit 7ecd804c1b
No known key found for this signature in database
GPG Key ID: 89BED52F1E290F8D
13 changed files with 493 additions and 434 deletions

View File

@ -1 +1 @@
175217515325031448193730963697929841246 225891607870108897605481763130834194451

View File

@ -102,7 +102,7 @@ class Context:
Category: General Utility Classes Category: General Utility Classes
Many operations such as ba.newnode() or ba.gettexture() operate Many operations such as ba.newnode or ba.gettexture operate
implicitly on the current context. Each ba.Activity has its own implicitly on the current context. Each ba.Activity has its own
Context and objects within that activity (nodes, media, etc) can only Context and objects within that activity (nodes, media, etc) can only
interact with other objects from that context. interact with other objects from that context.
@ -114,17 +114,17 @@ class Context:
the UI (there is a special 'ui' context for all user-interface-related the UI (there is a special 'ui' context for all user-interface-related
functionality) functionality)
When instantiating a ba.Context instance, a single 'source' argument When instantiating a ba.Context instance, a single ``'source'`` argument
is passed, which can be one of the following strings/objects: is passed, which can be one of the following strings/objects:
'empty': ``'empty'``:
Gives an empty context; it can be handy to run code here to ensure Gives an empty context; it can be handy to run code here to ensure
it does no loading of media, creation of nodes, etc. it does no loading of media, creation of nodes, etc.
'current': ``'current'``:
Sets the context object to the current context. Sets the context object to the current context.
'ui': ``'ui'``:
Sets to the UI context. UI functions as well as loading of media to Sets to the UI context. UI functions as well as loading of media to
be used in said functions must happen in the UI context. be used in said functions must happen in the UI context.
@ -138,16 +138,18 @@ class Context:
Usage: Usage:
Contexts are generally used with the python 'with' statement, which Contexts are generally used with the python 'with' statement, which
sets the context as current on entry and resets it to the previous sets the context as current on entry and resets it to the previous
value on exit. value on exit.
# Example: load a few textures into the UI context Example:
# (for use in widgets, etc): Load a few textures into the UI context
with ba.Context('ui'): (for use in widgets, etc):
tex1 = ba.gettexture('foo_tex_1') ```python
tex2 = ba.gettexture('foo_tex_2') >>> with ba.Context('ui'):
... tex1 = ba.gettexture('foo_tex_1')
... tex2 = ba.gettexture('foo_tex_2')
```
""" """
def __init__(self, source: Any): def __init__(self, source: Any):
@ -189,17 +191,21 @@ class ContextCall:
shutdown, whereas ba.WeakCall simply looks at whether the target shutdown, whereas ba.WeakCall simply looks at whether the target
object still exists. object still exists.
# Example A: code like this can inadvertently prevent our activity Examples:
# (self) from ending until the operation completes, since the bound Example A: code like this can inadvertently prevent our activity
# method we're passing (self.dosomething) contains a strong-reference (self) from ending until the operation completes, since the bound
# to self). method we're passing (self.dosomething) contains a strong-reference
start_some_long_action(callback_when_done=self.dosomething) to self).
>>> start_some_long_action(callback_when_done=self.dosomething)
# Example B: in this case our activity (self) can still die Example B: in this case our activity (self) can still die
# properly; the callback will clear itself when the activity starts properly; the callback will clear itself when the activity starts
# shutting down, becoming a harmless no-op and releasing the reference shutting down, becoming a harmless no-op and releasing the reference
# to our activity. to our activity.
start_long_action(callback_when_done=ba.ContextCall(self.mycallback)) ```python
>>> start_long_action(
... callback_when_done=ba.ContextCall(self.mycallback))
```
""" """
def __init__(self, call: Callable): def __init__(self, call: Callable):
@ -235,41 +241,41 @@ class InputDevice:
Attributes: Attributes:
allows_configuring allows_configuring (bool):
Whether the input-device can be configured. Whether the input-device can be configured.
has_meaningful_button_names has_meaningful_button_names (bool):
Whether button names returned by this instance match labels Whether button names returned by this instance match labels
on the actual device. (Can be used to determine whether to show on the actual device. (Can be used to determine whether to show
them in controls-overlays, etc.). them in controls-overlays, etc.).
player player (Optional[ba.SessionPlayer]):
The player associated with this input device. The player associated with this input device.
client_id client_id (int):
The numeric client-id this device is associated with. The numeric client-id this device is associated with.
This is only meaningful for remote client inputs; for This is only meaningful for remote client inputs; for
all local devices this will be -1. all local devices this will be -1.
name name (str):
The name of the device. The name of the device.
unique_identifier unique_identifier (str):
A string that can be used to persistently identify the device, A string that can be used to persistently identify the device,
even among other devices of the same type. Used for saving even among other devices of the same type. Used for saving
prefs, etc. prefs, etc.
id id (int):
The unique numeric id of this device. The unique numeric id of this device.
instance_number instance_number (int):
The number of this device among devices of the same type. The number of this device among devices of the same type.
is_controller_app is_controller_app (bool):
Whether this input-device represents a locally-connected Whether this input-device represents a locally-connected
controller-app. controller-app.
is_remote_client is_remote_client (bool):
Whether this input-device represents a remotely-connected Whether this input-device represents a remotely-connected
client. client.
@ -362,20 +368,20 @@ class Material:
A material can affect physical characteristics, generate sounds, A material can affect physical characteristics, generate sounds,
or trigger callback functions when collisions occur. or trigger callback functions when collisions occur.
Materials are applied to 'parts', which are groups of one or more Materials are applied to ``'parts'``, which are groups of one or more
rigid bodies created as part of a ba.Node. Nodes can have any number rigid bodies created as part of a ba.Node. Nodes can have any number
of parts, each with its own set of materials. Generally materials are of parts, each with its own set of materials. Generally materials are
specified as array attributes on the Node. The 'spaz' node, for specified as array attributes on the Node. The ``'spaz'`` node, for
example, has various attributes such as 'materials', example, has various attributes such as ``'materials'``,
'roller_materials', and 'punch_materials', which correspond to the ``'roller_materials'``, and ``'punch_materials'``, which correspond to the
various parts it creates. various parts it creates.
Use ba.Material() to instantiate a blank material, and then use its Use ba.Material to instantiate a blank material, and then use its
add_actions() method to define what the material does. ba.Material.add_actions method to define what the material does.
Attributes: Attributes:
label label (str):
A label for the material; only used for debugging. A label for the material; only used for debugging.
""" """
@ -393,133 +399,139 @@ class Material:
Add one or more actions to the material, optionally with conditions. Add one or more actions to the material, optionally with conditions.
Conditions: Conditions:
Conditions are provided as tuples which can be combined to form boolean Conditions are provided as tuples which can be combined to form boolean
logic. A single condition might look like ('condition_name', cond_arg), logic. A single condition might look like ``('condition_name', cond_arg)``,
or a more complex nested one might look like (('some_condition', or a more complex nested one might look like ``(('some_condition',
cond_arg), 'or', ('another_condition', cond2_arg)). cond_arg), 'or', ('another_condition', cond2_arg))``.
'and', 'or', and 'xor' are available to chain together 2 conditions, as ``'and'``, ``'or'``, and ``'xor'`` are available to chain together 2 conditions, as
seen above. seen above.
Available Conditions: Available Conditions:
``('they_have_material', material)`` - does the part we're hitting have a
('they_have_material', material) - does the part we're hitting have a
given ba.Material? given ba.Material?
('they_dont_have_material', material) - does the part we're hitting ``('they_dont_have_material', material)`` - does the part we're hitting
not have a given ba.Material? not have a given ba.Material?
('eval_colliding') - is 'collide' true at this point in material ``('eval_colliding')`` - is ``'collide'`` true at this point in material
evaluation? (see the modify_part_collision action) evaluation? (see the modify_part_collision action)
('eval_not_colliding') - is 'collide' false at this point in material ``('eval_not_colliding')`` - is 'collide' false at this point in material
evaluation? (see the modify_part_collision action) evaluation? (see the modify_part_collision action)
('we_are_younger_than', age) - is our part younger than 'age' ``('we_are_younger_than', age)`` - is our part younger than ``'age'``
(in milliseconds)? (in milliseconds)?
('we_are_older_than', age) - is our part older than 'age' ``('we_are_older_than', age)`` - is our part older than ``'age'``
(in milliseconds)? (in milliseconds)?
('they_are_younger_than', age) - is the part we're hitting younger than ``('they_are_younger_than', age)`` - is the part we're hitting younger than
'age' (in milliseconds)? ``'age'`` (in milliseconds)?
('they_are_older_than', age) - is the part we're hitting older than ``('they_are_older_than', age)`` - is the part we're hitting older than
'age' (in milliseconds)? ``'age'`` (in milliseconds)?
('they_are_same_node_as_us') - does the part we're hitting belong to ``('they_are_same_node_as_us')`` - does the part we're hitting belong to
the same ba.Node as us? the same ba.Node as us?
('they_are_different_node_than_us') - does the part we're hitting ``('they_are_different_node_than_us')`` - does the part we're hitting
belong to a different ba.Node than us? belong to a different ba.Node than us?
Actions: Actions:
In a similar manner, actions are specified as tuples. Multiple actions In a similar manner, actions are specified as tuples. Multiple actions
can be specified by providing a tuple of tuples. can be specified by providing a tuple of tuples.
Available Actions: Available Actions:
``('call', when, callable)`` - calls the provided callable; ``'when'`` can be
('call', when, callable) - calls the provided callable; 'when' can be either ``'at_connect'`` or ``'at_disconnect'``. ``'at_connect'`` means to fire
either 'at_connect' or 'at_disconnect'. 'at_connect' means to fire when the two parts first come in contact; ``'at_disconnect'`` means to
when the two parts first come in contact; 'at_disconnect' means to
fire once they cease being in contact. fire once they cease being in contact.
('message', who, when, message_obj) - sends a message object; 'who' can ``('message', who, when, message_obj)`` - sends a message object; ``'who'`` can
be either 'our_node' or 'their_node', 'when' can be 'at_connect' or be either ``'our_node'`` or ``'their_node'``, ``'when'`` can be ``'at_connect'`` or
'at_disconnect', and message_obj is the message object to send. ``'at_disconnect'``, and message_obj is the message object to send.
This has the same effect as calling the node's handlemessage() This has the same effect as calling the node's ba.Node.handlemessage
method. method.
('modify_part_collision', attr, value) - changes some characteristic ``('modify_part_collision', attr, value)`` - changes some characteristic
of the physical collision that will occur between our part and their of the physical collision that will occur between our part and their
part. This change will remain in effect as long as the two parts part. This change will remain in effect as long as the two parts
remain overlapping. This means if you have a part with a material remain overlapping. This means if you have a part with a material
that turns 'collide' off against parts younger than 100ms, and it that turns ``'collide'`` off against parts younger than 100ms, and it
touches another part that is 50ms old, it will continue to not touches another part that is 50ms old, it will continue to not
collide with that part until they separate, even if the 100ms collide with that part until they separate, even if the 100ms
threshold is passed. Options for attr/value are: 'physical' (boolean threshold is passed. Options for attr/value are: ``'physical'`` (boolean
value; whether a *physical* response will occur at all), 'friction' value; whether a *physical* response will occur at all), ``'friction'``
(float value; how friction-y the physical response will be), (float value; how friction-y the physical response will be),
'collide' (boolean value; whether *any* collision will occur at all, ``'collide'`` (boolean value; whether *any* collision will occur at all,
including non-physical stuff like callbacks), 'use_node_collide' including non-physical stuff like callbacks), ``'use_node_collide'``
(boolean value; whether to honor modify_node_collision overrides for (boolean value; whether to honor modify_node_collision overrides for
this collision), 'stiffness' (float value, how springy the physical this collision), ``'stiffness'`` (float value, how springy the physical
response is), 'damping' (float value, how damped the physical response is), ``'damping'`` (float value, how damped the physical
response is), 'bounce' (float value; how bouncy the physical response response is), ``'bounce'`` (float value; how bouncy the physical response
is). is).
('modify_node_collision', attr, value) - similar to ``('modify_node_collision', attr, value)`` - similar to
modify_part_collision, but operates at a node-level. ``modify_part_collision``, but operates at a node-level.
collision attributes set here will remain in effect as long as collision attributes set here will remain in effect as long as
*anything* from our part's node and their part's node overlap. *anything* from our part's node and their part's node overlap.
A key use of this functionality is to prevent new nodes from A key use of this functionality is to prevent new nodes from
colliding with each other if they appear overlapped; colliding with each other if they appear overlapped;
if modify_part_collision is used, only the individual parts that if ``modify_part_collision`` is used, only the individual parts that
were overlapping would avoid contact, but other parts could still were overlapping would avoid contact, but other parts could still
contact leaving the two nodes 'tangled up'. Using contact leaving the two nodes 'tangled up'. Using
modify_node_collision ensures that the nodes must completely ``modify_node_collision ensures`` that the nodes must completely
separate before they can start colliding. Currently the only attr separate before they can start colliding. Currently the only attr
available here is 'collide' (a boolean value). available here is ``'collide'`` (a boolean value).
('sound', sound, volume) - plays a ba.Sound when a collision occurs, at ``('sound', sound, volume)`` - plays a ba.Sound when a collision occurs, at
a given volume, regardless of the collision speed/etc. a given volume, regardless of the collision speed/etc.
('impact_sound', sound, targetImpulse, volume) - plays a sound when a ``('impact_sound', sound, targetImpulse, volume)`` - plays a sound when a
collision occurs, based on the speed of impact. Provide a ba.Sound, a collision occurs, based on the speed of impact. Provide a ba.Sound, a
target-impulse, and a volume. target-impulse, and a volume.
('skid_sound', sound, targetImpulse, volume) - plays a sound during a ``('skid_sound', sound, targetImpulse, volume)`` - plays a sound during a
collision when parts are 'scraping' against each other. Provide a collision when parts are 'scraping' against each other. Provide a
ba.Sound, a target-impulse, and a volume. ba.Sound, a target-impulse, and a volume.
('roll_sound', sound, targetImpulse, volume) - plays a sound during a ``('roll_sound', sound, targetImpulse, volume)`` - plays a sound during a
collision when parts are 'rolling' against each other. Provide a collision when parts are 'rolling' against each other. Provide a
ba.Sound, a target-impulse, and a volume. ba.Sound, a target-impulse, and a volume.
# example 1: create a material that lets us ignore Examples:
# collisions against any nodes we touch in the first example 1: create a material that lets us ignore
# 100 ms of our existence; handy for preventing us from collisions against any nodes we touch in the first
# exploding outward if we spawn on top of another object: 100 ms of our existence; handy for preventing us from
m = ba.Material() exploding outward if we spawn on top of another object:
m.add_actions(conditions=(('we_are_younger_than', 100), ```python
'or',('they_are_younger_than', 100)), >>> m = ba.Material()
actions=('modify_node_collision', 'collide', False)) ... m.add_actions(
... conditions=(('we_are_younger_than', 100),
... 'or', ('they_are_younger_than', 100)),
... actions=('modify_node_collision', 'collide', False))
```
# example 2: send a DieMessage to anything we touch, but cause example 2: send a ba.DieMessage to anything we touch, but cause
# no physical response. This should cause any ba.Actor to drop dead: no physical response. This should cause any ba.Actor to drop dead:
m = ba.Material() ```python
m.add_actions(actions=(('modify_part_collision', 'physical', False), >>> m = ba.Material()
('message', 'their_node', 'at_connect', ... m.add_actions(
ba.DieMessage()))) ... actions=(('modify_part_collision', 'physical', False),
... ('message', 'their_node', 'at_connect',
... ba.DieMessage())))
```
# example 3: play some sounds when we're contacting the ground: example 3: play some sounds when we're contacting the ground:
m = ba.Material() ```python
m.add_actions(conditions=('they_have_material', >>> m = ba.Material()
... m.add_actions(
... conditions=('they_have_material',
shared.footing_material), shared.footing_material),
actions=(('impact_sound', ba.getsound('metalHit'), 2, 5), ... actions=(('impact_sound', ba.getsound('metalHit'), 2, 5),
('skid_sound', ba.getsound('metalSkid'), 2, 5))) ... ('skid_sound', ba.getsound('metalSkid'), 2, 5)))
```
""" """
return None return None
@ -692,10 +704,13 @@ class Node:
target attribute to any value or connecting another node attribute target attribute to any value or connecting another node attribute
to it. to it.
# Example: create a locator and attach a light to it: Example:
light = ba.newnode('light') Create a locator and attach a light to it:
loc = ba.newnode('locator', attrs={'position': (0,10,0)}) ```python
loc.connectattr('position', light, 'position') >>> light = ba.newnode('light')
... loc = ba.newnode('locator', attrs={'position': (0, 10, 0)})
... loc.connectattr('position', light, 'position')
```
""" """
return None return None
@ -1005,16 +1020,19 @@ class Timer:
timeformat: A ba.TimeFormat value determining how the passed time is timeformat: A ba.TimeFormat value determining how the passed time is
interpreted. interpreted.
# Example: use a Timer object to print repeatedly for a few seconds: Example:
def say_it(): Use a Timer object to print repeatedly for a few seconds:
ba.screenmessage('BADGER!') ```python
def stop_saying_it(): >>> def say_it():
self.t = None ... ba.screenmessage('BADGER!')
ba.screenmessage('MUSHROOM MUSHROOM!') ... def stop_saying_it():
# Create our timer; it will run as long as we have the self.t ref. ... self.t = None
self.t = ba.Timer(0.3, say_it, repeat=True) ... ba.screenmessage('MUSHROOM MUSHROOM!')
# Now fire off a one-shot timer to kill it. ... # Create our timer; it will run as long as we have the self.t ref.
ba.timer(3.89, stop_saying_it) ... self.t = ba.Timer(0.3, say_it, repeat=True)
... # Now fire off a one-shot timer to kill it.
... ba.timer(3.89, stop_saying_it)
```
""" """
def __init__(self, def __init__(self,
@ -1797,10 +1815,13 @@ def do_once() -> bool:
The call is made from. Returns True if this location has not been The call is made from. Returns True if this location has not been
registered already, and False if it has. registered already, and False if it has.
# Example: this print will only fire for the first loop iteration: Example:
for i in range(10): This print will only fire for the first loop iteration:
if ba.do_once(): ```python
print('Hello once from loop!') >>> for i in range(10):
... if ba.do_once():
... print('Hello once from loop!')
```
""" """
return bool() return bool()
@ -4037,45 +4058,53 @@ def timer(time: float,
This timer cannot be canceled or modified once created. If you This timer cannot be canceled or modified once created. If you
require the ability to do so, use the ba.Timer class instead. require the ability to do so, use the ba.Timer class instead.
time: length of time (in seconds by default) that the timer will wait Arguments:
time (float):
Length of time (in seconds by default) that the timer will wait
before firing. Note that the actual delay experienced may vary before firing. Note that the actual delay experienced may vary
depending on the timetype. (see below) depending on the timetype. (see below)
call: A callable Python object. Note that the timer will retain a call (Callable[[], Any]):
A callable Python object. Note that the timer will retain a
strong reference to the callable for as long as it exists, so you strong reference to the callable for as long as it exists, so you
may want to look into concepts such as ba.WeakCall if that is not may want to look into concepts such as ba.WeakCall if that is not
desired. desired.
repeat: if True, the timer will fire repeatedly, with each successive repeat (bool):
If True, the timer will fire repeatedly, with each successive
firing having the same delay as the first. firing having the same delay as the first.
timetype can be either 'sim', 'base', or 'real'. It defaults to
'sim'. Types are explained below:
'sim' time maps to local simulation time in ba.Activity or ba.Session timetype (ba.TimeType):
Can be either ``SIM``, ``BASE``, or ``REAL``. It defaults to
``SIM``.
timeformat (ba.TimeFormat):
Defaults to seconds but can also be milliseconds.
- SIM time maps to local simulation time in ba.Activity or ba.Session
Contexts. This means that it may progress slower in slow-motion play Contexts. This means that it may progress slower in slow-motion play
modes, stop when the game is paused, etc. This time type is not modes, stop when the game is paused, etc. This time type is not
available in UI contexts. available in UI contexts.
- BASE time is also linked to gameplay in ba.Activity or ba.Session
'base' time is also linked to gameplay in ba.Activity or ba.Session
Contexts, but it progresses at a constant rate regardless of Contexts, but it progresses at a constant rate regardless of
slow-motion states or pausing. It can, however, slow down or stop slow-motion states or pausing. It can, however, slow down or stop
in certain cases such as network outages or game slowdowns due to in certain cases such as network outages or game slowdowns due to
cpu load. Like 'sim' time, this is unavailable in UI contexts. cpu load. Like 'sim' time, this is unavailable in UI contexts.
- REAL time always maps to actual clock time with a bit of filtering
'real' time always maps to actual clock time with a bit of filtering added, regardless of Context. (The filtering prevents it from going
added, regardless of Context. (the filtering prevents it from going
backwards or jumping forward by large amounts due to the app being backwards or jumping forward by large amounts due to the app being
backgrounded, system time changing, etc.) backgrounded, system time changing, etc.)
Real time timers are currently only available in the UI context. Real time timers are currently only available in the UI context.
the 'timeformat' arg defaults to seconds but can also be milliseconds. Examples:
Print some stuff through time:
# timer example: print some stuff through time: ```python
ba.screenmessage('hello from now!') >>> ba.screenmessage('hello from now!')
ba.timer(1.0, ba.Call(ba.screenmessage, 'hello from the future!')) >>> ba.timer(1.0, ba.Call(ba.screenmessage, 'hello from the future!'))
ba.timer(2.0, ba.Call(ba.screenmessage, 'hello from the future 2!')) >>> ba.timer(2.0, ba.Call(ba.screenmessage,
""" ... 'hello from the future 2!'))
```"""
return None return None

View File

@ -33,15 +33,18 @@ class Actor:
(killing off or transitioning out their nodes) when the last Python (killing off or transitioning out their nodes) when the last Python
reference to them disappears, so you can use logic such as: reference to them disappears, so you can use logic such as:
# Create a flag Actor in our game activity: Example:
from bastd.actor.flag import Flag ```python
self.flag = Flag(position=(0, 10, 0)) >>> # Create a flag Actor in our game activity:
>>> from bastd.actor.flag import Flag
# Later, destroy the flag. >>> self.flag = Flag(position=(0, 10, 0))
# (provided nothing else is holding a reference to it) >>>
# We could also just assign a new flag to this value. >>> # Later, destroy the flag.
# Either way, the old flag disappears. >>> # (provided nothing else is holding a reference to it)
self.flag = None >>> # We could also just assign a new flag to this value.
>>> # Either way, the old flag disappears.
>>> self.flag = None
```
This is in contrast to the behavior of the more low level ba.Nodes, This is in contrast to the behavior of the more low level ba.Nodes,
which are always explicitly created and destroyed and don't care which are always explicitly created and destroyed and don't care
@ -51,18 +54,18 @@ class Actor:
if you want an Actor to stick around until explicitly killed if you want an Actor to stick around until explicitly killed
regardless of references. regardless of references.
Another key feature of ba.Actor is its handlemessage() method, which Another key feature of ba.Actor is its ba.Actor.handlemessage method, which
takes a single arbitrary object as an argument. This provides a safe way takes a single arbitrary object as an argument. This provides a safe way
to communicate between ba.Actor, ba.Activity, ba.Session, and any other to communicate between ba.Actor, ba.Activity, ba.Session, and any other
class providing a handlemessage() method. The most universally handled class providing a handlemessage() method. The most universally handled
message type for Actors is the ba.DieMessage. message type for Actors is the ba.DieMessage.
# Another way to kill the flag from the example above: Another way to kill the flag from the example above:
# We can safely call this on any type with a 'handlemessage' method We can safely call this on any type with a 'handlemessage' method
# (though its not guaranteed to always have a meaningful effect). (though its not guaranteed to always have a meaningful effect).
# In this case the Actor instance will still be around, but its exists() In this case the Actor instance will still be around, but its exists()
# and is_alive() methods will both return False. and is_alive() methods will both return False.
self.flag.handlemessage(ba.DieMessage()) >>> self.flag.handlemessage(ba.DieMessage())
""" """
def __init__(self) -> None: def __init__(self) -> None:

View File

@ -166,6 +166,15 @@ class _WeakCall:
... ba.timer(5.0, ba.WeakCall(foo.bar)) ... ba.timer(5.0, ba.WeakCall(foo.bar))
... foo = None ... foo = None
EXAMPLE C: Wrap a method call with some positional and keyword args:
>>> myweakcall = ba.WeakCall(self.dostuff, argval1,
... namedarg=argval2)
... # Now we have a single callable to run that whole mess.
... # The same as calling myobj.dostuff(argval1, namedarg=argval2)
... # (provided my_obj still exists; this will do nothing
... # otherwise).
... myweakcall()
Note: additional args and keywords you provide to the WeakCall() Note: additional args and keywords you provide to the WeakCall()
constructor are stored as regular strong-references; you'll need constructor are stored as regular strong-references; you'll need
to wrap them in weakrefs manually if desired. to wrap them in weakrefs manually if desired.
@ -176,17 +185,6 @@ class _WeakCall:
Pass a callable as the first arg, followed by any number of Pass a callable as the first arg, followed by any number of
arguments or keywords. arguments or keywords.
Examples:
Example: wrap a method call with some positional and
keyword args:
>>> myweakcall = ba.WeakCall(myobj.dostuff, argval1,
... namedarg=argval2)
... # Now we have a single callable to run that whole mess.
... # The same as calling myobj.dostuff(argval1, namedarg=argval2)
... # (provided my_obj still exists; this will do nothing
... # otherwise).
... myweakcall()
""" """
if hasattr(args[0], '__func__'): if hasattr(args[0], '__func__'):
self._call = WeakMethod(args[0]) self._call = WeakMethod(args[0])
@ -232,12 +230,12 @@ class _Call:
Pass a callable as the first arg, followed by any number of Pass a callable as the first arg, followed by any number of
arguments or keywords. arguments or keywords.
# Example: wrap a method call with 1 positional and 1 keyword arg: Example:
mycall = ba.Call(myobj.dostuff, argval1, namedarg=argval2) Wrap a method call with 1 positional and 1 keyword arg:
>>> mycall = ba.Call(myobj.dostuff, argval, namedarg=argval2)
# Now we have a single callable to run that whole mess. ... # Now we have a single callable to run that whole mess.
# ..the same as calling myobj.dostuff(argval1, namedarg=argval2) ... # ..the same as calling myobj.dostuff(argval, namedarg=argval2)
mycall() ... mycall()
""" """
self._call = args[0] self._call = args[0]
self._args = args[1:] self._args = args[1:]
@ -251,7 +249,6 @@ class _Call:
str(self._args) + ' _keywds=' + str(self._keywds) + '>') str(self._args) + ' _keywds=' + str(self._keywds) + '>')
# Hack: pdoc won't run this
if TYPE_CHECKING: if TYPE_CHECKING:
WeakCall = Call WeakCall = Call
Call = Call Call = Call
@ -375,15 +372,18 @@ def storagename(suffix: str = None) -> str:
Note that this will function even if called in the class definition. Note that this will function even if called in the class definition.
# Example: generate a unique name for storage purposes: Examples:
class MyThingie: Generate a unique name for storage purposes:
```python
# This will give something like '_mymodule_submodule_mythingie_data'. >>> class MyThingie:
_STORENAME = ba.storagename('data') ... # This will give something like
... # '_mymodule_submodule_mythingie_data'.
# Use that name to store some data in the Activity we were passed. ... _STORENAME = ba.storagename('data')
def __init__(self, activity): ...
activity.customdata[self._STORENAME] = {} ... # Use that name to store some data in the Activity we were passed.
... def __init__(self, activity):
... activity.customdata[self._STORENAME] = {}
```
""" """
frame = inspect.currentframe() frame = inspect.currentframe()
if frame is None: if frame is None:

View File

@ -376,24 +376,29 @@ class Lstr:
To see available resource keys, look at any of the bs_language_*.py files To see available resource keys, look at any of the bs_language_*.py files
in the game or the translations pages at bombsquadgame.com/translate. in the game or the translations pages at bombsquadgame.com/translate.
# EXAMPLE 1: specify a string from a resource path Examples:
mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText') EXAMPLE 1: specify a string from a resource path
>>> mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText')
# EXAMPLE 2: specify a translated string via a category and english value; EXAMPLE 2: specify a translated string via a category and english
# if a translated value is available, it will be used; otherwise the value; if a translated value is available, it will be used; otherwise
# english value will be. To see available translation categories, look the english value will be. To see available translation categories,
# under the 'translations' resource section. look under the 'translations' resource section.
mynode.text = ba.Lstr(translate=('gameDescriptions', 'Defeat all enemies')) >>> mynode.text = ba.Lstr(translate=('gameDescriptions', 'Defeat all enemies'))
# EXAMPLE 3: specify a raw value and some substitutions. Substitutions can EXAMPLE 3: specify a raw value and some substitutions. Substitutions
# be used with resource and translate modes as well. can be used with resource and translate modes as well.
mynode.text = ba.Lstr(value='${A} / ${B}', >>> mynode.text = ba.Lstr(value='${A} / ${B}',
subs=[('${A}', str(score)), ('${B}', str(total))]) ... subs=[('${A}', str(score)), ('${B}', str(total))])
# EXAMPLE 4: Lstrs can be nested. This example would display the resource EXAMPLE 4: ba.Lstr's can be nested. This example would display the
# at res_a but replace ${NAME} with the value of the resource at res_b resource at res_a but replace ${NAME} with the value of the
mytextnode.text = ba.Lstr(resource='res_a', resource at res_b
subs=[('${NAME}', ba.Lstr(resource='res_b'))]) ```python
>>> mytextnode.text = ba.Lstr(
... resource='res_a',
... subs=[('${NAME}', ba.Lstr(resource='res_b'))])
```
""" """
# pylint: disable=dangerous-default-value # pylint: disable=dangerous-default-value

View File

@ -21,7 +21,7 @@ void PythonClassContext::SetupType(PyTypeObject* obj) {
"\n" "\n"
"Category: General Utility Classes\n" "Category: General Utility Classes\n"
"\n" "\n"
"Many operations such as ba.newnode() or ba.gettexture() operate\n" "Many operations such as ba.newnode or ba.gettexture operate\n"
"implicitly on the current context. Each ba.Activity has its own\n" "implicitly on the current context. Each ba.Activity has its own\n"
"Context and objects within that activity (nodes, media, etc) can only\n" "Context and objects within that activity (nodes, media, etc) can only\n"
"interact with other objects from that context.\n" "interact with other objects from that context.\n"
@ -33,16 +33,16 @@ void PythonClassContext::SetupType(PyTypeObject* obj) {
"the UI (there is a special 'ui' context for all user-interface-related\n" "the UI (there is a special 'ui' context for all user-interface-related\n"
"functionality)\n" "functionality)\n"
"\n" "\n"
"When instantiating a ba.Context instance, a single 'source' argument\n" "When instantiating a ba.Context instance, a single ``'source'`` argument\n"
"is passed, which can be one of the following strings/objects:\n\n" "is passed, which can be one of the following strings/objects:\n\n"
"'empty':\n" "``'empty'``:\n"
" Gives an empty context; it can be handy to run code here to ensure\n" " Gives an empty context; it can be handy to run code here to ensure\n"
" it does no loading of media, creation of nodes, etc.\n" " it does no loading of media, creation of nodes, etc.\n"
"\n" "\n"
"'current':\n" "``'current'``:\n"
" Sets the context object to the current context.\n" " Sets the context object to the current context.\n"
"\n" "\n"
"'ui':\n" "``'ui'``:\n"
" Sets to the UI context. UI functions as well as loading of media to\n" " Sets to the UI context. UI functions as well as loading of media to\n"
" be used in said functions must happen in the UI context.\n" " be used in said functions must happen in the UI context.\n"
"\n" "\n"
@ -56,16 +56,18 @@ void PythonClassContext::SetupType(PyTypeObject* obj) {
"\n" "\n"
"\n" "\n"
"Usage:\n" "Usage:\n"
" Contexts are generally used with the python 'with' statement, which\n"
" sets the context as current on entry and resets it to the previous\n"
" value on exit.\n"
"\n" "\n"
"Contexts are generally used with the python 'with' statement, which\n" "Example:\n"
"sets the context as current on entry and resets it to the previous\n" " Load a few textures into the UI context\n"
"value on exit.\n" " (for use in widgets, etc):\n"
"\n" " ```python\n"
"# Example: load a few textures into the UI context\n" " >>> with ba.Context('ui'):\n"
"# (for use in widgets, etc):\n" " ... tex1 = ba.gettexture('foo_tex_1')\n"
"with ba.Context('ui'):\n" " ... tex2 = ba.gettexture('foo_tex_2')\n"
" tex1 = ba.gettexture('foo_tex_1')\n" " ```\n";
" tex2 = ba.gettexture('foo_tex_2')\n";
obj->tp_new = tp_new; obj->tp_new = tp_new;
obj->tp_dealloc = (destructor)tp_dealloc; obj->tp_dealloc = (destructor)tp_dealloc;

View File

@ -38,17 +38,21 @@ void PythonClassContextCall::SetupType(PyTypeObject* obj) {
"shutdown, whereas ba.WeakCall simply looks at whether the target\n" "shutdown, whereas ba.WeakCall simply looks at whether the target\n"
"object still exists.\n" "object still exists.\n"
"\n" "\n"
"# Example A: code like this can inadvertently prevent our activity\n" "Examples:\n"
"# (self) from ending until the operation completes, since the bound\n" " Example A: code like this can inadvertently prevent our activity\n"
"# method we're passing (self.dosomething) contains a strong-reference\n" " (self) from ending until the operation completes, since the bound\n"
"# to self).\n" " method we're passing (self.dosomething) contains a strong-reference\n"
"start_some_long_action(callback_when_done=self.dosomething)\n" " to self).\n"
" >>> start_some_long_action(callback_when_done=self.dosomething)\n"
"\n" "\n"
"# Example B: in this case our activity (self) can still die\n" " Example B: in this case our activity (self) can still die\n"
"# properly; the callback will clear itself when the activity starts\n" " properly; the callback will clear itself when the activity starts\n"
"# shutting down, becoming a harmless no-op and releasing the reference\n" " shutting down, becoming a harmless no-op and releasing the reference\n"
"# to our activity.\n" " to our activity.\n"
"start_long_action(callback_when_done=ba.ContextCall(self.mycallback))\n"; " ```python\n"
" >>> start_long_action(\n"
" ... callback_when_done=ba.ContextCall(self.mycallback))\n"
" ```\n";
obj->tp_new = tp_new; obj->tp_new = tp_new;
obj->tp_dealloc = (destructor)tp_dealloc; obj->tp_dealloc = (destructor)tp_dealloc;

View File

@ -56,16 +56,16 @@ void PythonClassMaterial::SetupType(PyTypeObject* obj) {
"A material can affect physical characteristics, generate sounds,\n" "A material can affect physical characteristics, generate sounds,\n"
"or trigger callback functions when collisions occur.\n" "or trigger callback functions when collisions occur.\n"
"\n" "\n"
"Materials are applied to 'parts', which are groups of one or more\n" "Materials are applied to ``'parts'``, which are groups of one or more\n"
"rigid bodies created as part of a ba.Node. Nodes can have any number\n" "rigid bodies created as part of a ba.Node. Nodes can have any number\n"
"of parts, each with its own set of materials. Generally materials are\n" "of parts, each with its own set of materials. Generally materials are\n"
"specified as array attributes on the Node. The 'spaz' node, for\n" "specified as array attributes on the Node. The ``'spaz'`` node, for\n"
"example, has various attributes such as 'materials',\n" "example, has various attributes such as ``'materials'``,\n"
"'roller_materials', and 'punch_materials', which correspond to the\n" "``'roller_materials'``, and ``'punch_materials'``, which correspond to the\n"
"various parts it creates.\n" "various parts it creates.\n"
"\n" "\n"
"Use ba.Material() to instantiate a blank material, and then use its\n" "Use ba.Material to instantiate a blank material, and then use its\n"
"add_actions() method to define what the material does.\n" "ba.Material.add_actions method to define what the material does.\n"
"\n" "\n"
"Attributes:\n" "Attributes:\n"
"\n" "\n"
@ -281,133 +281,139 @@ PyMethodDef PythonClassMaterial::tp_methods[] = {
"Add one or more actions to the material, optionally with conditions.\n" "Add one or more actions to the material, optionally with conditions.\n"
"\n" "\n"
"Conditions:\n" "Conditions:\n"
" Conditions are provided as tuples which can be combined to form boolean\n"
" logic. A single condition might look like ``('condition_name', cond_arg)``,\n"
" or a more complex nested one might look like ``(('some_condition',\n"
" cond_arg), 'or', ('another_condition', cond2_arg))``.\n"
"\n" "\n"
"Conditions are provided as tuples which can be combined to form boolean\n" " ``'and'``, ``'or'``, and ``'xor'`` are available to chain together 2 conditions, as\n"
"logic. A single condition might look like ('condition_name', cond_arg),\n"
"or a more complex nested one might look like (('some_condition',\n"
"cond_arg), 'or', ('another_condition', cond2_arg)).\n"
"\n"
"'and', 'or', and 'xor' are available to chain together 2 conditions, as\n"
" seen above.\n" " seen above.\n"
"\n" "\n"
"Available Conditions:\n" "Available Conditions:\n"
"\n" " ``('they_have_material', material)`` - does the part we\'re hitting have a\n"
"('they_have_material', material) - does the part we\'re hitting have a\n"
" given ba.Material?\n" " given ba.Material?\n"
"\n" "\n"
"('they_dont_have_material', material) - does the part we\'re hitting\n" " ``('they_dont_have_material', material)`` - does the part we\'re hitting\n"
" not have a given ba.Material?\n" " not have a given ba.Material?\n"
"\n" "\n"
"('eval_colliding') - is 'collide' true at this point in material\n" " ``('eval_colliding')`` - is ``'collide'`` true at this point in material\n"
" evaluation? (see the modify_part_collision action)\n" " evaluation? (see the modify_part_collision action)\n"
"\n" "\n"
"('eval_not_colliding') - is 'collide' false at this point in material\n" " ``('eval_not_colliding')`` - is 'collide' false at this point in material\n"
" evaluation? (see the modify_part_collision action)\n" " evaluation? (see the modify_part_collision action)\n"
"\n" "\n"
"('we_are_younger_than', age) - is our part younger than 'age'\n" " ``('we_are_younger_than', age)`` - is our part younger than ``'age'``\n"
" (in milliseconds)?\n" " (in milliseconds)?\n"
"\n" "\n"
"('we_are_older_than', age) - is our part older than 'age'\n" " ``('we_are_older_than', age)`` - is our part older than ``'age'``\n"
" (in milliseconds)?\n" " (in milliseconds)?\n"
"\n" "\n"
"('they_are_younger_than', age) - is the part we're hitting younger than\n" " ``('they_are_younger_than', age)`` - is the part we're hitting younger than\n"
" 'age' (in milliseconds)?\n" " ``'age'`` (in milliseconds)?\n"
"\n" "\n"
"('they_are_older_than', age) - is the part we're hitting older than\n" " ``('they_are_older_than', age)`` - is the part we're hitting older than\n"
" 'age' (in milliseconds)?\n" " ``'age'`` (in milliseconds)?\n"
"\n" "\n"
"('they_are_same_node_as_us') - does the part we're hitting belong to\n" " ``('they_are_same_node_as_us')`` - does the part we're hitting belong to\n"
" the same ba.Node as us?\n" " the same ba.Node as us?\n"
"\n" "\n"
"('they_are_different_node_than_us') - does the part we're hitting\n" " ``('they_are_different_node_than_us')`` - does the part we're hitting\n"
" belong to a different ba.Node than us?\n" " belong to a different ba.Node than us?\n"
"\n" "\n"
"Actions:\n" "Actions:\n"
"\n" " In a similar manner, actions are specified as tuples. Multiple actions\n"
"In a similar manner, actions are specified as tuples. Multiple actions\n" " can be specified by providing a tuple of tuples.\n"
"can be specified by providing a tuple of tuples.\n"
"\n" "\n"
"Available Actions:\n" "Available Actions:\n"
"\n" " ``('call', when, callable)`` - calls the provided callable; ``'when'`` can be\n"
"('call', when, callable) - calls the provided callable; 'when' can be\n" " either ``'at_connect'`` or ``'at_disconnect'``. ``'at_connect'`` means to fire\n"
" either 'at_connect' or 'at_disconnect'. 'at_connect' means to fire\n" " when the two parts first come in contact; ``'at_disconnect'`` means to\n"
" when the two parts first come in contact; 'at_disconnect' means to\n"
" fire once they cease being in contact.\n" " fire once they cease being in contact.\n"
"\n" "\n"
"('message', who, when, message_obj) - sends a message object; 'who' can\n" " ``('message', who, when, message_obj)`` - sends a message object; ``'who'`` can\n"
" be either 'our_node' or 'their_node', 'when' can be 'at_connect' or\n" " be either ``'our_node'`` or ``'their_node'``, ``'when'`` can be ``'at_connect'`` or\n"
" 'at_disconnect', and message_obj is the message object to send.\n" " ``'at_disconnect'``, and message_obj is the message object to send.\n"
" This has the same effect as calling the node's handlemessage()\n" " This has the same effect as calling the node's ba.Node.handlemessage\n"
" method.\n" " method.\n"
"\n" "\n"
"('modify_part_collision', attr, value) - changes some characteristic\n" " ``('modify_part_collision', attr, value)`` - changes some characteristic\n"
" of the physical collision that will occur between our part and their\n" " of the physical collision that will occur between our part and their\n"
" part. This change will remain in effect as long as the two parts\n" " part. This change will remain in effect as long as the two parts\n"
" remain overlapping. This means if you have a part with a material\n" " remain overlapping. This means if you have a part with a material\n"
" that turns 'collide' off against parts younger than 100ms, and it\n" " that turns ``'collide'`` off against parts younger than 100ms, and it\n"
" touches another part that is 50ms old, it will continue to not\n" " touches another part that is 50ms old, it will continue to not\n"
" collide with that part until they separate, even if the 100ms\n" " collide with that part until they separate, even if the 100ms\n"
" threshold is passed. Options for attr/value are: 'physical' (boolean\n" " threshold is passed. Options for attr/value are: ``'physical'`` (boolean\n"
" value; whether a *physical* response will occur at all), 'friction'\n" " value; whether a *physical* response will occur at all), ``'friction'``\n"
" (float value; how friction-y the physical response will be),\n" " (float value; how friction-y the physical response will be),\n"
" 'collide' (boolean value; whether *any* collision will occur at all,\n" " ``'collide'`` (boolean value; whether *any* collision will occur at all,\n"
" including non-physical stuff like callbacks), 'use_node_collide'\n" " including non-physical stuff like callbacks), ``'use_node_collide'``\n"
" (boolean value; whether to honor modify_node_collision overrides for\n" " (boolean value; whether to honor modify_node_collision overrides for\n"
" this collision), 'stiffness' (float value, how springy the physical\n" " this collision), ``'stiffness'`` (float value, how springy the physical\n"
" response is), 'damping' (float value, how damped the physical\n" " response is), ``'damping'`` (float value, how damped the physical\n"
" response is), 'bounce' (float value; how bouncy the physical response\n" " response is), ``'bounce'`` (float value; how bouncy the physical response\n"
" is).\n" " is).\n"
"\n" "\n"
"('modify_node_collision', attr, value) - similar to\n" " ``('modify_node_collision', attr, value)`` - similar to\n"
" modify_part_collision, but operates at a node-level.\n" " ``modify_part_collision``, but operates at a node-level.\n"
" collision attributes set here will remain in effect as long as\n" " collision attributes set here will remain in effect as long as\n"
" *anything* from our part's node and their part's node overlap.\n" " *anything* from our part's node and their part's node overlap.\n"
" A key use of this functionality is to prevent new nodes from\n" " A key use of this functionality is to prevent new nodes from\n"
" colliding with each other if they appear overlapped;\n" " colliding with each other if they appear overlapped;\n"
" if modify_part_collision is used, only the individual parts that\n" " if ``modify_part_collision`` is used, only the individual parts that\n"
" were overlapping would avoid contact, but other parts could still\n" " were overlapping would avoid contact, but other parts could still\n"
" contact leaving the two nodes 'tangled up'. Using\n" " contact leaving the two nodes 'tangled up'. Using\n"
" modify_node_collision ensures that the nodes must completely\n" " ``modify_node_collision ensures`` that the nodes must completely\n"
" separate before they can start colliding. Currently the only attr\n" " separate before they can start colliding. Currently the only attr\n"
" available here is 'collide' (a boolean value).\n" " available here is ``'collide'`` (a boolean value).\n"
"\n" "\n"
"('sound', sound, volume) - plays a ba.Sound when a collision occurs, at\n" " ``('sound', sound, volume)`` - plays a ba.Sound when a collision occurs, at\n"
" a given volume, regardless of the collision speed/etc.\n" " a given volume, regardless of the collision speed/etc.\n"
"\n" "\n"
"('impact_sound', sound, targetImpulse, volume) - plays a sound when a\n" " ``('impact_sound', sound, targetImpulse, volume)`` - plays a sound when a\n"
" collision occurs, based on the speed of impact. Provide a ba.Sound, a\n" " collision occurs, based on the speed of impact. Provide a ba.Sound, a\n"
" target-impulse, and a volume.\n" " target-impulse, and a volume.\n"
"\n" "\n"
"('skid_sound', sound, targetImpulse, volume) - plays a sound during a\n" " ``('skid_sound', sound, targetImpulse, volume)`` - plays a sound during a\n"
" collision when parts are 'scraping' against each other. Provide a\n" " collision when parts are 'scraping' against each other. Provide a\n"
" ba.Sound, a target-impulse, and a volume.\n" " ba.Sound, a target-impulse, and a volume.\n"
"\n" "\n"
"('roll_sound', sound, targetImpulse, volume) - plays a sound during a\n" " ``('roll_sound', sound, targetImpulse, volume)`` - plays a sound during a\n"
" collision when parts are 'rolling' against each other. Provide a\n" " collision when parts are 'rolling' against each other. Provide a\n"
" ba.Sound, a target-impulse, and a volume.\n" " ba.Sound, a target-impulse, and a volume.\n"
"\n" "\n"
"# example 1: create a material that lets us ignore\n" "Examples:\n"
"# collisions against any nodes we touch in the first\n" " example 1: create a material that lets us ignore\n"
"# 100 ms of our existence; handy for preventing us from\n" " collisions against any nodes we touch in the first\n"
"# exploding outward if we spawn on top of another object:\n" " 100 ms of our existence; handy for preventing us from\n"
"m = ba.Material()\n" " exploding outward if we spawn on top of another object:\n"
"m.add_actions(conditions=(('we_are_younger_than', 100),\n" " ```python\n"
" 'or',('they_are_younger_than', 100)),\n" " >>> m = ba.Material()\n"
" actions=('modify_node_collision', 'collide', False))\n" " ... m.add_actions(\n"
" ... conditions=(('we_are_younger_than', 100),\n"
" ... 'or', ('they_are_younger_than', 100)),\n"
" ... actions=('modify_node_collision', 'collide', False))\n"
" ```\n"
"\n" "\n"
"# example 2: send a DieMessage to anything we touch, but cause\n" " example 2: send a ba.DieMessage to anything we touch, but cause\n"
"# no physical response. This should cause any ba.Actor to drop dead:\n" " no physical response. This should cause any ba.Actor to drop dead:\n"
"m = ba.Material()\n" " ```python\n"
"m.add_actions(actions=(('modify_part_collision', 'physical', False),\n" " >>> m = ba.Material()\n"
" ('message', 'their_node', 'at_connect',\n" " ... m.add_actions(\n"
" ba.DieMessage())))\n" " ... actions=(('modify_part_collision', 'physical', False),\n"
" ... ('message', 'their_node', 'at_connect',\n"
" ... ba.DieMessage())))\n"
" ```\n"
"\n" "\n"
"# example 3: play some sounds when we're contacting the ground:\n" " example 3: play some sounds when we're contacting the ground:\n"
"m = ba.Material()\n" " ```python\n"
"m.add_actions(conditions=('they_have_material',\n" " >>> m = ba.Material()\n"
" ... m.add_actions(\n"
" ... conditions=('they_have_material',\n"
" shared.footing_material),\n" " shared.footing_material),\n"
" actions=(('impact_sound', ba.getsound('metalHit'), 2, 5),\n" " ... actions=(('impact_sound', ba.getsound('metalHit'), 2, 5),\n"
" ('skid_sound', ba.getsound('metalSkid'), 2, 5)))\n" " ... ('skid_sound', ba.getsound('metalSkid'), 2, 5)))\n"
" ```\n"
"\n"}, "\n"},
{"__dir__", (PyCFunction)Dir, METH_NOARGS, {"__dir__", (PyCFunction)Dir, METH_NOARGS,
"allows inclusion of our custom attrs in standard python dir()"}, "allows inclusion of our custom attrs in standard python dir()"},

View File

@ -444,10 +444,13 @@ PyMethodDef PythonClassNode::tp_methods[] = {
"target attribute to any value or connecting another node attribute\n" "target attribute to any value or connecting another node attribute\n"
"to it.\n" "to it.\n"
"\n" "\n"
"# Example: create a locator and attach a light to it:\n" "Example:\n"
"light = ba.newnode('light')\n" " Create a locator and attach a light to it:\n"
"loc = ba.newnode('locator', attrs={'position': (0,10,0)})\n" " ```python\n"
"loc.connectattr('position', light, 'position')"}, " >>> light = ba.newnode('light')\n"
" ... loc = ba.newnode('locator', attrs={'position': (0, 10, 0)})\n"
" ... loc.connectattr('position', light, 'position')\n"
" ```\n"},
{"__dir__", (PyCFunction)Dir, METH_NOARGS, {"__dir__", (PyCFunction)Dir, METH_NOARGS,
"allows inclusion of our custom attrs in standard python dir()"}, "allows inclusion of our custom attrs in standard python dir()"},
{nullptr}}; {nullptr}};

View File

@ -45,16 +45,19 @@ void PythonClassTimer::SetupType(PyTypeObject* obj) {
"timeformat: A ba.TimeFormat value determining how the passed time is\n" "timeformat: A ba.TimeFormat value determining how the passed time is\n"
"interpreted.\n" "interpreted.\n"
"\n" "\n"
"# Example: use a Timer object to print repeatedly for a few seconds:\n" "Example:\n"
"def say_it():\n" " Use a Timer object to print repeatedly for a few seconds:\n"
" ba.screenmessage('BADGER!')\n" " ```python\n"
"def stop_saying_it():\n" " >>> def say_it():\n"
" self.t = None\n" " ... ba.screenmessage('BADGER!')\n"
" ba.screenmessage('MUSHROOM MUSHROOM!')\n" " ... def stop_saying_it():\n"
"# Create our timer; it will run as long as we have the self.t ref.\n" " ... self.t = None\n"
"self.t = ba.Timer(0.3, say_it, repeat=True)\n" " ... ba.screenmessage('MUSHROOM MUSHROOM!')\n"
"# Now fire off a one-shot timer to kill it.\n" " ... # Create our timer; it will run as long as we have the self.t ref.\n"
"ba.timer(3.89, stop_saying_it)"; " ... self.t = ba.Timer(0.3, say_it, repeat=True)\n"
" ... # Now fire off a one-shot timer to kill it.\n"
" ... ba.timer(3.89, stop_saying_it)\n"
" ```\n";
obj->tp_new = tp_new; obj->tp_new = tp_new;
obj->tp_dealloc = (destructor)tp_dealloc; obj->tp_dealloc = (destructor)tp_dealloc;
} }

View File

@ -1033,52 +1033,53 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
"This timer cannot be canceled or modified once created. If you\n" "This timer cannot be canceled or modified once created. If you\n"
" require the ability to do so, use the ba.Timer class instead.\n" " require the ability to do so, use the ba.Timer class instead.\n"
"\n" "\n"
"time: length of time (in seconds by default) that the timer will " "Arguments:\n"
"wait\n" " time (float):\n"
"before firing. Note that the actual delay experienced may vary\n " " Length of time (in seconds by default) that the timer will wait\n"
"depending on the timetype. (see below)\n" " before firing. Note that the actual delay experienced may vary\n "
" depending on the timetype. (see below)\n"
"\n" "\n"
"call: A callable Python object. Note that the timer will retain a\n" " call (Callable[[], Any]):\n"
"strong reference to the callable for as long as it exists, so you\n" " A callable Python object. Note that the timer will retain a\n"
"may want to look into concepts such as ba.WeakCall if that is not\n" " strong reference to the callable for as long as it exists, so you\n"
"desired.\n" " may want to look into concepts such as ba.WeakCall if that is not\n"
" desired.\n"
"\n" "\n"
"repeat: if True, the timer will fire repeatedly, with each " " repeat (bool):\n"
"successive\n" " If True, the timer will fire repeatedly, with each successive\n"
"firing having the same delay as the first.\n" " firing having the same delay as the first.\n"
"\n" "\n"
"timetype can be either 'sim', 'base', or 'real'. It defaults to\n"
"'sim'. Types are explained below:\n"
"\n" "\n"
"'sim' time maps to local simulation time in ba.Activity or " " timetype (ba.TimeType):\n"
"ba.Session\n" " Can be either ``SIM``, ``BASE``, or ``REAL``. It defaults to\n"
"Contexts. This means that it may progress slower in slow-motion " " ``SIM``. \n"
"play\n" "\n"
" timeformat (ba.TimeFormat):\n"
" Defaults to seconds but can also be milliseconds.\n"
"\n"
"- SIM time maps to local simulation time in ba.Activity or ba.Session\n"
"Contexts. This means that it may progress slower in slow-motion play\n"
"modes, stop when the game is paused, etc. This time type is not\n" "modes, stop when the game is paused, etc. This time type is not\n"
"available in UI contexts.\n" "available in UI contexts.\n"
"\n" "- BASE time is also linked to gameplay in ba.Activity or ba.Session\n"
"'base' time is also linked to gameplay in ba.Activity or ba.Session\n"
"Contexts, but it progresses at a constant rate regardless of\n " "Contexts, but it progresses at a constant rate regardless of\n "
"slow-motion states or pausing. It can, however, slow down or stop\n" "slow-motion states or pausing. It can, however, slow down or stop\n"
"in certain cases such as network outages or game slowdowns due to\n" "in certain cases such as network outages or game slowdowns due to\n"
"cpu load. Like 'sim' time, this is unavailable in UI contexts.\n" "cpu load. Like 'sim' time, this is unavailable in UI contexts.\n"
"\n" "- REAL time always maps to actual clock time with a bit of filtering\n"
"'real' time always maps to actual clock time with a bit of " "added, regardless of Context. (The filtering prevents it from going\n"
"filtering\n"
"added, regardless of Context. (the filtering prevents it from "
"going\n"
"backwards or jumping forward by large amounts due to the app being\n" "backwards or jumping forward by large amounts due to the app being\n"
"backgrounded, system time changing, etc.)\n" "backgrounded, system time changing, etc.)\n"
"Real time timers are currently only available in the UI context.\n" "Real time timers are currently only available in the UI context.\n"
"\n" "\n"
"the 'timeformat' arg defaults to seconds but can also be " "Examples:\n"
"milliseconds.\n" " Print some stuff through time:\n"
"\n" " ```python\n"
"# timer example: print some stuff through time:\n" " >>> ba.screenmessage('hello from now!')\n"
"ba.screenmessage('hello from now!')\n" " >>> ba.timer(1.0, ba.Call(ba.screenmessage, 'hello from the future!'))\n"
"ba.timer(1.0, ba.Call(ba.screenmessage, 'hello from the future!'))\n" " >>> ba.timer(2.0, ba.Call(ba.screenmessage,\n"
"ba.timer(2.0, ba.Call(ba.screenmessage, 'hello from the future " " ... 'hello from the future 2!'))\n"
"2!'))\n"}, " ```\n"},
{"time", (PyCFunction)PyTime, METH_VARARGS | METH_KEYWORDS, {"time", (PyCFunction)PyTime, METH_VARARGS | METH_KEYWORDS,
"time(timetype: ba.TimeType = TimeType.SIM,\n" "time(timetype: ba.TimeType = TimeType.SIM,\n"

View File

@ -846,10 +846,13 @@ auto PythonMethodsSystem::GetMethods() -> std::vector<PyMethodDef> {
"The call is made from. Returns True if this location has not been\n" "The call is made from. Returns True if this location has not been\n"
"registered already, and False if it has.\n" "registered already, and False if it has.\n"
"\n" "\n"
"# Example: this print will only fire for the first loop iteration:\n" "Example:\n"
"for i in range(10):\n" " This print will only fire for the first loop iteration:\n"
" if ba.do_once():\n" " ```python\n"
" print('Hello once from loop!')"}, " >>> for i in range(10):\n"
" ... if ba.do_once():\n"
" ... print('Hello once from loop!')\n"
" ```\n"},
{"_app", (PyCFunction)PyApp, METH_VARARGS | METH_KEYWORDS, {"_app", (PyCFunction)PyApp, METH_VARARGS | METH_KEYWORDS,
"_app() -> ba.App\n" "_app() -> ba.App\n"

View File

@ -89,7 +89,7 @@ def generate(projroot: str) -> None:
pdoc.render.configure(docformat='google', pdoc.render.configure(docformat='google',
search=True, search=True,
show_source=True) show_source=True)
pdoc.pdoc('ba', 'bastd', output_directory=outdirname) pdoc.pdoc('ba', output_directory=outdirname)
except Exception as exc: except Exception as exc:
import traceback import traceback
traceback.print_exc() traceback.print_exc()