f0e8964d806351cd9a2e876db194730da44bc4f9
1 """BulletML implementation."""
3 from __future__
import division
5 from math
import atan2
, sin
, cos
7 from bulletml
import parser
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
, actions
, params
, rank
, repeat
=1):
52 return type(self
)(self
, actions
, params
, rank
, repeat
)
55 """End this action and its parents."""
61 def copy_state(self
, other
):
62 """Copy fire/movement state from other to self."""
63 self
.direction_frames
= other
.direction_frames
64 self
.direction
= other
.direction
65 self
.aiming
= other
.aiming
66 self
.speed_frames
= other
.speed_frames
67 self
.speed
= other
.speed
68 self
.accel_frames
= other
.accel_frames
71 self
.previous_fire_direction
= other
.previous_fire_direction
72 self
.previous_fire_speed
= other
.previous_fire_speed
74 def step(self
, owner
, created
):
75 """Advance by one frame."""
76 s_params
= self
.params
79 if self
.speed_frames
> 0:
80 self
.speed_frames
-= 1
81 owner
.speed
+= self
.speed
83 if self
.direction_frames
> 0:
84 # I'm still not sure what the aim check is supposed to do.
85 self
.direction_frames
-= 1
86 if self
.aiming
and self
.direction_frames
<= 0:
87 owner
.direction
+= owner
.aim
89 owner
.direction
+= self
.direction
91 if self
.accel_frames
> 0:
92 self
.accel_frames
-= 1
99 if self
.wait_frames
> 0:
100 self
.wait_frames
-= 1
107 action
= self
.actions
[self
.pc
]
113 if self
.parent
is not None:
114 self
.parent
.copy_state(self
)
115 owner
.replace(self
, self
.parent
)
119 action
= self
.actions
[self
.pc
]
121 if isinstance(action
, (parser
.ActionDef
, parser
.ActionRef
)):
122 actions
, params
= action(s_params
, rank
)
123 child
= self
.__class
__(self
, actions
, params
, rank
)
124 owner
.replace(self
, child
)
125 child
.step(owner
, created
)
128 elif action(owner
, self
, s_params
, rank
, created
):
131 class Bullet(object):
132 """Simple bullet implementation.
135 x, y - current X/Y position
136 px, py - X/Y position prior to the last step
137 mx, my - X/Y axis-oriented speed modifier ("acceleration")
138 direction - direction of movement, in radians
139 speed - speed of movement, in units per frame
140 target - object with .x and .y fields for "aim" directions
141 vanished - set to true by a <vanish> action
142 rank - game difficulty, 0 to 1, default 0.5
143 tags - string tags set by the running actions
144 appearance - string used to set bullet appearance
146 Contructor Arguments:
147 x, y, direction, speed, target, rank, tags, appearance
148 - same as the above attributes
149 actions - internal action list
150 Action - custom Action constructor
154 def __init__(self
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
155 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 # New bullets reset the parent hierarchy.
169 self
.actions
= [Action(None, action
, params
, rank
)
170 for action
, params
in actions
]
173 def FromDocument(cls
, doc
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
174 params
=(), rank
=0.5, Action
=Action
):
175 """Construct a new Bullet from a loaded BulletML document."""
176 actions
= [a(params
, rank
) for a
in doc
.actions
]
177 return cls(x
=x
, y
=y
, direction
=direction
, speed
=speed
,
178 target
=target
, actions
=actions
, rank
=rank
, Action
=Action
)
181 return ("%s(%r, %r, accel=%r, direction=%r, speed=%r, "
182 "actions=%r, target=%r, appearance=vanished=%r)") % (
183 type(self
).__name
__, self
.x
, self
.y
, (self
.mx
, self
.my
),
184 self
.direction
, self
.speed
, self
.actions
, self
.target
,
185 self
.appearance
, self
.vanished
)
189 """Angle to the target, in radians.
191 If the target does not exist or cannot be found, return 0.
194 target_x
= self
.target
.x
195 target_y
= self
.target
.y
196 except AttributeError:
199 return atan2(target_x
- self
.x
, target_y
- self
.y
)
203 """Check if this bullet is finished running.
205 A bullet is finished when it has vanished, and all its
206 actions have finished.
208 If this is true, the bullet should be removed from the screen.
209 (You will probably want to cull it under other circumstances
212 if not self
.vanished
:
214 for action
in self
.actions
:
215 if not action
.finished
:
220 """Vanish this bullet and stop all actions."""
222 for action
in self
.actions
:
226 def replace(self
, old
, new
):
227 """Replace an active action with another.
229 This is mostly used by actions internally to queue children.
232 idx
= self
.actions
.index(old
)
236 self
.actions
[idx
] = new
239 """Advance by one frame.
241 This updates the position and velocity, and may also set the
244 It returns any new bullets this bullet spawned during this step.
248 for action
in self
.actions
:
249 action
.step(self
, created
)
253 self
.x
+= self
.mx
+ sin(self
.direction
) * self
.speed
254 self
.y
+= -self
.my
+ cos(self
.direction
) * self
.speed