edb9d29e4c544bae3506e1681cbe25141a78c1db
3 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html
6 from __future__
import division
10 from xml
.etree
.ElementTree
import ElementTree
13 from cStringIO
import StringIO
15 from StringIO
import StringIO
17 from bulletml
.errors
import Error
18 from bulletml
.expr
import NumberDef
, INumberDef
20 class ParseError(Error
):
21 """Raised when an error occurs parsing the XML structure."""
25 """Strip namespace poop off the front of a tag."""
27 return element
.tag
.rsplit('}', 1)[1]
31 class ParamList(object):
32 """List of parameter definitions."""
34 def __init__(self
, params
=[]):
35 self
.params
= list(params
)
38 def FromElement(cls
, doc
, element
):
39 """Construct using an ElementTree-style element."""
40 return cls([NumberDef(subelem
.text
) for subelem
in element
41 if realtag(subelem
) == "param"])
43 def __call__(self
, params
, rank
):
44 return [param(params
, rank
) for param
in self
.params
]
47 return "%s(%r)" % (type(self
).__name
__, self
.params
)
49 class Direction(object):
50 """Raw direction value."""
52 VALID_TYPES
= ["relative", "absolute", "aim", "sequence"]
54 def __init__(self
, type, value
):
55 if type not in self
.VALID_TYPES
:
56 raise ValueError("invalid type %r" % type)
61 def FromElement(cls
, doc
, element
, default
="absolute"):
62 """Construct using an ElementTree-style element."""
63 return cls(element
.get("type", default
), NumberDef(element
.text
))
65 def __call__(self
, params
, rank
):
66 return (math
.radians(self
.value(params
, rank
)), self
.type)
69 return "%s(%r, type=%r)" % (
70 type(self
).__name
__, self
.value
, self
.type)
72 class ChangeDirection(object):
73 """Direction change over time."""
75 def __init__(self
, term
, direction
):
77 self
.direction
= direction
80 def FromElement(cls
, doc
, element
):
81 """Construct using an ElementTree-style element."""
82 for subelem
in element
.getchildren():
83 tag
= realtag(subelem
)
84 if tag
== "direction":
85 direction
= Direction
.FromElement(doc
, subelem
)
87 term
= INumberDef(subelem
.text
)
89 return cls(term
, direction
)
90 except UnboundLocalError as exc
:
91 raise ParseError(str(exc
))
93 def __call__(self
, params
, rank
):
94 return self
.term(params
, rank
), self
.direction(params
, rank
)
97 return "%s(term=%r, direction=%r)" % (
98 type(self
).__name
__, self
.term
, self
.direction
)
101 """Raw speed value."""
103 VALID_TYPES
= ["relative", "absolute", "sequence"]
105 def __init__(self
, type, value
):
106 if type not in self
.VALID_TYPES
:
107 raise ValueError("invalid type %r" % type)
112 def FromElement(cls
, doc
, element
):
113 """Construct using an ElementTree-style element."""
114 return cls(element
.get("type", "absolute"), NumberDef(element
.text
))
116 def __call__(self
, params
, rank
):
117 return (self
.value(params
, rank
), self
.type)
120 return "%s(%r, type=%r)" % (type(self
).__name
__, self
.value
, self
.type)
122 class ChangeSpeed(object):
123 """Speed change over time."""
125 def __init__(self
, term
, speed
):
130 def FromElement(cls
, doc
, element
):
131 """Construct using an ElementTree-style element."""
132 for subelem
in element
.getchildren():
133 tag
= realtag(subelem
)
135 speed
= Speed
.FromElement(doc
, subelem
)
137 term
= INumberDef(subelem
.text
)
139 return cls(term
, speed
)
140 except UnboundLocalError as exc
:
141 raise ParseError(str(exc
))
143 def __call__(self
, params
, rank
):
144 return self
.term(params
, rank
), self
.speed(params
, rank
)
147 return "%s(term=%r, speed=%r)" % (
148 type(self
).__name
__, self
.term
, self
.speed
)
151 """Wait for some frames."""
153 def __init__(self
, frames
):
157 def FromElement(cls
, doc
, element
):
158 """Construct using an ElementTree-style element."""
159 return cls(INumberDef(element
.text
))
161 def __call__(self
, params
, rank
):
162 return self
.frames(params
, rank
)
165 return "%s(%r)" % (type(self
).__name
__, self
.frames
)
167 class Vanish(object):
168 """Make the owner disappear."""
174 def FromElement(cls
, doc
, element
):
175 """Construct using an ElementTree-style element."""
179 return "%s()" % (type(self
).__name
__)
181 class Repeat(object):
182 """Repeat an action definition."""
184 def __init__(self
, times
, action
):
189 def FromElement(cls
, doc
, element
):
190 """Construct using an ElementTree-style element."""
191 for subelem
in element
.getchildren():
192 tag
= realtag(subelem
)
194 times
= INumberDef(subelem
.text
)
195 elif tag
== "action":
196 action
= ActionDef
.FromElement(doc
, subelem
)
197 elif tag
== "actionRef":
198 action
= ActionRef
.FromElement(doc
, subelem
)
200 return cls(times
, action
)
201 except UnboundLocalError as exc
:
202 raise ParseError(str(exc
))
204 def __call__(self
, params
, rank
):
205 return self
.times(params
, rank
), self
.action(params
, rank
)
208 return "%s(%r, %r)" % (type(self
).__name
__, self
.times
, self
.action
)
211 """Accelerate over some time."""
216 def __init__(self
, term
, horizontal
=None, vertical
=None):
218 self
.horizontal
= horizontal
219 self
.vertical
= vertical
222 def FromElement(cls
, doc
, element
):
223 """Construct using an ElementTree-style element."""
227 for subelem
in element
.getchildren():
228 tag
= realtag(subelem
)
230 term
= INumberDef(subelem
.text
)
231 elif tag
== "horizontal":
232 horizontal
= Speed
.FromElement(doc
, subelem
)
233 elif tag
== "vertical":
234 vertical
= Speed
.FromElement(doc
, subelem
)
237 return cls(term
, horizontal
, vertical
)
238 except AttributeError:
241 def __call__(self
, params
, rank
):
242 frames
= self
.term(params
, rank
)
243 horizontal
= self
.horizontal
and self
.horizontal(params
, rank
)
244 vertical
= self
.vertical
and self
.vertical(params
, rank
)
245 return frames
, horizontal
, vertical
248 return "%s(%r, horizontal=%r, vertical=%r)" % (
249 type(self
).__name
__, self
.term
, self
.horizontal
, self
.vertical
)
251 class BulletDef(object):
252 """Bullet definition."""
257 def __init__(self
, actions
=[], direction
=None, speed
=None):
258 self
.direction
= direction
260 self
.actions
= list(actions
)
263 def FromElement(cls
, doc
, element
):
264 """Construct using an ElementTree-style element."""
268 for subelem
in element
.getchildren():
269 tag
= realtag(subelem
)
270 if tag
== "direction":
271 direction
= Direction
.FromElement(doc
, subelem
)
273 speed
= Speed
.FromElement(doc
, subelem
)
274 elif tag
== "action":
275 actions
.append(ActionDef
.FromElement(doc
, subelem
))
276 elif tag
== "actionRef":
277 actions
.append(ActionRef
.FromElement(doc
, subelem
))
278 dfn
= cls(actions
, direction
, speed
)
279 doc
.bullets
[element
.get("label")] = dfn
282 def __call__(self
, params
, rank
):
283 actions
= [action(params
, rank
) for action
in self
.actions
]
285 self
.direction
and self
.direction(params
, rank
),
286 self
.speed
and self
.speed(params
, rank
),
290 return "%s(direction=%r, speed=%r, actions=%r)" % (
291 type(self
).__name
__, self
.direction
, self
.speed
, self
.actions
)
293 class BulletRef(object):
294 """Create a bullet by name with parameters."""
296 def __init__(self
, bullet
, params
=None):
298 self
.params
= params
or ParamList()
301 def FromElement(cls
, doc
, element
):
302 """Construct using an ElementTree-style element."""
303 bullet
= cls(element
.get("label"), ParamList
.FromElement(doc
, element
))
304 doc
._bullet
_refs
.append(bullet
)
307 def __call__(self
, params
, rank
):
308 return self
.bullet(self
.params(params
, rank
), rank
)
311 return "%s(params=%r, bullet=%r)" % (
312 type(self
).__name
__, self
.params
, self
.bullet
)
314 class ActionDef(object):
315 """Action definition."""
317 # This is self-referential, so it's filled in later.
318 CONSTRUCTORS
= dict()
320 def __init__(self
, actions
):
321 self
.actions
= list(actions
)
324 def FromElement(cls
, doc
, element
):
325 """Construct using an ElementTree-style element."""
327 for subelem
in element
.getchildren():
328 tag
= realtag(subelem
)
330 ctr
= cls
.CONSTRUCTORS
[tag
]
334 actions
.append(ctr
.FromElement(doc
, subelem
))
336 doc
.actions
[element
.get("label")] = dfn
339 def __call__(self
, params
, rank
):
340 return self
.actions
, params
343 return "%s(%r)" % (type(self
).__name
__, self
.actions
)
345 class ActionRef(object):
346 """Run an action by name with parameters."""
348 def __init__(self
, action
, params
=None):
350 self
.params
= params
or ParamList()
353 def FromElement(cls
, doc
, element
):
354 """Construct using an ElementTree-style element."""
355 action
= cls(element
.get("label"), ParamList
.FromElement(doc
, element
))
356 doc
._action
_refs
.append(action
)
359 def __call__(self
, params
, rank
):
360 return self
.action(self
.params(params
, rank
), rank
)
363 return "%s(params=%r, action=%r)" % (
364 type(self
).__name
__, self
.params
, self
.action
)
366 class FireDef(object):
367 """Fire definition (creates a bullet)."""
369 def __init__(self
, bullet
, direction
=None, speed
=None):
371 self
.direction
= direction
375 def FromElement(cls
, doc
, element
):
376 """Construct using an ElementTree-style element."""
380 for subelem
in element
.getchildren():
381 tag
= realtag(subelem
)
382 if tag
== "direction":
383 direction
= Direction
.FromElement(doc
, subelem
, "aim")
385 speed
= Speed
.FromElement(doc
, subelem
)
386 elif tag
== "bullet":
387 bullet
= BulletDef
.FromElement(doc
, subelem
)
388 elif tag
== "bulletRef":
389 bullet
= BulletRef
.FromElement(doc
, subelem
)
392 fire
= cls(bullet
, direction
, speed
)
393 except UnboundLocalError as exc
:
394 raise ParseError(str(exc
))
396 doc
.fires
[element
.get("label")] = fire
399 def __call__(self
, params
, rank
):
400 direction
, speed
, actions
= self
.bullet(params
, rank
)
402 direction
= self
.direction(params
, rank
)
404 speed
= self
.speed(params
, rank
)
405 return direction
, speed
, actions
408 return "%s(direction=%r, speed=%r, bullet=%r)" % (
409 type(self
).__name
__, self
.direction
, self
.speed
, self
.bullet
)
411 class FireRef(object):
412 """Fire a bullet by name with parameters."""
414 def __init__(self
, fire
, params
=None):
416 self
.params
= params
or ParamList()
419 def FromElement(cls
, doc
, element
):
420 """Construct using an ElementTree-style element."""
421 fired
= cls(element
.get("label"), ParamList
.FromElement(doc
, element
))
422 doc
._fire
_refs
.append(fired
)
425 def __call__(self
, params
, rank
):
426 """Generate a Bullet from the FireDef and params."""
427 return self
.fire(self
.params(params
, rank
), rank
)
430 return "%s(params=%r, fire=%r)" % (
431 type(self
).__name
__, self
.params
, self
.fire
)
433 class BulletML(object):
434 """BulletML document.
436 A BulletML document is a collection of bullets, actions, and
437 firings, as well as a base game type.
446 def __init__(self
, type="none", bullets
={}, fires
={}, actions
={}):
453 def FromDocument(cls
, source
):
454 if isinstance(source
, (str, unicode)):
455 source
= StringIO(source
)
458 root
= tree
.parse(source
)
460 self
= cls(type=root
.get("type", "none"))
462 self
._bullet
_refs
= []
463 self
._action
_refs
= []
466 for element
in root
.getchildren():
467 tag
= realtag(element
)
468 if tag
in self
.CONSTRUCTORS
:
469 self
.CONSTRUCTORS
[tag
].FromElement(self
, element
)
472 for ref
in self
._bullet
_refs
:
473 ref
.bullet
= self
.bullets
[ref
.bullet
]
474 for ref
in self
._fire
_refs
:
475 ref
.fire
= self
.fires
[ref
.fire
]
476 for ref
in self
._action
_refs
:
477 ref
.action
= self
.actions
[ref
.action
]
478 except KeyError as exc
:
479 raise ParseError("unknown reference %s" % exc
)
481 del(self
._bullet
_refs
)
482 del(self
._action
_refs
)
485 self
.bullets
.pop(None, None)
486 self
.actions
.pop(None, None)
487 self
.fires
.pop(None, None)
493 """Get a list of all top-level actions."""
494 return [dfn
for name
, dfn
in self
.actions
.iteritems()
495 if name
and name
.startswith("top")]
498 return "%s(type=%r, bullets=%r, actions=%r, fires=%r)" % (
499 type(self
).__name
__, self
.type, self
.bullets
, self
.actions
,
502 ActionDef
.CONSTRUCTORS
= dict(
506 changeSpeed
=ChangeSpeed
,
507 changeDirection
=ChangeDirection
,