fcb3aab34ea99da4e76f2c6fdee77b1fd7ded9ce
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 # The Noiz implementation was a little weird here, I think
77 # there was a bug in it that prevented it from working if
78 # the frame count was 1. I'm still not sure what the aim
79 # check is supposed to do, exactly.
80 self
.direction_frames
-= 1
81 if self
.aiming
and self
.direction_frames
<= 0:
82 self
.owner
.direction
+= self
.owner
.aim
84 self
.owner
.direction
+= self
.direction
86 if self
.accel_frames
> 0:
87 self
.accel_frames
-= 1
88 self
.owner
.mx
+= self
.mx
89 self
.owner
.my
+= self
.my
94 if self
.wait_frames
> 0:
102 action
= self
.actions
[self
.pc
]
108 if self
.parent
is not None:
109 self
.parent
.copy_state(self
)
110 self
.owner
.replace(self
, self
.parent
)
114 action
= self
.actions
[self
.pc
]
116 if isinstance(action
, parser
.Repeat
):
117 repeat
, (actions
, params
) = action(self
.params
, self
.rank
)
119 self
.owner
, self
, actions
, params
, self
.rank
, repeat
)
120 self
.owner
.replace(self
, child
)
121 created
.extend(child
.step())
124 elif isinstance(action
, (parser
.ActionDef
, parser
.ActionRef
)):
125 actions
, params
= action(self
.params
, self
.rank
)
126 child
= Action(self
.owner
, self
, actions
, params
, self
.rank
)
127 self
.owner
.replace(self
, child
)
128 created
.extend(child
.step())
131 elif isinstance(action
, (parser
.FireDef
, parser
.FireRef
)):
132 direction
, speed
, actions
= action(self
.params
, self
.rank
)
134 direction
, type = direction
135 if type == "aim" or type is None:
136 direction
+= self
.owner
.aim
137 elif type == "sequence":
138 direction
+= self
.previous_fire_direction
139 elif type == "relative":
140 direction
+= self
.owner
.direction
142 direction
= self
.owner
.aim
143 self
.previous_fire_direction
= direction
147 if type == "sequence":
148 speed
+= self
.previous_fire_speed
149 elif type == "relative":
150 # The reference Noiz implementation uses
151 # prvFireSpeed here, but the standard is
152 # pretty clear -- "0 means that the direction
153 # of this fire and the direction of the bullet
155 speed
+= self
.owner
.speed
158 self
.previous_fire_speed
= speed
160 bullet
= Bullet(self
.owner
.x
, self
.owner
.y
, direction
, speed
,
161 self
.owner
.target
, actions
, self
)
162 created
.append(bullet
)
164 elif isinstance(action
, parser
.ChangeSpeed
):
165 frames
, (speed
, type) = action(self
.params
, self
.rank
)
166 self
.speed_frames
= frames
167 if type == "sequence":
169 elif type == "relative":
170 self
.speed
= speed
/ frames
172 self
.speed
= (speed
- self
.owner
.speed
) / frames
174 elif isinstance(action
, parser
.ChangeDirection
):
175 frames
, (direction
, type) = action(self
.params
, self
.rank
)
176 self
.direction_frames
= frames
178 if type == "sequence":
179 self
.direction
= direction
181 if type == "absolute":
183 direction
- self
.owner
.direction
) % PI_2
184 elif type == "relative":
185 self
.direction
= direction
191 - self
.owner
.direction
) % PI_2
193 if self
.direction
> math
.pi
:
194 self
.direction
-= PI_2
195 if self
.direction
< -math
.pi
:
196 self
.direction
+= PI_2
197 self
.direction
/= self
.direction_frames
199 elif isinstance(action
, parser
.Accel
):
200 frames
, horizontal
, vertical
= action(self
.params
, self
.rank
)
201 self
.accel_frames
= frames
203 mx
, type = horizontal
204 if type == "sequence":
206 elif type == "absolute":
207 self
.mx
= (mx
- self
.owner
.mx
) / frames
208 elif type == "relative":
209 self
.mx
= mx
/ frames
212 if type == "sequence":
214 elif type == "absolute":
215 self
.my
= (my
- self
.owner
.my
) / frames
216 elif type == "relative":
217 self
.my
= my
/ frames
219 elif isinstance(action
, parser
.Wait
):
220 self
.wait_frames
= action(self
.params
, self
.rank
)
223 elif isinstance(action
, parser
.Vanish
):
229 class Bullet(object):
230 """Simple bullet implementation."""
232 def __init__(self
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
233 actions
=(), parent
=None, rank
=None):
238 self
.direction
= direction
240 self
.vanished
= False
243 rank
= parent
.rank
if parent
else 0.5
244 # New bullets reset the parent hierarchy.
245 self
._actions
= [Action(self
, None, action
, params
, rank
)
246 for action
, params
in actions
]
249 return ("%s(%r, %r, accel=%r, direction=%r, speed=%r, "
250 "actions=%r, target=%r, vanished=%r)") % (
251 type(self
).__name
__, self
.x
, self
.y
, (self
.mx
, self
.my
),
252 self
.direction
, self
.speed
, self
._actions
, self
.target
,
257 """Angle to the target."""
258 if self
.target
is None:
259 return self
.direction
261 return math
.atan2(self
.target
.x
- self
.x
, self
.y
- self
.target
.y
)
265 """Check if this bullet is finished running.
267 If this is true, the bullet should be removed from the screen.
268 (You will probably want to cull it under other circumstances
271 if not self
.vanished
:
273 for action
in self
._actions
:
274 if not action
.finished
:
279 """Vanish this bullet and stop all actions."""
281 for action
in self
._actions
:
285 def replace(self
, old
, new
):
286 """Replace an active action with another."""
288 idx
= self
._actions
.index(old
)
292 self
._actions
[idx
] = new
295 """Advance by one frame.
297 This updates the direction, speed, x, y, px, and py members,
298 and may set the vanished flag.
302 for action
in self
._actions
:
303 created
.extend(action
.step())
307 self
.x
+= self
.mx
+ math
.sin(self
.direction
) * self
.speed
308 self
.y
+= self
.my
- math
.cos(self
.direction
) * self
.speed
313 def FromDoc(cls
, doc
, params
=(), x
=0, y
=0, speed
=0, direction
=0,
314 target
=None, rank
=0.5):
315 """Construct a bullet from top-level actions in a document."""
316 actions
= [act(params
, rank
) for act
in doc
.top
]
317 return cls(x
, y
, direction
, speed
, target
, actions
, rank
=rank
)