Пример #1
0
class Rain(ParticleSystem):
    def __init__(self, w, h):
        ParticleSystem.__init__(self, False)
        self.pos_var = Point2(w, 0)
        self.speed = h * 3.5
        self.speed_var = h * 2.5

    texture = pyglet.resource.image('res/particles/rain2.png').texture


    # def load_texture(self):
    #     return pyglet.image.load('res/particles/rain.png').texture

    # total paticles
    total_particles = 2000

    # duration
    duration = -1

    # gravity
    gravity = Point2(0, -100)

    # angle
    angle = -105.0
    angle_var = 0

    # speed of particles
    # speed = 300.0
    # speed_var = 0.0

    # radial
    # radial_accel = -380
    # radial_accel_var = 0

    # tangential
    # tangential_accel = 45.0
    # tangential_accel_var = 0.0

    # emitter variable position
    # pos_var = Point2(100, 0)

    # life of particles
    life = 12.0
    life_var = 0.0

    # emits per frame
    emission_rate = total_particles / life

    # color of particles
    start_color = Color(1, 1, 1, 0.5)
    # start_color_var = Color(0, 0, 0, 0.0)
    end_color = Color(1, 1, 1, 0)
    # end_color_var = Color(0, 0, 0, 0, 0.0)

    # size, in pixels
    size = 20.0
    size_var = 10.0

    # blend additive
    blend_additive = True
Пример #2
0
class FighterFire(cocos.particle_systems.Smoke):
    """飞机喷射火焰粒子系统"""

    angle = 270.0  # 粒子方向角度
    speed = 50.0  # 粒子移动速度
    life = 1.0  # 粒子生命期
    size = 20.0  # 粒子大小
    size_var = 5.0  # 粒子大小偏差
    start_color = Color(0.1, 0.25, 1.0, 1.0)  # 粒子的开始颜色
    start_color_var = Color(0.0, 0.0, 0.0, 0.0)  # 粒子开始颜色偏差
    end_color = Color(0.0, 0.0, 0.0, 0.0)  # 粒子的结束颜色
    end_color_var = Color(0.0, 0.0, 0.0, 0.0)  # 粒子的结束颜色偏差
Пример #3
0
class Galaxy(ParticleSystem):

    # total particles
    total_particles = 200

    # duration
    duration = -1

    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 90.0
    angle_var = 360.0

    # speed of particles
    speed = 60.0
    speed_var = 10.0

    # radial
    radial_accel = -80.0
    radial_accel_var = 0

    # tangential
    tangential_accel = 80.0
    tangential_accel_var = 0.0

    # emitter variable position
    pos_var = Point2(0, 0)

    # life of particles
    life = 4.0
    life_var = 1.0

    # size, in pixels
    size = 37.0
    size_var = 10.0

    # emits per frame
    emission_rate = total_particles / life

    # color of particles
    start_color = Color(0.12, 0.25, 0.76, 1.0)
    start_color_var = Color(0.0, 0.0, 0.0, 0.0)
    end_color = Color(0.0, 0.0, 0.0, 0.0)
    end_color_var = Color(0.0, 0.0, 0.0, 0.0)

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = True
Пример #4
0
class Spiral(ParticleSystem):

    # total paticles
    total_particles = 500

    # duration
    duration = -1

    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 90.0
    angle_var = 0.0

    # speed of particles
    speed = 150.0
    speed_var = 0.0

    # radial
    radial_accel = -380
    radial_accel_var = 0

    # tangential
    tangential_accel = 45.0
    tangential_accel_var = 0.0

    # emitter variable position
    pos_var = Point2(0, 0)

    # life of particles
    life = 12.0
    life_var = 0.0

    # emits per frame
    emission_rate = total_particles / life

    # color of particles
    start_color = Color(0.5, 0.5, 0.5, 1.0)
    start_color_var = Color(0.5, 0.5, 0.5, 0.0)
    end_color = Color(0.5, 0.5, 0.5, 1.0)
    end_color_var = Color(0.5, 0.5, 0.5, 0.0)

    # size, in pixels
    size = 20.0
    size_var = 10.0

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = True
Пример #5
0
class BigExplosion(cocos.particle_systems.Explosion):
    """玩家或敌人爆炸粒子系统"""

    speed = 100.0  # 粒子移动速度
    life = 0.5  # 粒子生命期
    life_var = 0.2
    size = 5.0  # 粒子大小
    size_var = 1.0  # 粒子大小偏差
    gravity = Point2(0, 0)  # 粒子的重力, (0, 0)不受重力影响
    start_color = Color(0.7, 0.2, 0.5, 1.0)  # 粒子的开始颜色
    start_color_var = Color(0.5, 0.5, 0.7, 0.0)  # 粒子开始颜色偏差
    end_color = Color(0.5, 0.5, 0.5, 0.3)  # 粒子的结束颜色
    end_color_var = Color(0.5, 0.5, 0.5, 0.0)  # 粒子的结束颜色偏差
