836fb410c4efbaaafec9a1c60c7d90b2b7bf50e9
1 """BulletML implementation."""
3 from __future__
import division
5 from math
import atan2
, sin
, cos
7 from bulletml
.parser
import ActionDef
, ActionRef
9 __all__
= ["Action", "Bullet"]
12 """Running action implementation.
14 To implement new actions, add a new element/class pair to
15 parser.ActionDef.CONSTRUCTORS. It should support FromXML,
16 __getstate__, and __setstate__, and 5-ary __call__:
18 def __call__(self, owner, action, params, rank, created)
20 Which will be called to execute it. This function should modify
21 owner, action, and created in-place, and return true if action
22 execution should stop for this bullet this frame.
26 def __init__(self
, parent
, actions
, params
, rank
, repeat
=1):
27 self
.actions
= actions
34 self
.direction_frames
= 0
39 self
.previous_fire_direction
= 0
40 self
.previous_fire_speed
= 0
45 self
.copy_state(parent
)
48 return "%s(pc=%r, actions=%r)" % (
49 type(self
).__name
__, self
.pc
, self
.actions
)
51 def Child(self
, action
, params
, rank
, repeat
=1):
52 actions
, params
= action(params
, rank
)
53 return type(self
)(self
, actions
, params
, rank
, repeat
)
56 """End this action and its parents."""
62 def copy_state(self
, other
):
63 """Copy fire/movement state from other to self."""
64 self
.direction_frames
= other
.direction_frames
65 self
.direction
= other
.direction
66 self
.aiming
= other
.aiming
67 self
.speed_frames
= other
.speed_frames
68 self
.speed
= other
.speed
69 self
.accel_frames
= other
.accel_frames
72 self
.previous_fire_direction
= other
.previous_fire_direction
73 self
.previous_fire_speed
= other
.previous_fire_speed
75 def step(self
, owner
, created
):
76 """Advance by one frame."""
78 if self
.speed_frames
> 0:
79 self
.speed_frames
-= 1
80 owner
.speed
+= self
.speed
82 if self
.direction_frames
> 0:
83 # I'm still not sure what the aim check is supposed to do.
84 self
.direction_frames
-= 1
85 if self
.aiming
and self
.direction_frames
<= 0:
86 owner
.direction
+= owner
.aim
88 owner
.direction
+= self
.direction
90 if self
.accel_frames
> 0:
91 self
.accel_frames
-= 1
98 if self
.wait_frames
> 0:
102 s_params
= self
.params
109 action
= self
.actions
[self
.pc
]
115 if self
.parent
is not None:
116 self
.parent
.copy_state(self
)
117 owner
.replace(self
, self
.parent
)
121 action
= self
.actions
[self
.pc
]
123 if isinstance(action
, (ActionDef
, ActionRef
)):
124 child
= self
.Child(action
, s_params
, rank
)
125 owner
.replace(self
, child
)
126 child
.step(owner
, created
)
129 elif action(owner
, self
, s_params
, rank
, created
):
132 class Bullet(object):
133 """Simple bullet implementation.
136 x, y - current X/Y position
137 px, py - X/Y position prior to the last step
138 mx, my - X/Y axis-oriented speed modifier ("acceleration")
139 direction - direction of movement, in radians
140 speed - speed of movement, in units per frame
141 target - object with .x and .y fields for "aim" directions
142 vanished - set to true by a <vanish> action
143 rank - game difficulty, 0 to 1, default 0.5
144 tags - string tags set by the running actions
145 appearance - string used to set bullet appearance
147 Contructor Arguments:
148 x, y, direction, speed, target, rank, tags, appearance
149 - same as the above attributes
150 actions - internal action list
151 Action - custom Action constructor
155 def __init__(self
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
156 actions
=(), rank
=0.5, tags
=(), appearance
=None):
161 self
.direction
= direction
163 self
.vanished
= False
166 self
.tags
= set(tags
)
167 self
.appearance
= appearance
168 self
.actions
= list(actions
)
171 def FromDocument(cls
, doc
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
172 params
=(), rank
=0.5, Action
=Action
):
173 """Construct a new Bullet from a loaded BulletML document."""
174 actions
= [action(params
, rank
) for action
in doc
.actions
]
175 # New bullets reset the parent hierarchy.
176 actions
= [Action(None, action
, params
, rank
)
177 for action
, params
in actions
]
178 return cls(x
=x
, y
=y
, direction
=direction
, speed
=speed
,
179 target
=target
, actions
=actions
, rank
=rank
)
182 return ("%s(%r, %r, accel=%r, direction=%r, speed=%r, "
183 "actions=%r, target=%r, appearance=vanished=%r)") % (
184 type(self
).__name
__, self
.x
, self
.y
, (self
.mx
, self
.my
),
185 self
.direction
, self
.speed
, self
.actions
, self
.target
,
186 self
.appearance
, self
.vanished
)
190 """Angle to the target, in radians.
192 If the target does not exist or cannot be found, return 0.
195 target_x
= self
.target
.x
196 target_y
= self
.target
.y
197 except AttributeError:
200 return atan2(target_x
- self
.x
, target_y
- self
.y
)
204 """Check if this bullet is finished running.
206 A bullet is finished when it has vanished, and all its
207 actions have finished.
209 If this is true, the bullet should be removed from the screen.
210 (You will probably want to cull it under other circumstances
213 if not self
.vanished
:
215 for action
in self
.actions
:
216 if not action
.finished
:
221 """Vanish this bullet and stop all actions."""
223 for action
in self
.actions
:
227 def replace(self
, old
, new
):
228 """Replace an active action with another.
230 This is mostly used by actions internally to queue children.
233 idx
= self
.actions
.index(old
)
237 self
.actions
[idx
] = new
240 """Advance by one frame.
242 This updates the position and velocity, and may also set the
245 It returns any new bullets this bullet spawned during this step.
249 for action
in self
.actions
:
250 action
.step(self
, created
)
253 direction
= self
.direction
256 self
.x
+= self
.mx
+ sin(direction
) * speed
257 self
.y
+= -self
.my
+ cos(direction
) * speed