9df26c62a865f555458c8e0a1acfb6e16ee2bddc
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
):
21 self
.actions
= actions
27 self
.aim_direction
= 0
29 self
.direction_frames
= 0
37 self
.previous_fire_direction
= 0
38 self
.previous_fire_speed
= 0
44 self
.copy_state(parent
)
47 return "%s(pc=%r, actions=%r)" % (
48 type(self
).__name
__, self
.pc
, self
.actions
)
51 """End this action and its parents."""
57 def copy_state(self
, other
):
58 """Copy fire/movement state from other to self."""
59 self
.direction_frames
= other
.direction_frames
60 self
.direction
= other
.direction
61 self
.aiming
= other
.aiming
62 self
.speed_frames
= other
.speed_frames
63 self
.speed
= other
.speed
64 self
.accel_frames
= other
.accel_frames
67 self
.previous_fire_direction
= other
.previous_fire_direction
68 self
.previous_fire_speed
= other
.previous_fire_speed
71 """Advance by one frame."""
74 if self
.speed_frames
> 0:
75 self
.speed_frames
-= 1
76 self
.owner
.speed
+= self
.speed
78 if self
.direction_frames
> 0:
79 self
.direction_frames
-= 1
80 if 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:
101 if self
.pc
>= len(self
.actions
):
106 if self
.parent
is not None:
107 self
.parent
.copy_state(self
)
108 self
.owner
.replace(self
, self
.parent
)
113 action
= self
.actions
[self
.pc
]
115 if isinstance(action
, parser
.Repeat
):
116 repeat
, (actions
, params
) = action(self
.params
, self
.rank
)
117 child
= Action(self
.owner
, self
, actions
, params
, self
.rank
)
118 child
.repeat
= repeat
119 self
.owner
.replace(self
, child
)
120 created
.extend(child
.step())
123 elif isinstance(action
, (parser
.ActionDef
, parser
.ActionRef
)):
124 actions
, params
= action(self
.params
, self
.rank
)
125 child
= Action(self
.owner
, self
, actions
, params
, self
.rank
)
126 self
.owner
.replace(self
, child
)
127 created
.extend(child
.step())
130 elif isinstance(action
, (parser
.FireDef
, parser
.FireRef
)):
131 direction
, speed
, actions
= action(self
.params
, self
.rank
)
133 direction
, type = direction
134 if type == "aim" or type is None:
135 direction
+= self
.owner
.aim
136 elif type == "sequence":
137 direction
+= self
.previous_fire_direction
138 elif type == "relative":
139 direction
+= self
.owner
.direction
141 direction
= self
.owner
.aim
142 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":
180 self
.direction
= direction
182 if type == "absolute":
185 direction
- self
.owner
.direction
) % PI_2
186 elif type == "relative":
188 self
.direction
= direction
194 - self
.owner
.direction
) % PI_2
196 while self
.direction
> math
.pi
:
197 self
.direction
-= PI_2
198 while self
.direction
< -math
.pi
:
199 self
.direction
+= PI_2
200 self
.direction
/= self
.direction_frames
202 elif isinstance(action
, parser
.Accel
):
203 frames
, horizontal
, vertical
= action(self
.params
, self
.rank
)
204 self
.accel_frames
= frames
206 mx
, type = horizontal
207 if type == "sequence":
209 elif type == "absolute":
210 self
.mx
= (mx
- self
.owner
.mx
) / frames
211 elif type == "relative":
212 self
.mx
= mx
/ frames
215 if type == "sequence":
217 elif type == "absolute":
218 self
.my
= (my
- self
.owner
.my
) / frames
219 elif type == "relative":
220 self
.my
= my
/ frames
222 elif isinstance(action
, parser
.Wait
):
223 self
.wait_frames
= action(self
.params
, self
.rank
)
226 elif isinstance(action
, parser
.Vanish
):
232 class Bullet(object):
233 """Simple bullet implementation."""
235 def __init__(self
, x
=0, y
=0, direction
=0, speed
=0, target
=None,
236 actions
=(), parent
=None, rank
=None):
241 self
.direction
= direction
243 self
.vanished
= False
247 rank
= parent
.rank
if parent
else 0.5
248 # New bullets reset the parent hierarchy.
249 self
.actions
= [Action(self
, None, action
, params
, rank
)
250 for action
, params
in actions
]
253 return ("%s(%r, %r, accel=%r, direction=%r, speed=%r, "
254 "actions=%r, target=%r, vanished=%r)") % (
255 type(self
).__name
__, self
.x
, self
.y
, (self
.mx
, self
.my
),
256 self
.direction
, self
.speed
, self
.actions
, self
.target
,
261 """Angle to the target."""
262 if self
.target
is None:
263 return self
.direction
265 return math
.atan2(self
.target
.x
- self
.x
, self
.y
- self
.target
.y
)
269 """Check if this bullet is finished running."""
270 if not self
.vanished
:
272 for action
in self
.actions
:
273 if not action
.finished
:
278 """Vanish this bullet and stop all actions."""
280 for action
in self
.actions
:
284 def replace(self
, old
, new
):
285 """Replace an active action with another."""
287 idx
= self
.actions
.index(old
)
291 self
.actions
[idx
] = new
294 """Advance by one frame."""
297 for action
in self
.actions
:
298 created
.extend(action
.step())
302 self
.x
+= self
.mx
+ math
.sin(self
.direction
) * self
.speed
303 self
.y
+= self
.my
- math
.cos(self
.direction
) * self
.speed
308 def FromDoc(cls
, doc
, params
=(), x
=0, y
=0, speed
=0, direction
=0,
309 target
=None, rank
=0.5):
310 """Construct a bullet from top-level actions in a document."""
311 actions
= [act(params
, rank
) for act
in doc
.top
]
312 return cls(x
, y
, direction
, speed
, target
, actions
, rank
=rank
)