Пример #6
0
class Death(ParticleSystem):

    pic = resources.particleTexture
    texture = pic.get_texture()

    # total particles
    total_particles = 1000

    # duration
    duration = 0.12

    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 90
    angle_var = 360

    # radial
    radial_accel = -1000
    radial_accel_var = 100

    # speed of particles
    speed = 400
    speed_var = 50

    # emitter variable position
    pos_var = Point2(5, 5)

    # life of particles
    life = 0.3
    life_var = 0.1

    # emits per frame
    # emission_rate = total_particles / life
    emission_rate = 1000

    start_color = Color(1, 0.53, 0, 1.0)
    start_color_var = Color(0.0, 0.0, 0.0, 0.0)
    end_color = Color(1, 1, 1, 0)
    end_color_var = Color(0.0, 0.0, 0.0, 0.0)

    # size, in pixels
    size = 15
    size_var = 3

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = True
Пример #7
0
class Smoke(ParticleSystem):

    # total particles
    total_particles = 80

    # duration
    duration = -1

    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 90.0
    angle_var = 10.0

    # speed of particles
    speed = 25.0
    speed_var = 10.0

    # radial
    radial_accel = 5
    radial_accel_var = 0

    # tangential
    tangential_accel = 0.0
    tangential_accel_var = 0.0

    # emitter variable position
    pos_var = Point2(0.1, 0)

    # life of particles
    life = 4.0
    life_var = 1.0

    # size, in pixels
    size = 40.0
    size_var = 10.0

    # emits per frame
    emission_rate = total_particles / life

    start_color = Color(0.5, 0.5, 0.5, 0.1)
    start_color_var = Color(0, 0, 0, 0.1)
    end_color = Color(0.5, 0.5, 0.5, 0.1)
    end_color_var = Color(0, 0, 0, 0.1)

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = False
Пример #8
0
class Fireworks(ParticleSystem):

    # total particles
    total_particles = 3000

    # duration
    duration = -1

    # gravity
    gravity = Point2(0, -90)

    # angle
    angle = 90
    angle_var = 20

    # radial
    radial_accel = 0
    radial_accel_var = 0

    # speed of particles
    speed = 180
    speed_var = 50

    # emitter variable position
    pos_var = Point2(0, 0)

    # life of particles
    life = 3.5
    life_var = 1

    # emits per frame
    emission_rate = total_particles / life

    # color of particles
    start_color = Color(0.5, 0.5, 0.5, 1.0)
    start_color_var = Color(0.5, 0.5, 0.5, 1.0)
    end_color = Color(0.1, 0.1, 0.1, 0.2)
    end_color_var = Color(0.1, 0.1, 0.1, 0.2)

    # size, in pixels
    size = 8.0
    size_var = 2.0

    # blend additive
    blend_additive = False

    # color modulate
    color_modulate = True
Пример #9
0
class Fire(ParticleSystem):

    # total particles
    total_particles = 250

    # duration
    duration = -1

    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 90.0
    angle_var = 10.0

    # radial
    radial_accel = 0
    radial_accel_var = 0

    # speed of particles
    speed = 60.0
    speed_var = 20.0

    # emitter variable position
    pos_var = Point2(40, 20)

    # life of particles
    life = 3.0
    life_var = 0.25

    # emits per frame
    emission_rate = total_particles / life

    # color of particles
    start_color = Color(0.76, 0.25, 0.12, 1.0)
    start_color_var = Color(0.0, 0.0, 0.0, 0.0)
    end_color = Color(0.0, 0.0, 0.0, 1.0)
    end_color_var = Color(0.0, 0.0, 0.0, 0.0)

    # size, in pixels
    size = 100.0
    size_var = 10.0

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = True
Пример #10
0
class Explosion(ParticleSystem):

    # total particle
    total_particles = 700

    # duration
    duration = 0.1

    # gravity
    gravity = Point2(0, -90)

    # angle
    angle = 90.0
    angle_var = 360.0

    # radial
    radial_accel = 0
    radial_accel_var = 0

    # speed of particles
    speed = 70.0
    speed_var = 40.0

    # emitter variable position
    pos_var = Point2(0, 0)

    # life of particles
    life = 5.0
    life_var = 2.0

    # emits per frame
    emission_rate = total_particles / duration

    # color of particles
    start_color = Color(0.7, 0.2, 0.1, 1.0)
    start_color_var = Color(0.5, 0.5, 0.5, 0.0)
    end_color = Color(0.5, 0.5, 0.5, 0.0)
    end_color_var = Color(0.5, 0.5, 0.5, 0.0)

    # size, in pixels
    size = 15.0
    size_var = 10.0

    # blend additive
    blend_additive = False

    # color modulate
    color_modulate = True
    def _load_background(self):
        background = Sprite(
            self.gallery.content["display"]["worldmap_quarters"])
        self.optimal_scale = (self.config.window_width * self.display_pos_size[
            "display_worldmap_quarters"]["W"]) / background.width
        background.image_anchor = 0, 0
        background.scale = self.optimal_scale
        background.x = self.config.window_width * self.display_pos_size[
            "display_worldmap_quarters"]["X"]
        background.y = self.config.window_height * self.display_pos_size[
            "display_worldmap_quarters"]["Y"]
        self.left_margin = background.x
        self.bottom_margin = background.y
        self.optimal_width = background.width
        self.optimal_height = background.height
        self.add(background)

        self.ps_alarm = Sun()
        self.ps_alarm.total_particles = 20
        self.ps_alarm.speed = 0.0
        self.ps_alarm.speed_var = 0.0
        self.ps_alarm.life_var = 0.0
        self.ps_alarm.blend_additive = True
        self.ps_alarm.position = (
            self.optimal_width * self.button_positions["ps_alarm"]["X"] +
            self.left_margin), (
                self.optimal_height * self.button_positions["ps_alarm"]["Y"] +
                self.bottom_margin)
        self.ps_alarm.start_color = Color(1, 0.40, 0.40, 0.40)
        self.ps_alarm.size = 50.0
