e2b7c800d03f7d30f5bb0d0fc1c04ee47120e24d
3 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html
6 from __future__
import division
8 from xml
.etree
.ElementTree
import ElementTree
11 from cStringIO
import StringIO
13 from StringIO
import StringIO
15 from bulletml
.errors
import Error
16 from bulletml
.expr
import NumberDef
, INumberDef
18 class ParseError(Error
):
19 """Raised when an error occurs parsing the XML structure."""
23 """Strip namespace poop off the front of a tag."""
25 return element
.tag
.rsplit('}', 1)[1]
29 class ParamList(object):
30 """List of parameter definitions."""
32 def __init__(self
, params
=[]):
33 self
.params
= list(params
)
36 def FromElement(cls
, doc
, element
):
37 """Construct using an ElementTree-style element."""
38 return cls([NumberDef(subelem
.text
) for subelem
in element
39 if realtag(subelem
) == "param"])
41 def __call__(self
, params
, rank
):
42 return [param(params
, rank
) for param
in self
.params
]
45 return "%s(%r)" % (type(self
).__name
__, self
.params
)
47 class Direction(object):
48 """Raw direction value."""
50 VALID_TYPES
= ["relative", "absolute", "aim", "sequence"]
52 def __init__(self
, type, value
):
53 if type not in self
.VALID_TYPES
:
54 raise ValueError("invalid type %r" % type)
59 def FromElement(cls
, doc
, element
, default
="absolute"):
60 """Construct using an ElementTree-style element."""
61 return cls(element
.get("type", default
), NumberDef(element
.text
))
63 def __call__(self
, params
, rank
):
64 return (self
.value(params
, rank
), self
.type)
67 return "%s(%r, type=%r)" % (
68 type(self
).__name
__, self
.value
, self
.type)
70 class ChangeDirection(object):
71 """Direction change over time."""
73 def __init__(self
, term
, direction
):
75 self
.direction
= direction
78 def FromElement(cls
, doc
, element
):
79 """Construct using an ElementTree-style element."""
80 for subelem
in element
.getchildren():
81 tag
= realtag(subelem
)
82 if tag
== "direction":
83 direction
= Direction
.FromElement(doc
, subelem
)
85 term
= INumberDef(subelem
.text
)
87 return cls(term
, direction
)
88 except UnboundLocalError as exc
:
89 raise ParseError(str(exc
))
91 def __call__(self
, params
, rank
):
92 return self
.term(params
, rank
), self
.direction(params
, rank
)
95 return "%s(term=%r, direction=%r)" % (
96 type(self
).__name
__, self
.term
, self
.direction
)
99 """Raw speed value."""
101 VALID_TYPES
= ["relative", "absolute", "sequence"]
103 def __init__(self
, type, value
):
104 if type not in self
.VALID_TYPES
:
105 raise ValueError("invalid type %r" % type)
110 def FromElement(cls
, doc
, element
):
111 """Construct using an ElementTree-style element."""
112 return cls(element
.get("type", "absolute"), NumberDef(element
.text
))
114 def __call__(self
, params
, rank
):
115 return (self
.value(params
, rank
), self
.type)
118 return "%s(%r, type=%r)" % (type(self
).__name
__, self
.value
, self
.type)
120 class ChangeSpeed(object):
121 """Speed change over time."""
123 def __init__(self
, term
, speed
):
128 def FromElement(cls
, doc
, element
):
129 """Construct using an ElementTree-style element."""
130 for subelem
in element
.getchildren():
131 tag
= realtag(subelem
)
133 speed
= Speed
.FromElement(doc
, subelem
)
135 term
= INumberDef(subelem
.text
)
137 return cls(term
, speed
)
138 except UnboundLocalError as exc
:
139 raise ParseError(str(exc
))
141 def __call__(self
, params
, rank
):
142 return self
.term(params
, rank
), self
.speed(params
, rank
)
145 return "%s(term=%r, speed=%r)" % (
146 type(self
).__name
__, self
.term
, self
.speed
)
149 """Wait for some frames."""
151 def __init__(self
, frames
):
155 def FromElement(cls
, doc
, element
):
156 """Construct using an ElementTree-style element."""
157 return cls(INumberDef(element
.text
))
159 def __call__(self
, params
, rank
):
160 return self
.frames(params
, rank
)
163 return "%s(%r)" % (type(self
).__name
__, self
.frames
)
165 class Vanish(object):
166 """Make the owner disappear."""
172 def FromElement(cls
, doc
, element
):
173 """Construct using an ElementTree-style element."""
177 return "%s()" % (type(self
).__name
__)
179 class Repeat(object):
180 """Repeat an action definition."""
182 def __init__(self
, times
, action
):
187 def FromElement(cls
, doc
, element
):
188 """Construct using an ElementTree-style element."""
189 for subelem
in element
.getchildren():
190 tag
= realtag(subelem
)
192 times
= INumberDef(subelem
.text
)
193 elif tag
== "action":
194 action
= ActionDef
.FromElement(doc
, subelem
)
195 elif tag
== "actionRef":
196 action
= ActionRef
.FromElement(doc
, subelem
)
198 return cls(times
, action
)
199 except UnboundLocalError as exc
:
200 raise ParseError(str(exc
))
202 def __call__(self
, params
, rank
):
203 return self
.times(params
, rank
), self
.action(params
, rank
)
206 return "%s(%r, %r)" % (type(self
).__name
__, self
.times
, self
.action
)
209 """Accelerate over some time."""
214 def __init__(self
, term
, horizontal
=None, vertical
=None):
216 self
.horizontal
= horizontal
217 self
.vertical
= vertical
220 def FromElement(cls
, doc
, element
):
221 """Construct using an ElementTree-style element."""
225 for subelem
in element
.getchildren():
226 tag
= realtag(subelem
)
228 term
= INumberDef(subelem
.text
)
229 elif tag
== "horizontal":
230 horizontal
= Speed
.FromElement(doc
, subelem
)
231 elif tag
== "vertical":
232 vertical
= Speed
.FromElement(doc
, subelem
)
235 return cls(term
, horizontal
, vertical
)
236 except AttributeError:
239 def __call__(self
, params
, rank
):
240 frames
= self
.term(params
, rank
)
241 horizontal
= self
.horizontal
and self
.horizontal(params
, rank
)
242 vertical
= self
.vertical
and self
.vertical(params
, rank
)
243 return frames
, horizontal
, vertical
246 return "%s(%r, horizontal=%r, vertical=%r)" % (
247 type(self
).__name
__, self
.term
, self
.horizontal
, self
.vertical
)
249 class BulletDef(object):
250 """Bullet definition."""
255 def __init__(self
, actions
=[], direction
=None, speed
=None):
256 self
.direction
= direction
258 self
.actions
= list(actions
)
261 def FromElement(cls
, doc
, element
):
262 """Construct using an ElementTree-style element."""
266 for subelem
in element
.getchildren():
267 tag
= realtag(subelem
)
268 if tag
== "direction":
269 direction
= Direction
.FromElement(doc
, subelem
)
271 speed
= Speed
.FromElement(doc
, subelem
)
272 elif tag
== "action":
273 actions
.append(ActionDef
.FromElement(doc
, subelem
))
274 elif tag
== "actionRef":
275 actions
.append(ActionRef
.FromElement(doc
, subelem
))
276 dfn
= cls(actions
, direction
, speed
)
277 doc
.bullets
[element
.get("label")] = dfn
280 def __call__(self
, params
, rank
):
281 actions
= [action(params
, rank
) for action
in self
.actions
]
283 self
.direction
and self
.direction(params
, rank
),
284 self
.speed
and self
.speed(params
, rank
),
288 return "%s(direction=%r, speed=%r, actions=%r)" % (
289 type(self
).__name
__, self
.direction
, self
.speed
, self
.actions
)
291 class BulletRef(object):
292 """Create a bullet by name with parameters."""
294 def __init__(self
, bullet
, params
=None):
296 self
.params
= params
or ParamList()
299 def FromElement(cls
, doc
, element
):
300 """Construct using an ElementTree-style element."""
301 bullet
= cls(element
.get("label"), ParamList
.FromElement(doc
, element
))
302 doc
._bullet
_refs
.append(bullet
)
305 def __call__(self
, params
, rank
):
306 return self
.bullet(self
.params(params
, rank
), rank
)
309 return "%s(params=%r, bullet=%r)" % (
310 type(self
).__name
__, self
.params
, self
.bullet
)
312 class ActionDef(object):
313 """Action definition."""
315 # This is self-referential, so it's filled in later.
316 CONSTRUCTORS
= dict()
318 def __init__(self
, actions
):
319 self
.actions
= list(actions
)
322 def FromElement(cls
, doc
, element
):
323 """Construct using an ElementTree-style element."""
325 for subelem
in element
.getchildren():
326 tag
= realtag(subelem
)
328 ctr
= cls
.CONSTRUCTORS
[tag
]
332 actions
.append(ctr
.FromElement(doc
, subelem
))
334 doc
.actions
[element
.get("label")] = dfn
337 def __call__(self
, params
, rank
):
338 return self
.actions
, params
341 return "%s(%r)" % (type(self
).__name
__, self
.actions
)
343 class ActionRef(object):
344 """Run an action by name with parameters."""
346 def __init__(self
, action
, params
=None):
348 self
.params
= params
or ParamList()
351 def FromElement(cls
, doc
, element
):
352 """Construct using an ElementTree-style element."""
353 action
= cls(element
.get("label"), ParamList
.FromElement(doc
, element
))
354 doc
._action
_refs
.append(action
)
357 def __call__(self
, params
, rank
):
358 return self
.action(self
.params(params
, rank
), rank
)
361 return "%s(params=%r, action=%r)" % (
362 type(self
).__name
__, self
.params
, self
.action
)
364 class FireDef(object):
365 """Fire definition (creates a bullet)."""
367 def __init__(self
, bullet
, direction
=None, speed
=None):
369 self
.direction
= direction
373 def FromElement(cls
, doc
, element
):
374 """Construct using an ElementTree-style element."""
378 for subelem
in element
.getchildren():
379 tag
= realtag(subelem
)
380 if tag
== "direction":
381 direction
= Direction
.FromElement(doc
, subelem
, "aim")
383 speed
= Speed
.FromElement(doc
, subelem
)
384 elif tag
== "bullet":
385 bullet
= BulletDef
.FromElement(doc
, subelem
)
386 elif tag
== "bulletRef":
387 bullet
= BulletRef
.FromElement(doc
, subelem
)
390 fire
= cls(bullet
, direction
, speed
)
391 except UnboundLocalError as exc
:
392 raise ParseError(str(exc
))
394 doc
.fires
[element
.get("label")] = fire
397 def __call__(self
, params
, rank
):
398 direction
, speed
, actions
= self
.bullet(params
, rank
)
400 direction
= self
.direction(params
, rank
)
402 speed
= self
.speed(params
, rank
)
403 return direction
, speed
, actions
406 return "%s(direction=%r, speed=%r, bullet=%r)" % (
407 type(self
).__name
__, self
.direction
, self
.speed
, self
.bullet
)
409 class FireRef(object):
410 """Fire a bullet by name with parameters."""
412 def __init__(self
, fire
, params
=None):
414 self
.params
= params
or ParamList()
417 def FromElement(cls
, doc
, element
):
418 """Construct using an ElementTree-style element."""
419 fired
= cls(element
.get("label"), ParamList
.FromElement(doc
, element
))
420 doc
._fire
_refs
.append(fired
)
423 def __call__(self
, params
, rank
):
424 """Generate a Bullet from the FireDef and params."""
425 return self
.fire(self
.params(params
, rank
), rank
)
428 return "%s(params=%r, fire=%r)" % (
429 type(self
).__name
__, self
.params
, self
.fire
)
431 class BulletML(object):
432 """BulletML document.
434 A BulletML document is a collection of bullets, actions, and
435 firings, as well as a base game type.
444 def __init__(self
, source
):
449 self
._bullet
_refs
= []
450 self
._action
_refs
= []
453 if isinstance(source
, (str, unicode)):
454 source
= StringIO(source
)
457 root
= tree
.parse(source
)
459 self
.type = root
.get("type", "none")
461 for element
in root
.getchildren():
462 tag
= realtag(element
)
463 if tag
in self
.CONSTRUCTORS
:
464 self
.CONSTRUCTORS
[tag
].FromElement(self
, element
)
467 for ref
in self
._bullet
_refs
:
468 ref
.bullet
= self
.bullets
[ref
.bullet
]
469 for ref
in self
._fire
_refs
:
470 ref
.fire
= self
.fires
[ref
.fire
]
471 for ref
in self
._action
_refs
:
472 ref
.action
= self
.actions
[ref
.action
]
473 except KeyError as exc
:
474 raise ParseError("unknown reference %s" % exc
)
476 del(self
._bullet
_refs
)
477 del(self
._action
_refs
)
480 self
.bullets
.pop(None, None)
481 self
.actions
.pop(None, None)
482 self
.fires
.pop(None, None)
486 """Get a list of all top-level actions."""
487 return [dfn
for name
, dfn
in self
.actions
.iteritems()
488 if name
and name
.startswith("top")]
491 return "%s(type=%r, bullets=%r, actions=%r, fires=%r)" % (
492 type(self
).__name
__, self
.type, self
.bullets
, self
.actions
,
495 ActionDef
.CONSTRUCTORS
= dict(
499 changeSpeed
=ChangeSpeed
,
500 changeDirection
=ChangeDirection
,