1 """BulletML implementation.
3 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html
6 from __future__
import division
10 from bulletml
import parser
12 # TODO(jfw): This is very non-Pythonic, it's pretty much just the
13 # BulletML reference ActionImpl translated to Python.
18 """Running action implementation."""
20 def __init__(self
, owner
, parent
, actions
, params
, rank
, repeat
=1):
21 self
.actions
= actions
28 self
.direction_frames
= 0
34 self
.previous_fire_direction
= 0
35 self
.previous_fire_speed
= 0
41 self
.copy_state(parent
)
44 return "%s(pc=%r, actions=%r)" % (
45 type(self
).__name
__, self
.pc
, self
.actions
)
48 """End this action and its parents."""
54 def copy_state(self
, other
):
55 """Copy fire/movement state from other to self."""
56 self
.direction_frames
= other
.direction_frames
57 self
.direction
= other
.direction
58 self
.aiming
= other
.aiming
59 self
.speed_frames
= other
.speed_frames
60 self
.speed
= other
.speed
61 self
.accel_frames
= other
.accel_frames
64 self
.previous_fire_direction
= other
.previous_fire_direction
65 self
.previous_fire_speed
= other
.previous_fire_speed
68 """Advance by one frame."""
71 if self
.speed_frames
> 0:
72 self
.speed_frames
-= 1
73 self
.owner
.speed
+= self
.speed
75 if self
.direction_frames
> 0:
76 self
.direction_frames
-= 1
77 if self
.direction_frames
<= 0:
79 self
.owner
.direction
+= self
.owner
.aim
81 self
.owner
.direction
+= self
.direction
83 if self
.accel_frames
> 0:
84 self
.accel_frames
-= 1
85 self
.owner
.mx
+= self
.mx
86 self
.owner
.my
+= self
.my
91 if self
.wait_frames
> 0:
99 action
= self
.actions
[self
.pc
]
105 if self
.parent
is not None:
106 self
.parent
.copy_state(self
)
107 self
.owner
.replace(self
, self
.parent
)
111 action
= self
.actions
[self
.pc
]
113 if isinstance(action
, parser
.Repeat
):
114 repeat
, (actions
, params
) = action(self
.params
, self
.rank
)
116 self
.owner
, self
, actions
, params
, self
.rank
, repeat
)
117 self
.owner
.replace(self
, child
)
118 created
.extend(child
.step())
121 elif isinstance(action
, (parser
.ActionDef
, parser
.ActionRef
)):
122 actions
, params
= action(self
.params
, self
.rank
)
123 child
= Action(self
.owner
, self
, actions
, params
, self
.rank
)
124 self
.owner
.replace(self
, child
)
125 created
.extend(child
.step())
128 elif isinstance(action
, (parser
.FireDef
, parser
.FireRef
)):
129 direction
, speed
, actions
= action(self
.params
, self
.rank
)
131 direction
, type = direction
132 if type == "aim" or type is None:
133 direction
+= self
.owner
.aim
134 elif type == "sequence":
135 direction
+= self
.previous_fire_direction
136 elif type == "relative":
137 direction
+= self
.owner
.direction
139 direction
= self
.owner
.aim
140 self
.previous_fire_direction
= direction
144 if type == "sequence":
145 speed
+= self
.previous_fire_speed
146 elif type == "relative":
147 # The reference Noiz implementation uses
148 # prvFireSpeed here, but the standard is
149 # pretty clear -- "0 means that the direction
150 # of this fire and the direction of the bullet
152 speed
+= self
.owner
.speed
155 self
.previous_fire_speed
= speed
157 bullet
= Bullet(self
.owner
.x
, self
.owner
.y
, direction
, speed
,
158 self
.owner
.target
, actions
, self
)
159 created
.append(bullet
)
161 elif isinstance(action
, parser
.ChangeSpeed
):
162 frames
, (speed
, type) = action(self
.params
, self
.rank
)
163 self
.speed_frames
= frames
164 if type == "sequence":
166 elif type == "relative":
167 self
.speed
= speed
/ frames
169 self
.speed
= (speed
- self
.owner
.speed
) / frames
171 elif isinstance(action
, parser
.ChangeDirection
):
172 frames
, (direction
, type) = action(self
.params
, self
.rank
)
173 self
.direction_frames
= frames
175 if type == "sequence":
176 self
.direction
= direction
178 if type == "absolute":
180 direction
- self
.owner
.direction
) % PI_2
181 elif type == "relative":
182 self
.direction
= direction
188 - self
.owner
.direction
) % PI_2
190 if self
.direction
> math
.pi
:
191 self
.direction
-= PI_2
192 if self
.direction
< -math
.pi
:
193 self
.direction
+= PI_2
194 self
.direction
/= self
.direction_frames
196 elif isinstance(action
, parser
.Accel
):
197 frames
, horizontal
, vertical
= action(self
.params
, self
.rank
)
198 self
.accel_frames
= frames
200 mx
, type = horizontal
201 if type == "sequence":
203 elif type == "absolute":
204 self
.mx
= (mx
- self
.owner
.mx
) / frames
205 elif type == "relative":
206 self
.mx
= mx
/ frames
209 if type == "sequence":
211 elif type == "absolute":
212 self
.my
= (my
- self
.owner
.my
) / frames
213 elif type == "relative":
214 self
.my
= my
/ frames
216 elif isinstance(action
, parser
.Wait
):
217 self
.wait_frames
= action(self
.params
, self
.rank
)
220 elif isinstance(action
, parser
.Vanish
):
226 class Bullet(object):
227 """Simple bullet implementation."""
229 def __init__(self
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
230 actions
=(), parent
=None, rank
=None):
235 self
.direction
= direction
237 self
.vanished
= False
240 rank
= parent
.rank
if parent
else 0.5
241 # New bullets reset the parent hierarchy.
242 self
._actions
= [Action(self
, None, action
, params
, rank
)
243 for action
, params
in actions
]
246 return ("%s(%r, %r, accel=%r, direction=%r, speed=%r, "
247 "actions=%r, target=%r, vanished=%r)") % (
248 type(self
).__name
__, self
.x
, self
.y
, (self
.mx
, self
.my
),
249 self
.direction
, self
.speed
, self
._actions
, self
.target
,
254 """Angle to the target."""
255 if self
.target
is None:
256 return self
.direction
258 return math
.atan2(self
.target
.x
- self
.x
, self
.y
- self
.target
.y
)
262 """Check if this bullet is finished running.
264 If this is true, the bullet should be removed from the screen.
265 (You will probably want to cull it under other circumstances
268 if not self
.vanished
:
270 for action
in self
._actions
:
271 if not action
.finished
:
276 """Vanish this bullet and stop all actions."""
278 for action
in self
._actions
:
282 def replace(self
, old
, new
):
283 """Replace an active action with another."""
285 idx
= self
._actions
.index(old
)
289 self
._actions
[idx
] = new
292 """Advance by one frame.
294 This updates the direction, speed, x, y, px, and py members,
295 and may set the vanished flag.
299 for action
in self
._actions
:
300 created
.extend(action
.step())
304 self
.x
+= self
.mx
+ math
.sin(self
.direction
) * self
.speed
305 self
.y
+= self
.my
- math
.cos(self
.direction
) * self
.speed
310 def FromDoc(cls
, doc
, params
=(), x
=0, y
=0, speed
=0, direction
=0,
311 target
=None, rank
=0.5):
312 """Construct a bullet from top-level actions in a document."""
313 actions
= [act(params
, rank
) for act
in doc
.top
]
314 return cls(x
, y
, direction
, speed
, target
, actions
, rank
=rank
)