Пример #12
0
class Explosion0(ParticleSystem):
    # total particles
    total_particles = 500

    # duration
    duration = 0.05

    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 90.0
    angle_var = 360

    # radial
    radial_accel = -200
    radial_accel_var = 40

    # speed of particles
    speed = 100
    speed_var = 80

    # emitter variable position
    pos_var = Point2(5, 5)

    # life of particles
    life = 0.18
    life_var = 0.1

    # emits per frame
    emission_rate = total_particles / life

    # color of particles
    start_color = Color(0.76, 0.25, 0.12, 1.0)
    start_color_var = Color(0.0, 0.0, 0.0, 0.0)
    end_color = Color(0.0, 0.0, 0.0, 1.0)
    end_color_var = Color(0.0, 0.0, 0.0, 0.0)

    # size, in pixels
    size = 70.0
    size_var = 10.0

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = True
Пример #13
0
class BrokenBrick(ParticleSystem):
    total_particles = 4
    duration = 0.1
    gravity = Point2(0, -600)
    angle = 90
    angle_var = 90.0
    speed = 100.0
    life = 3.0
    size = 8.0
    start_color = Color(255, 255, 255, 255)
    end_color = Color(255, 255, 255, 255)
    emission_rate = total_particles / duration

    def __init__(self, pos):
        super().__init__()

        self.position = pos
        self.auto_remove_on_finish = True

    def load_texture(self):
        self.__class__.texture = Image.normal_brick2.get_texture()
    def update_actor(self, dt):
        self.transarctica.proximity_alarm = 0
        if self.last_break_status != self.transarctica.is_break_released:
            self.last_break_status = self.transarctica.is_break_released
            if self.transarctica.is_break_released:
                self.start()
            else:
                self.stop()

        if (self.last_position["X"] != self.transarctica.current_position["X"]
                or self.last_position["Y"] !=
                self.transarctica.current_position["Y"]
            ) or (self.last_reverse_status != self.transarctica.is_in_reverse):
            cell = self.map.get("rails").cells[
                self.transarctica.current_position["X"]][
                    self.transarctica.current_position["Y"]]
            if (self.last_reverse_status != self.transarctica.is_in_reverse):
                self.turn_back()

            if cell.tile:
                self.transarctica.Speed_Modifier = 1
                if (("speedmod" in cell.properties) and
                    ("tunneldir" not in cell.properties)) or (
                        ("tunneldir" in cell.properties) and
                        (self.transarctica.direction
                         in cell.properties["tunneldir"])):
                    self.transarctica.Speed_Modifier = float(
                        cell.properties["speedmod"])
                if (self.last_position["X"] !=
                        self.transarctica.current_position["X"]
                        or self.last_position["Y"] !=
                        self.transarctica.current_position["Y"]):
                    self.tile_entry_point = self.position
                self.next_turn_at = (-1, -1)
                if "nodes" in cell.properties:
                    self.arriving_from = self.opposites[
                        self.transarctica.direction]
                    self.leaving_toward = cell.properties["directions"]
                    if len(self.leaving_toward) == 1:
                        self.on_tile = "bumper"
                        self.leaving_toward = self.arriving_from
                    elif len(self.leaving_toward) == 2:
                        self.on_tile = "normal"
                        self.leaving_toward = self.leaving_toward.replace(
                            self.arriving_from, "")
                    elif len(self.leaving_toward) == 3:
                        if self.leaving_toward[0] != self.arriving_from:
                            self.leaving_toward = self.leaving_toward[0]
                            self.on_tile = "normal"
                        else:
                            self.leaving_toward = self.leaving_toward[1]
                            self.on_tile = "switch"
                    elif len(self.leaving_toward) == 5:
                        self.on_tile = "cross"
                        self.leaving_toward = self.opposites[
                            self.arriving_from]

                    if (self.leaving_toward !=
                            self.opposites[self.arriving_from]) and (
                                self.next_turn_at == (-1, -1)):
                        rails = self.map.get("hidden objects")
                        self.next_turn_at = rails.cells[
                            self.transarctica.current_position["X"]][
                                self.transarctica.current_position["Y"]].center

                map_event = self.map.check_map_events(
                    self.transarctica.current_position["X"],
                    self.transarctica.current_position["Y"])
                if map_event in ["tunnel_block", "bridge_monster"]:
                    self.stop()
                    self.start()
                    events.emit_show_event("display_event_" + map_event)
                elif map_event in ["arrive_at_city"]:
                    self.on_tile = "city_bumper"
                elif map_event in ["bridge_out"]:
                    self.stop()
                    self.start()
                    events.emit_show_event("display_event_build_bridge")
                    self.transarctica.current_position[
                        "X"] = self.last_position["X"]
                    self.transarctica.current_position[
                        "Y"] = self.last_position["Y"]
                    self.transarctica.is_in_reverse = not (
                        self.transarctica.is_in_reverse)
                    self.transarctica.is_break_released = not (
                        self.transarctica.is_break_released)
                    self.map.place_player()
                elif map_event in ["end_of_rail"]:
                    self.stop()
                    self.start()
                    self.transarctica.current_position[
                        "X"] = self.last_position["X"]
                    self.transarctica.current_position[
                        "Y"] = self.last_position["Y"]
                    self.transarctica.is_in_reverse = not (
                        self.transarctica.is_in_reverse)
                    self.transarctica.is_break_released = not (
                        self.transarctica.is_break_released)
                    self.map.place_player()

                self.last_position["X"] = self.transarctica.current_position[
                    "X"]
                self.last_position["Y"] = self.transarctica.current_position[
                    "Y"]
                self.last_direction = self.transarctica.direction
            else:
                map_event = self.map.check_map_events(
                    self.transarctica.current_position["X"],
                    self.transarctica.current_position["Y"])
                if map_event in ["arrive_at_city"]:
                    self.service_bumper_hit()

                self.transarctica.current_position["X"] = self.last_position[
                    "X"]
                self.transarctica.current_position["Y"] = self.last_position[
                    "Y"]
                self.map.place_player()

        if self.transarctica.is_in_reverse:
            dirs = self.transarctica.direction + "R"
        else:
            dirs = self.transarctica.direction + "F"

        if self.prev_dirs != dirs:
            self.image = self.rotation_vars[dirs]["sprite"]
            self.smoke.position = self.rotation_vars[dirs]["smoke_pos"]
            self.prev_dirs = dirs

        if self.transarctica.speed == 0:
            self.smoke.angle = self.rotation_vars["SF"]["smoke_angle"]
        else:
            self.smoke.angle = self.rotation_vars[dirs]["smoke_angle"]
            self.smoke.speed = (self.transarctica.speed // 10) + 10

        if (self.transarctica.Speed_Modifier > 1):
            self.smoke.total_particles = 0
            self.counter = (self.counter + 1) % 30
            if (self.counter == 15):
                self.image = self.rotation_vars["BLANK"]["sprite"]
            elif self.counter == 0:
                self.prev_dirs = "BLANK"
        else:
            self.smoke.total_particles = 60
        self.smoke.start_color = Color(
            0.10, 0.10, 0.10,
            max(min(1, self.transarctica.engine_temp / 600), 0.01))
        if self.is_moving:
            self.move(dt)
Пример #15
0
    def _load_background(self):
        background = Sprite(self.gallery.content["display"]["worldmap_engine"])
        self.optimal_scale = (self.config.window_width *
                              self.display_pos_size["display_worldmap_engine"]
                              ["W"]) / background.width
        background.image_anchor = 0, 0
        background.scale = self.optimal_scale
        background.x = self.config.window_width * self.display_pos_size[
            "display_worldmap_engine"]["X"]
        background.y = self.config.window_height * self.display_pos_size[
            "display_worldmap_engine"]["Y"]
        self.left_margin = background.x
        self.bottom_margin = background.y
        self.optimal_width = background.width
        self.optimal_height = background.height

        pic_fire = Sprite(
            self.gallery.content["display"]["worldmap_engine_fire"])
        pic_fire.image_anchor = 0, 0
        pic_fire.scale = self.optimal_scale
        pic_fire.x = self.optimal_width * self.button_positions["pic_fire"][
            "X"] + self.left_margin
        pic_fire.y = self.optimal_height * self.button_positions["pic_fire"][
            "Y"] + self.bottom_margin
        self.add(pic_fire, name="pic_fire")

        self.ps_fire = Fire()
        self.ps_fire.size = 120
        self.ps_fire.position = (
            self.optimal_width * self.button_positions["ps_fire"]["X"] +
            self.left_margin,
            self.optimal_height * self.button_positions["ps_fire"]["Y"] +
            self.bottom_margin)
        self.add(self.ps_fire, name="ps_fire")

        self.add(background)

        pic_anthracite = Sprite(
            self.gallery.content["display"]["worldmap_engine_anthracite"])
        pic_anthracite.image_anchor = 0, 0
        pic_anthracite.scale = self.optimal_scale
        pic_anthracite.x = self.optimal_width * self.button_positions[
            "pic_anthracite"]["X"] + self.left_margin
        pic_anthracite.y = self.optimal_height * self.button_positions[
            "pic_anthracite"]["Y"] + self.bottom_margin
        self.add(pic_anthracite, name="pic_anthracite")

        pic_lignite = Sprite(
            self.gallery.content["display"]["worldmap_engine_lignite"])
        pic_lignite.image_anchor = 0, 0
        pic_lignite.scale = self.optimal_scale
        pic_lignite.x = self.optimal_width * self.button_positions[
            "pic_lignite"]["X"] + self.left_margin
        pic_lignite.y = self.optimal_height * self.button_positions[
            "pic_lignite"]["Y"] + self.bottom_margin
        self.add(pic_lignite, name="pic_lignite")

        self.ps_alarm = Sun()
        self.ps_alarm.total_particles = 20
        self.ps_alarm.speed = 0.0
        self.ps_alarm.speed_var = 0.0
        self.ps_alarm.life_var = 0.0
        self.ps_alarm.blend_additive = True
        self.ps_alarm.position = (
            self.optimal_width * self.button_positions["ps_alarm"]["X"] +
            self.left_margin), (
                self.optimal_height * self.button_positions["ps_alarm"]["Y"] +
                self.bottom_margin)
        self.ps_alarm.start_color = Color(1, 0.40, 0.40, 0.40)
        self.ps_alarm.size = 50.0
Пример #16
0
class PyFenseProjectileSlow(ParticleSystem, pyglet.event.EventDispatcher):
    """
    Projectile in the form of particles for the slow tower.
    Class variables have to be used because of ParticleSystem.
    """

    # total particles
    total_particles = 2000

    # duration
    duration = 0.1
    # gravity
    gravity = Point2(0, 0)

    # angle
    angle = 0
    angle_var = 5

    # radial
    radial_accel = 1000
    radial_accel_var = 0

    # speed of particles, fallback value
    speed = 800
    speed_var = 50

    # emitter variable position
    pos_var = Point2(12, 0)

    # distance that particles fly, fallback value
    distance = 200

    # life of particles, fallback value
    life = 5
    life_var = 0.005

    # emits per frame
    emission_rate = 500

    # color of particles
    start_color = Color(0.58, 0.98, 0.98, 1.0)
    start_color_var = Color(0.0, 0.0, 0.0, 0.6)
    end_color = Color(0.53, 0.96, 0.95, 1.0)
    end_color_var = Color(0.0, 0.0, 0.0, 0.2)

    # size, in pixels
    size = 40
    size_var = 2.0

    # blend additive
    blend_additive = True

    # color modulate
    color_modulate = True

    def __init__(self, towerParent, target, towerNumber, rotation, speed,
                 damage, effect, effectDuration, effectFactor):
        """
        Create a projectile and schedule event.

        :Parameters:
            `towerParent`: tower object
                Tower that launched the projectile.
            `target` : enemy object
                Enemy that is targeted.
            `towerNumber` : int
                Number of the parent tower.
            `rotation` : int
                Rotation of the parent tower.
            `speed` : int
                Speed of the particles.
            `damage` : int
                Damage the projectile causes.
            `effect` : string
                Effect that is caused by projectile (here: slow)
            `effectDuration` : int
                Duration that the effect is active.
            `effectFactor` : int
                How strong the effect is.
        """

        super().__init__()
        self.position = towerParent.position
        self.rotation = rotation - 90
        __class__.speed = speed
        __class__.distance = self._distance(target.position, self.position)
        __class__.life = __class__.distance / __class__.speed
        self.damage = damage

        self.schedule_interval(self._dispatch_hit_event, __class__.life,
                               target, towerNumber, effect, effectDuration,
                               effectFactor)

    def _dispatch_hit_event(self, dt, target, towerNumber, effect,
                            effectDuration, effectFactor):
        """
        Dispatch event when enemy is hit.

        The event is then handled by the enitites class in order to subtract
        health points from the enemy and to handle the different effects.
        """

        self.unschedule(self._dispatch_hit_event)
        self.dispatch_event('on_target_hit', self, target, towerNumber, effect,
                            effectDuration, effectFactor)

    def _distance(self, a, b):
        """
        Compute distance between two tupels (= position).
        """
        dis = math.sqrt((b[0] - a[0])**2 + (b[1] - a[1])**2)
        return dis
Пример #17
0
def performAttackOnUnit(battle, target_unit):
    # perform an attack on the given target BattleUnit
    turn_unit = battle.getTurnUnit()
    if turn_unit is None or target_unit is None\
            or turn_unit.isDestroyed() or target_unit.isDestroyed():
        # TODO: make sure it is a player unit's turn
        return 0

    src_cell = turn_unit.col, turn_unit.row
    dest_cell = target_unit.col, target_unit.row

    # minimum travel time to target used to determine when to show the damage floater
    min_travel_time = 0
    # maximum travel time to target used to determine when all animations are finished
    max_travel_time = 0

    # cell distance used to determine which range of weapons will fire
    cell_distance = Battle.getCellDistance(src_cell, dest_cell)
    target_range = Battle.getDistanceRange(cell_distance)
    print(target_range + ": " + str(src_cell) + " -> " + str(dest_cell) +
          " = " + str(cell_distance))

    # TODO: introduce dynamic damage (optional?)
    attack_damage = int(getattr(turn_unit, target_range))

    # apply damage to model before animating
    attack_remainder = target_unit.applyDamage(attack_damage)

    # determine actual target point based on the target unit sprite size
    target_sprite = target_unit.getSprite()
    real_x = (dest_cell[0] * Board.TILE_SIZE) + Board.TILE_SIZE // 2
    real_y = (dest_cell[1] *
              Board.TILE_SIZE) + (2 * target_sprite.get_height() // 3)

    for weaponMap in turn_unit.mech.weapons:
        for weapon in weaponMap.iterkeys():

            if not weapon.inRange(cell_distance):
                continue

            weapon_data = weaponMap[weapon]

            # get sound channel to use just for this weapon
            weapon_channel = pygame.mixer.find_channel()
            if weapon_channel is None:
                weapon_channel = pygame.mixer.Channel(0)

            weapon_offset = weapon_data.get('offset', [0, 0])
            weapon_x = turn_unit.sprite.position[0] + weapon_offset[0]
            weapon_y = turn_unit.sprite.position[1] + weapon_offset[1]

            weapon_color = weapon.get_color()

            if weapon.isPPC():
                # fire test ppcs
                ppc = Meteor()
                ppc.size = 10
                ppc.speed = 20
                ppc.gravity = Point2(0, 0)
                # TODO: offer decreased particle emission rate to improve performance
                ppc.emission_rate = 100
                ppc.life = 0.5
                ppc.life_var = 0.1

                ppc.position = weapon_x, weapon_y
                battle.board.add(ppc, z=1000)

                target_x = real_x + random_offset()
                target_y = real_y + random_offset()
                target_pos = target_x, target_y

                # figure out the duration based on speed and distance
                ppc_speed = weapon.get_speed()  # pixels per second
                distance = Point2(ppc.x,
                                  ppc.y).distance(Point2(target_x, target_y))
                ppc_t = distance / ppc_speed

                ppc_sound = Resources.ppc_sound
                weapon_channel.play(ppc_sound)

                action = Delay(0.5) + MoveTo((target_x, target_y), duration=ppc_t) \
                         + CallFunc(impact_ppc, ppc) \
                         + Delay(0.5) + CallFunc(ppc.kill) \
                         + Delay(ppc_sound.get_length()) \
                         + CallFunc(weapon_channel.stop)

                ppc.do(action)

                travel_time = 0.5 + ppc_t
                if min_travel_time == 0 or min_travel_time > travel_time:
                    min_travel_time = travel_time

                if travel_time > max_travel_time:
                    max_travel_time = travel_time

            elif weapon.isFlamer():
                # fire test flamer
                flamer = Fire()

                flamer.size = 25
                flamer.speed = 300
                flamer.gravity = Point2(0, 0)
                # TODO: offer decreased particle emission rate to improve performance
                flamer.emission_rate = 100

                dx = real_x - weapon_x
                dy = real_y - weapon_y
                rads = atan2(-dy, dx)
                rads %= 2 * pi
                angle = degrees(rads) + 90

                flamer.rotation = angle
                flamer.angle_var = 5
                flamer.pos_var = Point2(5, 5)

                flamer.position = weapon_x, weapon_y
                battle.board.add(flamer, z=1000)

                target_x = real_x + random_offset()
                target_y = real_y + random_offset()
                target_pos = target_x, target_y

                # figure out the duration based on speed and distance
                flamer_speed = weapon.get_speed()  # pixels per second
                distance = Point2(flamer.x, flamer.y).distance(
                    Point2(target_x, target_y))
                flamer_t = 1

                flamer.life = distance / flamer_speed
                flamer.life_var = 0

                flamer_sound = Resources.flamer_sound
                weapon_channel.play(flamer_sound)

                action = Delay(flamer_t) \
                         + CallFunc(impact_flamer, flamer) \
                         + CallFunc(weapon_channel.fadeout, 750) \
                         + Delay(flamer_t) + CallFunc(flamer.kill) \
                         + CallFunc(weapon_channel.stop)

                flamer.do(action)

                travel_time = flamer_t
                if min_travel_time == 0 or min_travel_time > travel_time:
                    min_travel_time = travel_time

                if travel_time > max_travel_time:
                    max_travel_time = travel_time

            elif weapon.isLaser():
                # fire test laser
                las_life = 1.0
                las_size = (1, 1, 1)
                if weapon.isShort():
                    las_size = (2, 1, 0.5)
                    las_life = 0.5
                elif weapon.isMedium():
                    las_size = (3, 2, 1)
                    las_life = 0.75
                elif weapon.isLong():
                    las_size = (6, 4, 2)
                    las_life = 1.0

                target_x = real_x + random_offset()
                target_y = real_y + random_offset()
                target_pos = target_x, target_y

                las_outer = gl.SingleLine(
                    (weapon_x, weapon_y), (target_x, target_y),
                    width=las_size[0],
                    color=(weapon_color[0], weapon_color[1], weapon_color[2],
                           50))
                las_middle = gl.SingleLine(
                    (weapon_x, weapon_y), (target_x, target_y),
                    width=las_size[1],
                    color=(weapon_color[0], weapon_color[1], weapon_color[2],
                           125))
                las_inner = gl.SingleLine(
                    (weapon_x, weapon_y), (target_x, target_y),
                    width=las_size[2],
                    color=(weapon_color[0], weapon_color[1], weapon_color[2],
                           200))

                las_outer.visible = False
                las_middle.visible = False
                las_inner.visible = False

                node = cocos.layer.Layer()
                node.add(las_outer, z=1)
                node.add(las_middle, z=2)
                node.add(las_inner, z=3)
                battle.board.add(node, z=1000)

                # give lasers a small particle pre-fire effect
                laser_charge = Galaxy()
                laser_charge.angle = 270
                laser_charge.angle_var = 180
                laser_charge.position = weapon_x, weapon_y
                laser_charge.size = 10
                laser_charge.size_var = 5
                laser_charge.emission_rate = 15
                laser_charge.life = 0.5
                laser_charge.speed = 0
                laser_charge.speed_var = 0
                laser_charge.start_color = Color(weapon_color[0] / 255,
                                                 weapon_color[1] / 255,
                                                 weapon_color[2] / 255, 1.0)
                laser_charge.end_color = Color(weapon_color[0] / 255,
                                               weapon_color[1] / 255,
                                               weapon_color[2] / 255, 1.0)
                node.add(laser_charge, z=0)

                laser_drift = random.uniform(-15.0, 15.0), random.uniform(
                    -15.0, 15.0)

                las_action = Delay(0.5) + ToggleVisibility() \
                             + CallFunc(create_laser_impact, battle.board, target_pos, laser_drift, las_life) \
                             + gl.LineDriftBy(laser_drift, las_life) \
                             + CallFunc(laser_charge.stop_system) + CallFunc(node.kill)
                las_outer.do(las_action)
                las_middle.do(las_action)
                las_inner.do(las_action)

                las_sound = Resources.las_sound
                weapon_channel.play(las_sound)
                las_duration_ms = int(las_action.duration * 1000)
                weapon_channel.fadeout(las_duration_ms)

                travel_time = 0.5
                if min_travel_time == 0 or min_travel_time > travel_time:
                    min_travel_time = travel_time

                if travel_time > max_travel_time:
                    max_travel_time = travel_time

            elif weapon.isBallistic():
                # fire test ballistic projectile
                num_ballistic = weapon.get_projectiles()

                if weapon.isGauss():
                    ballistic_img = Resources.gauss_img
                elif weapon.isLBX():
                    # LBX fires only one projectile, but will appear to have multiple random impacts
                    num_ballistic = 1
                    ballistic_img = Resources.buckshot_img
                else:
                    ballistic_img = Resources.ballistic_img

                # machine gun sound only plays once instead of per projectile
                cannon_sound = None
                if weapon.isMG():
                    cannon_sound = Resources.machinegun_sound
                elif weapon.isGauss():
                    cannon_sound = Resources.gauss_sound

                for i in range(num_ballistic):
                    ballistic = Sprite(ballistic_img)
                    ballistic.visible = False
                    ballistic.position = weapon_x, weapon_y
                    ballistic.scale = weapon.get_scale()
                    ballistic.anchor = 0, 0

                    dx = real_x - weapon_x
                    dy = real_y - weapon_y
                    rads = atan2(-dy, dx)
                    rads %= 2 * pi
                    angle = degrees(rads)

                    ballistic.rotation = angle

                    target_x = real_x + random_offset()
                    target_y = real_y + random_offset()
                    target_pos = target_x, target_y

                    # figure out the duration based on speed and distance
                    ballistic_speed = weapon.get_speed()  # pixels per second
                    distance = Point2(weapon_x, weapon_y).distance(
                        Point2(target_x, target_y))
                    ballistic_t = distance / ballistic_speed

                    # setup the firing sound
                    if cannon_sound is None:
                        cannon_sound = Resources.cannon_sound

                    impact_func = create_ballistic_impact
                    if weapon.isLBX():
                        impact_func = create_lbx_impact

                    action = Delay(i * 0.1) + ToggleVisibility() \
                             + CallFunc(weapon_channel.play, cannon_sound) \
                             + MoveTo((target_x, target_y), ballistic_t) \
                             + CallFunc(impact_func, weapon, battle.board, target_pos) \
                             + CallFunc(ballistic.kill)

                    if weapon.isGauss():
                        # give gauss sound a bit more time to stop
                        action += Delay(cannon_sound.get_length())

                    if i == num_ballistic - 1:
                        # stop the sound channel after the last projectile only
                        action += CallFunc(weapon_channel.stop)

                    ballistic.do(action)

                    battle.board.add(ballistic, z=1000 + i)

                    travel_time = (i * 0.1) + ballistic_t
                    if min_travel_time == 0 or min_travel_time > travel_time:
                        min_travel_time = travel_time

                    if travel_time > max_travel_time:
                        max_travel_time = travel_time

            elif weapon.isMissile():
                # get another sound channel to use just for the explosions
                explosion_channel = pygame.mixer.find_channel()
                if explosion_channel is None:
                    explosion_channel = pygame.mixer.Channel(1)

                # fire test missile projectile
                missile_img = Resources.missile_img

                num_missile = weapon_data.get('count', 1)

                num_per_row = 1
                if weapon.isLRM():
                    num_per_row = 5
                elif weapon.isSRM():
                    num_per_row = 2

                for i in range(num_missile):

                    tube_x = i % num_per_row
                    tube_y = i // num_per_row

                    missile = Sprite(missile_img)
                    missile.visible = False
                    missile.position = weapon_x + tube_x, weapon_y + tube_y
                    missile.scale = weapon.get_scale()
                    missile.anchor = 0, 0

                    dx = real_x - weapon_x
                    dy = real_y - weapon_y
                    rads = atan2(-dy, dx)
                    rads %= 2 * pi
                    angle = degrees(rads)

                    missile.rotation = angle

                    target_x = real_x + random_offset()
                    target_y = real_y + random_offset()
                    target_pos = target_x, target_y

                    # figure out the duration based on speed and distance
                    missile_speed = weapon.get_speed()  # pixels per second
                    distance = Point2(weapon_x, weapon_y).distance(
                        Point2(target_x, target_y))
                    missile_t = distance / missile_speed

                    rand_missile_sound = random.randint(0, 7)
                    missile_sound = Resources.missile_sounds[
                        rand_missile_sound]

                    explosion_sound = Resources.explosion_sound

                    action = Delay(i * 0.05) + ToggleVisibility() \
                             + CallFunc(weapon_channel.play, missile_sound) \
                             + MoveTo((target_x, target_y), missile_t) \
                             + CallFunc(create_missile_impact, battle.board, target_pos) \
                             + CallFunc(missile.kill) \
                             + CallFunc(explosion_channel.play, explosion_sound) \
                             + Delay(0.5)

                    if i == num_missile - 1:
                        # stop the sound channels after the last missile only
                        action += CallFunc(weapon_channel.stop) + CallFunc(
                            explosion_channel.stop)

                    missile.do(action)

                    battle.board.add(missile, z=1000 + i)

                    travel_time = (i * 0.05) + missile_t
                    if min_travel_time == 0 or min_travel_time > travel_time:
                        min_travel_time = travel_time

                    if travel_time > max_travel_time:
                        max_travel_time = travel_time

    # scroll focus over to the target area halfway through the travel time
    target_area = Board.board_to_layer(target_unit.col, target_unit.row)

    # show damage floater after the travel time of the first projectile to hit
    floater = floaters.TextFloater("%i" % attack_damage)
    floater.visible = False
    floater.position = real_x, real_y + target_sprite.get_height() // 3
    battle.board.add(floater, z=2000)

    action = Delay(min_travel_time / 2) + CallFunc(battle.scroller.set_focus, *target_area) \
        + Delay(min_travel_time / 2) + ToggleVisibility() \
        + Delay(0.25) + MoveBy((0, Board.TILE_SIZE), 1.0) \
        + FadeOut(1.0) + CallFunc(floater.kill)
    floater.do(action)

    if action.duration > max_travel_time:
        max_travel_time = action.duration

    stats_action = Delay(min_travel_time) + CallFunc(target_sprite.updateStatsIndicators) \
                   + CallFunc(Interface.UI.updateTargetUnitStats, target_unit)
    target_sprite.do(stats_action)

    if attack_remainder > 0:
        print("Overkill by %i!" % attack_remainder)

    if target_unit.structure > 0:
        print("Remaining %i/%i" % (target_unit.armor, target_unit.structure))

    else:
        print("Target destroyed!")
        # show destroyed floater after the travel time of the first projectile to hit
        destroyed = floaters.TextFloater("DESTROYED")
        destroyed.visible = False
        destroyed.position = real_x, real_y + target_sprite.get_height() // 3
        battle.board.add(destroyed, z=5000)

        # get another sound channel to use just for the explosions
        explosion_channel = pygame.mixer.find_channel()
        if explosion_channel is None:
            explosion_channel = pygame.mixer.Channel(1)

        explosion_sound = Resources.explosion_multiple_sound

        action = Delay(max_travel_time) + ToggleVisibility() \
            + CallFunc(explosion_channel.play, explosion_sound) \
            + (MoveBy((0, Board.TILE_SIZE), 1.0) | CallFunc(create_destruction_explosions, battle.board, target_unit)) \
            + Delay(0.5) + CallFunc(target_sprite.destroy) + FadeOut(2.0) + CallFunc(destroyed.kill)
        destroyed.do(action)

        # give a bit of extra time to explode
        max_travel_time = action.duration

    return max_travel_time
    def __init__(self, map_manager):
        """initializer"""
        gallery = Gallery()

        self.rotation_vars = {}
        self.rotation_vars["NF"] = {}
        self.rotation_vars["SR"] = {}
        self.rotation_vars["BF"] = {}
        self.rotation_vars["DR"] = {}
        self.rotation_vars["EF"] = {}
        self.rotation_vars["WR"] = {}
        self.rotation_vars["CF"] = {}
        self.rotation_vars["AR"] = {}
        self.rotation_vars["SF"] = {}
        self.rotation_vars["NR"] = {}
        self.rotation_vars["DF"] = {}
        self.rotation_vars["BR"] = {}
        self.rotation_vars["WF"] = {}
        self.rotation_vars["ER"] = {}
        self.rotation_vars["AF"] = {}
        self.rotation_vars["CR"] = {}
        self.rotation_vars["BLANK"] = {}

        self.rotation_vars["NF"]["sprite"] = gallery.content["actor"][
            "engine000"]
        self.rotation_vars["SR"]["sprite"] = gallery.content["actor"][
            "engine000"]
        self.rotation_vars["BF"]["sprite"] = gallery.content["actor"][
            "engine045"]
        self.rotation_vars["DR"]["sprite"] = gallery.content["actor"][
            "engine045"]
        self.rotation_vars["EF"]["sprite"] = gallery.content["actor"][
            "engine090"]
        self.rotation_vars["WR"]["sprite"] = gallery.content["actor"][
            "engine090"]
        self.rotation_vars["CF"]["sprite"] = gallery.content["actor"][
            "engine135"]
        self.rotation_vars["AR"]["sprite"] = gallery.content["actor"][
            "engine135"]
        self.rotation_vars["SF"]["sprite"] = gallery.content["actor"][
            "engine180"]
        self.rotation_vars["NR"]["sprite"] = gallery.content["actor"][
            "engine180"]
        self.rotation_vars["DF"]["sprite"] = gallery.content["actor"][
            "engine225"]
        self.rotation_vars["BR"]["sprite"] = gallery.content["actor"][
            "engine225"]
        self.rotation_vars["WF"]["sprite"] = gallery.content["actor"][
            "engine270"]
        self.rotation_vars["ER"]["sprite"] = gallery.content["actor"][
            "engine270"]
        self.rotation_vars["AF"]["sprite"] = gallery.content["actor"][
            "engine315"]
        self.rotation_vars["CR"]["sprite"] = gallery.content["actor"][
            "engine315"]
        self.rotation_vars["BLANK"]["sprite"] = gallery.content["switch"][
            "off"]

        self.rotation_vars["NF"]["smoke_angle"] = 270
        self.rotation_vars["SR"]["smoke_angle"] = 90
        self.rotation_vars["BF"]["smoke_angle"] = 225
        self.rotation_vars["DR"]["smoke_angle"] = 45
        self.rotation_vars["EF"]["smoke_angle"] = 180
        self.rotation_vars["WR"]["smoke_angle"] = 0
        self.rotation_vars["CF"]["smoke_angle"] = 135
        self.rotation_vars["AR"]["smoke_angle"] = 315
        self.rotation_vars["SF"]["smoke_angle"] = 90
        self.rotation_vars["NR"]["smoke_angle"] = 270
        self.rotation_vars["DF"]["smoke_angle"] = 45
        self.rotation_vars["BR"]["smoke_angle"] = 225
        self.rotation_vars["WF"]["smoke_angle"] = 0
        self.rotation_vars["ER"]["smoke_angle"] = 180
        self.rotation_vars["AF"]["smoke_angle"] = 315
        self.rotation_vars["CR"]["smoke_angle"] = 135
        self.rotation_vars["BLANK"]["smoke_angle"] = 0

        self.rotation_vars["NF"]["smoke_pos"] = -2, 17
        self.rotation_vars["SR"]["smoke_pos"] = -2, 17
        self.rotation_vars["BF"]["smoke_pos"] = 1, 26
        self.rotation_vars["DR"]["smoke_pos"] = 1, 26
        self.rotation_vars["EF"]["smoke_pos"] = 18, 17
        self.rotation_vars["WR"]["smoke_pos"] = 18, 17
        self.rotation_vars["CF"]["smoke_pos"] = 25, -1
        self.rotation_vars["AR"]["smoke_pos"] = 25, -1
        self.rotation_vars["SF"]["smoke_pos"] = -2, -17
        self.rotation_vars["NR"]["smoke_pos"] = -2, -17
        self.rotation_vars["DF"]["smoke_pos"] = -26, -2
        self.rotation_vars["BR"]["smoke_pos"] = -26, -2
        self.rotation_vars["WF"]["smoke_pos"] = -18, 17
        self.rotation_vars["ER"]["smoke_pos"] = -18, 17
        self.rotation_vars["AF"]["smoke_pos"] = -5, 30
        self.rotation_vars["CR"]["smoke_pos"] = -5, 30
        self.rotation_vars["BLANK"]["smoke_pos"] = -18, 17

        self.is_moving = False
        Sprite.__init__(self, self.rotation_vars["BLANK"]["sprite"])
        self.counter = 0
        self.map = map_manager
        self.transarctica = director.core.query_mover("Transarctica")
        self.transarctica.map = map_manager
        self.driver = TrainDriver()
        self.driver.target = self
        self.config = Config()
        self.break_sound_source = load('music/stop.wav')  #, streaming=False)
        #self.break_sound.volume=0.5 * self.config.sound_switch

        self.anchor = self.config.tile_width / 2, self.config.tile_width / 2
        #self.transarctica.current_position = self.config.start_position
        self.last_position = {
            "X": self.transarctica.current_position["X"],
            "Y": self.transarctica.current_position["Y"]
        }
        self.max_forward_speed = 450 / self.config.km_per_tile * self.config.tile_width / 3600
        self.position = (-1, -1)
        self.last_break_status = self.transarctica.is_break_released
        self.last_reverse_status = self.transarctica.is_in_reverse
        self.last_direction = self.transarctica.direction
        self.arriving_from = ""
        self.leaving_toward = ""
        self.on_tile = "none"
        self.next_turn_at = (-1, -1)
        self.tile_entry_point = (-1, -1)

        self.smoke = Smoke()
        self.smoke.blend_additive = False
        self.smoke.position = self.rotation_vars["NF"]["smoke_pos"]
        self.smoke.life = 1
        self.smoke.speed = 30
        self.smoke.start_color = Color(0.20, 0.20, 0.20, 0.40)
        self.smoke.size = 15.0
        self.smoke.total_particles = 60
        self.add(self.smoke)
        self.prev_dirs = ""