From cbf6542a1876a86e4f75c96bad92f653ed924618 Mon Sep 17 00:00:00 2001 From: Joe Wreschnig Date: Mon, 15 Mar 2010 22:47:00 -0700 Subject: [PATCH] Implementation. --- bulletml/__init__.py | 1 + bulletml/impl.py | 274 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 bulletml/impl.py diff --git a/bulletml/__init__.py b/bulletml/__init__.py index 9dc15d1..4643d00 100644 --- a/bulletml/__init__.py +++ b/bulletml/__init__.py @@ -4,3 +4,4 @@ http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html """ from bulletml.parser import BulletML +from bulletml.impl import Bullet, Action diff --git a/bulletml/impl.py b/bulletml/impl.py new file mode 100644 index 0000000..c228a33 --- /dev/null +++ b/bulletml/impl.py @@ -0,0 +1,274 @@ +"""BulletML implementation. + +http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html +""" + +from __future__ import division + +import math + +from bulletml import parser, errors + +# TODO(jfw): This is very non-Pythonic, it's pretty much just the +# BulletML reference ActionImpl translated to Python. + +class Action(object): + """Running action implementation.""" + + def __init__(self, owner, parent, actions, params, rank): + self.actions = actions + self.parent = parent + self.repeat = 0 + self.wait_frames = 0 + self.speed = 0 + self.speed_frames = 0 + self.aim_direction = 0 + self.direction = 0 + self.direction_frames = 0 + self.aiming = False + self.aim_mx = 0 + self.aim_my = 0 + self.mx = 0 + self.my = 0 + self.owner = owner + self.accel_frames = 0 + self.previous_fire_direction = 0 + self.previous_fire_speed = 0 + self.params = params + self.rank = rank + self.pc = -1 + if parent: + self.copy_state(parent) + + def vanish(self): + if self.parent: + self.parent.vanish() + self.repeat = 0 + self.pc = None + + def copy_state(self, other): + self.direction_frames = other.direction_frames + self.direction = other.direction + self.aiming = other.aiming + self.speed_frames = other.speed_frames + self.speed = other.speed + self.accel_frames = other.accel_frames + self.mx = other.mx + self.my = other.my + self.previous_fire_direction = other.previous_fire_direction + self.previous_fire_speed = other.previous_fire_speed + + def step(self): + created = [] + + if self.speed_frames > 0: + self.speed_frames -= 1 + self.owner.speed += self.speed + if self.direction_frames > 0: + self.direction_frames -= 1 + if self.direction_frames <= 0: + if self.aiming: + self.owner.direction += self.owner.aim + else: + self.owner.direction += self.direction + if self.accel_frames > 0: + self.accel_frames -= 1 + self.owner.mx += self.mx + self.owner.my += self.my + + if self.pc is None: + return created + + if self.wait_frames > 0: + self.wait_frames -= 1 + return created + + while True: + self.pc += 1 + if self.pc >= len(self.actions): + self.repeat -= 1 + if self.repeat <= 0: + if self.parent is not None: + self.parent.copy_state(self) + self.owner.replace(self, self.parent) + break + else: + self.pc = 0 + + action = self.actions[self.pc] + + if isinstance(action, parser.Repeat): + repeat, (actions, params) = action(self.params, self.rank) + child = Action(self.owner, self, actions, params, self.rank) + child.repeat = repeat + self.owner.replace(self, child) + created.extend(child.step()) + break + + elif isinstance(action, (parser.ActionDef, parser.ActionRef)): + action, params = action(self.params, self.rank) + child = Action(self.owner, self, actions, params, self.rank) + self.owner.replace(self, child) + created.extend(child.step()) + break + + elif isinstance(action, (parser.FireDef, parser.FireRef)): + direction, speed, actions = action(self.params, self.rank) + if direction: + direction, type = direction + if type == "aim": + direction += self.owner.aim + elif type == "sequence": + direction += self.previous_fire_direction + elif type == "relative": + direction += self.owner.direction + else: + direction = self.owner.aim + self.previous_fire_direction = direction + + + if speed: + speed, type = speed + if type == "sequence": + speed += self.previous_fire_speed + elif type == "relative": + # FIXME(jfw): Reference impl uses prvFireSpeed + # here? That doesn't seem right at all. + speed += self.owner.speed + else: + speed = 1 + self.previous_fire_speed = speed + + bullet = Bullet(self.owner.x, self.owner.y, direction, speed, + self.owner.target, actions, self) + created.append(bullet) + + elif isinstance(action, parser.ChangeSpeed): + frames, (speed, type) = action(self.params, self.rank) + self.speed_frames = frames + if type == "sequence": + self.speed = speed + elif type == "relative": + self.speed = speed / frames + else: + self.speed = (speed - self.owner.speed) / frames + + elif isinstance(action, parser.ChangeDirection): + frames, (direction, type) = action(self.params, self.rank) + self.direction_frames = frames + self.aiming = False + if type == "sequence": + self.aiming = False + self.direction = direction + else: + if type == "absolute": + self.aiming = False + self.direction = ( + direction - self.owner.direction) % 360 + elif type == "relative": + self.aiming = False + self.direction = direction + else: + self.aiming = True + self.direction = ( + direction + + self.owner.aim + - self.owner.direction) % 360 + + while self.direction > 180: + self.direction -= 360 + while self.direction < -180: + self.direction += 360 + self.direction /= self.direction_frames + + elif isinstance(action, parser.Accel): + frames, horizontal, vertical = action(self.params, self.rank) + self.accel_frames = frames + if horizontal: + mx, type = horizontal + if type == "sequence": + self.mx = mx + elif type == "absolute": + self.mx = (mx - bullet.mx) / frames + elif type == "relative": + self.mx = mx / frames + if vertical: + my, type = vertical + if type == "sequence": + self.my = my + elif type == "absolute": + self.my = (my - bullet.my) / frames + elif type == "relative": + self.my = my / frames + + elif isinstance(action, parser.Wait): + self.wait_frames = action(self.params, self.rank) + break + + elif isinstance(action, parser.Vanish): + self.owner.vanish() + break + + return created + +class Bullet(object): + """Simple bullet implementation.""" + + def __init__(self, x=0, y=0, direction=0, speed=0, target=None, + actions=(), parent=None): + self.x = self.px = x + self.y = self.py = y + self.mx = 0 + self.my = 0 + self.direction = direction + self.speed = speed + self.vanished = False + self.target = target + self.actions = [] + if actions and not parent: + raise errors.Error + for action, params in actions: + self.actions.append( + Action(self, parent, action, params, parent.rank)) + + def __repr__(self): + return ("%s(%r, %r, accel=%r, direction=%r, speed=%r, " + "actions=%r, target=%r, vanished=%r)") % ( + type(self).__name__, self.x, self.y, (self.mx, self.my), + self.direction, self.speed, self.actions, self.target, + self.vanished) + + @property + def aim(self): + """Angle to the target.""" + if self.target is None: + return self.direction + else: + return math.atan2(self.target.x - self.x, self.target.y - self.y) + + def vanish(self): + """Vanish this bullet and stop all actions.""" + self.vanished = True + for action in self.actions: + action.vanish() + self.actions = [] + + def replace(self, old, new): + try: + idx = self.actions.index(old) + except ValueError: + pass + else: + self.actions[idx] = new + + def step(self): + created = [] + + for action in self.actions: + created.extend(action.step()) + + direction = math.degrees(self.direction) + self.x += self.mx + math.sin(direction) * self.speed + self.y += self.my + math.cos(direction) * self.speed + + return created -- 2.20.1