예제 #1
0
    def createFloatingDamage(self, loc, direction, byPlayer, damage):
        dmgStr = str(damage)

        speed = 0.1
        if byPlayer:
            color = ColorPalette.getColorByColor(Color.brightcyan)
        else:
            color = ColorPalette.getColorByColor(Color.red)

        n = 0
        for char in dmgStr:
            particle = self.particlePool.pop()
            particle.init(
                x=loc.x + n,
                y=loc.y - 1,
                life=10,
                angle=90,
                speed=speed,
                fadeout=False,
                byStep=False,
                charType=0,
                active=True,
                damage=0,
                damageEveryStep=False,
                byPlayer=byPlayer,
                color=color)
            particle.char = dmgStr[n]
            self.particleActive.append(particle)

            n += 1
예제 #2
0
    def drawStatusbar(self):
        # fps = 0
        # if n > 100:
        #    fps = 1000 * (float)(n) / (float)(current_milli_time() - self.startTime)
        #    #fps = self.workTime * 1000.0
        playerEntity = EntityFinder.findPlayer(self.world.world)
        if playerEntity is None:
            # No player here yet
            return

        #self.viewport.erase()
        #self.viewport.border()
        playerAttackable = self.world.world.component_for_entity(
            playerEntity, Attackable)
        player = self.world.world.component_for_entity(playerEntity, Player)

        color, attr = ColorPalette.getColorByColor(Color.black)
        bgcolor, _ = ColorPalette.getColorByColor(Color.white)

        s = " Health: " + str(playerAttackable.getHealth())
        s += "  Points: " + str(player.points)
        s += " " * (Config.columns - 2 - len(s))
        #s += "  FPS: %.0f" % (fps)
        self.viewport.addstrStatic(1, 1, s, color, attr, bg=bgcolor)

        self.printSkillbar(color, attr, bgcolor, playerEntity)
        self.printAttackbar(color, attr, bgcolor, playerEntity)
    def readAnimation(
        self, characterTextureType :CharacterTextureType,
        characterAnimationType :CharacterAnimationType,
    ) -> Animation:
        ct = characterTextureType.name
        cat = characterAnimationType.name
        filename = "data/textures/character/{}/{}_{}.ascii".format(ct, ct, cat)

        # return fake animation if file does not exist(yet)
        if not os.path.isfile(filename):
            animation = Animation()
            animation.arr = [[['X', 'X', 'X'], ['X', 'X', 'X'], ['X', 'X', 'X']]]
            animation.height = 3
            animation.width = 3
            animation.frameCount = 1
            animation.frameTime = [10.0]
            animation.frameColors = [ColorPalette.getColorByColor(Color.white)]
            logger.debug("Could not find animation {}, replacing".format(
                filename
            ))
            return animation

        animation = TextureHelper.readAnimationFile(filename)
        animation.name = "{}_{}".format(ct, cat)

        filenameYaml = "data/textures/character/{}/{}_{}.yaml".format(ct, ct, cat)
        TextureHelper.loadYamlIntoAnimation(filenameYaml, animation)

        return animation
예제 #4
0
    def createHitBurst(self, loc, direction, byPlayer, damage):
        particleCount = 5
        life = 20
        spacingAngle = 10.0

        n = 0
        while n < particleCount:
            particle = self.particlePool.pop()
            if direction is Direction.right:
                angle = (spacingAngle * particleCount / 2) - (spacingAngle * n)
            else:
                angle = 180 + (spacingAngle * particleCount / 2) - (spacingAngle * n)

            x = loc.x
            y = loc.y
            particle.init(
                x=x, y=y,
                life=life,
                angle=angle,
                speed=0.1,
                fadeout=True,
                byStep=False,
                charType=3,
                active=True,
                damage=damage,
                damageEveryStep=False,
                color=ColorPalette.getColorByColor(Color.grey))

            self.particleActive.append(particle)
            n += 1
예제 #5
0
    def loadYamlIntoAnimation(filename :str, animation :Animation):
        with open(filename, 'r') as stream:
            data = yaml.safe_load(stream)

        try:
            animation.endless = data['endless']
            animation.advanceByStep = data['advanceByStep']
            animation.frameColors = data['frameColors']
        except TypeError as error:
            raise Exception("Error, missing field in yaml file {}, error {}".format(
                filename, error
            ))

        # FrameTime can be non-existing, e.g. if 'advanceByStep' = True,
        # or if it is one frame and endless...
        # Therefore, frameTime will be None, as defined in Animation
        if 'frameTime' in data:
            animation.frameTime = data['frameTime']
            # convert to float
            for (idx, frameTimeEntry) in enumerate(animation.frameTime):
                animation.frameTime[idx] = float(frameTimeEntry)

        if 'direction' in data:
            if data['direction'] == 'left':
                animation.originalDirection = Direction.left
            elif data['direction'] == 'right':
                animation.originalDirection = Direction.right
            else:
                animation.originalDirection = Direction.none

        # colors:
        # - <Color>: white, brightblue, ...
        # - ColorType.<ColorType>: background, world, ...
        for (n, color) in enumerate(data['frameColors']):
            if color.startswith('ColorType.'):
                color = color.split('.')[1]
                colorType = ColorPalette.getColorTypeByStr(color)
                animation.frameColors[n] = ColorPalette.getColorByColorType(
                    colorType)
            else:
                color = ColorPalette.getColorByStr(color)
                animation.frameColors[n] = ColorPalette.getColorByColor(
                    color)
예제 #6
0
    def showCharAtPos(self, char: str, timeout: float, coordinate: Coordinates,
                      color: Color):
        textureMinimal = TextureMinimal(
            char=char,
            colorArr=[ColorPalette.getColorByColor(color)],
            timeArr=[timeout],
        )
        renderableMinimal = RenderableMinimal(
            texture=textureMinimal,
            coordinates=coordinate,
        )

        self.addRenderableMinimal(renderableMinimal)
예제 #7
0
파일: game.py 프로젝트: dobin/nkeyrollover
    def draw2(self, frame: int):
        """Draws foremost layer (e.g. "pause" sign)"""
        if self.sceneManager.currentScene.showMap():
            self.statusBar.drawStatusbar()

        if self.showStats:
            self.drawStats()

        # not drawing related, but who cares
        if frame % 1000 == 0:
            self.logEntityStats()

        if self.pause:
            color, attr = ColorPalette.getColorByColor(Color.white)
            self.viewport.addstr(12, 40, "Paused", color, attr)
예제 #8
0
    def init(
        self,
        x :int =0,
        y :int =0,
        life :int =0,
        angle :float =0,
        speed :int =1,
        fadeout :bool =True,
        byStep :bool =False,
        charType :int =0,
        active :bool =False,
        damage :int =0,
        damageEveryStep :bool =False,
        byPlayer :bool =True,
        color =None,
    ):
        self.x = x
        self.y = y
        self.life = life
        self.originalLife = life
        self.angle = angle
        self.speed = speed
        self.fadeout = fadeout
        self.byStep = byStep
        self.charType = charType
        self.damage = damage
        self.damageEveryStep = damageEveryStep
        self.byPlayer = byPlayer

        self.angleInRadians = angle * math.pi / 180
        self.velocity = {
            'x': speed * math.cos(self.angleInRadians),
            'y': -speed * math.sin(self.angleInRadians)
        }

        self.color, self.attr = ColorPalette.getColorByColor(Color.brightmagenta)
        if color is not None:
            self.color = color[0]
            self.attr = color[1]

        self.setChar()

        self.rx = 0.0
        self.ry = 0.0

        self.movementTimer.setTimer(self.speed)
        self.active = active
예제 #9
0
 def createChar(self, loc, direction, byPlayer, damage):
     particle = self.particlePool.pop()
     x = loc.x
     y = loc.y
     particle.init(
         x=x, y=y,
         life=100,
         angle=0,
         speed=0.0,
         fadeout=True,
         byStep=False,
         charType=3,
         active=True,
         damage=damage,
         damageEveryStep=False,
         color=ColorPalette.getColorByColor(Color.green))
     self.particleActive.append(particle)
예제 #10
0
    def createImpact(self, loc, direction, byPlayer, damage):
        particle = self.particlePool.pop()

        particle.init(
            x=loc.x, y=loc.y,
            life=10,
            angle=0,
            speed=0.0,
            fadeout=True,
            byStep=False,
            charType=4,
            active=True,
            damage=0,
            damageEveryStep=False,
            color=ColorPalette.getColorByColor(Color.red))

        self.particleActive.append(particle)
예제 #11
0
    def getAnimation(self, displayText=None, direction=Direction.right, time=1.5):
        animation = Animation()

        animation.width = len(displayText) + 2
        animation.height = 4
        animation.frameCount = 1
        animation.frameTime = [
            time,
        ]
        animation.frameColors = [
            ColorPalette.getColorByColor(Color.white)
        ]
        animation.endless = False
        animation.advanceByStep = False

        l = animation.width

        animation.arr = [[]]
        l1 = []
        l1.append('.')
        l1.extend(list('-' * (l - 2)))
        l1.append('.')
        animation.arr[0].append(l1)

        l2 = []
        l2.append('|')
        l2.extend(list(displayText))
        l2.append('|')
        animation.arr[0].append(l2)

        l3 = []
        l3.append('`')
        l3.append(',')
        l3.extend(list('-' * (l - 3)))
        l3.append('\'')
        animation.arr[0].append(l3)

        l4 = []
        l4.append('/')
        l4.extend(list('' * (l - 1)))
        animation.arr[0].append(l4)

        return animation
예제 #12
0
파일: game.py 프로젝트: dobin/nkeyrollover
    def drawStats(self):
        x = 2
        y = 1
        color, attr = ColorPalette.getColorByColorType(ColorType.menu)

        o = []

        enemiesAlive = EntityFinder.numEnemies(world=self.world)
        enemiesAttacking = EntityFinder.numEnemiesInState(world=self.world,
                                                          state='attack')
        enemiesChasing = EntityFinder.numEnemiesInState(world=self.world,
                                                        state='chase')
        enemiesWandering = EntityFinder.numEnemiesInState(world=self.world,
                                                          state='wander')

        o.append("Enemies:")
        o.append("  Alive     : {}".format(enemiesAlive))
        o.append("  Attacking : {}".format(enemiesAttacking))
        o.append("  Chasing   : {}".format(enemiesChasing))
        o.append("  Wandering: {}".format(enemiesWandering))

        playerEntity = EntityFinder.findPlayer(self.world)
        playerRenderable = self.world.component_for_entity(
            playerEntity, Renderable)

        o.append('Player:')
        o.append('  Location:' + str(playerRenderable.getLocation()))

        o.append('Scene:')
        o.append('  Name:' + self.sceneManager.currentScene.name)
        o.append('  Scne State:' + str(self.sceneProcessor.state))
        o.append('  Enemies Alive:' +
                 str(self.sceneProcessor.numEnemiesAlive()))
        o.append('  Enemies Visible:' +
                 str(self.sceneProcessor.numEnemiesVisible()))

        n = 0
        while n < len(o):
            self.viewport.addstr(y + n, x, o[n], color=color, attr=attr)
            n += 1
예제 #13
0
 def convertMapAnsiToUnicode(self):
     xp_file_layer = self.xpmap['layer_data'][0]
     for x in range(xp_file_layer['width']):
         for y in range(xp_file_layer['height']):
             # {'keycode': 65, 'fore_r': 178, 'fore_g': 134, 'fore_b': 0,
             # 'back_r': 0, 'back_g': 0, 'back_b': 0}
             char = xp_file_layer['cells'][x][y]['keycode']
             color = ColorPalette.getColorByRgb(
                 xp_file_layer['cells'][x][y]['fore_r'],
                 xp_file_layer['cells'][x][y]['fore_g'],
                 xp_file_layer['cells'][x][y]['fore_b'])
             # we only accept official palette colors. if it is not recognized, i
             # gnore character completely (artefact? misclick?)
             if color is not None:
                 xp_file_layer['cells'][x][y]['color'] = color
                 if char != 32 and char != 0:
                     k = chr(ansitounicode.getUnicode(char))
                     xp_file_layer['cells'][x][y]['keycode'] = k
                 else:
                     xp_file_layer['cells'][x][y]['keycode'] = ''
             else:
                 xp_file_layer['cells'][x][y]['keycode'] = ''
예제 #14
0
    def createBullet(self, loc, direction, byPlayer, damage):
        particle = self.particlePool.pop()

        if direction is Direction.left:
            angle = 180
        else:
            angle = 0

        particle.init(
            x=loc.x, y=loc.y,
            life=100,
            angle=angle,
            speed=0.1,
            fadeout=False,
            byStep=False,
            charType=5,
            active=True,
            damage=10,
            damageEveryStep=True,
            byPlayer=byPlayer,
            color=ColorPalette.getColorByColor(Color.red))

        self.particleActive.append(particle)
예제 #15
0
    def makeEffect(self, effect, pos, x, y, char, columnCount, rowCnt,
                   charDirection):
        if effect is TextureEmiterEffect.explode:
            movementX = 0
            movementY = 0

            if y == 0:
                movementY = -1
            if x == 0:
                movementX = -1

            if y == columnCount - 1:
                movementY = 1
            if x == rowCnt - 1:
                movementX = 1

            c = Coordinates(
                x=pos.x + x,
                y=pos.y + y,
            )

            timeArr = [0.1, 0.1, 0.1]
            colorArr = [
                ColorPalette.getColorByColor(Color.brightyellow),
                ColorPalette.getColorByColor(Color.yellow),
                ColorPalette.getColorByColor(Color.grey),
            ]

            textureMinimal = TextureMinimal(char=char,
                                            movementX=movementX,
                                            movementY=movementY,
                                            timeArr=timeArr,
                                            colorArr=colorArr)
            renderableMinimal = RenderableMinimal(
                texture=textureMinimal,
                coordinates=c,
            )
            self.addRenderableMinimal(renderableMinimal)

        # push away
        if effect is TextureEmiterEffect.pushback:
            if charDirection is Direction.right:
                d = -1
            else:
                d = 1

            c = Coordinates(
                x=pos.x + x,
                y=pos.y + y,
            )

            timeArr = [0.05, 0.1, 0.2, 0.4]
            colorArr = [
                ColorPalette.getColorByColor(Color.white),
                ColorPalette.getColorByColor(Color.white),
                ColorPalette.getColorByColor(Color.grey),
                ColorPalette.getColorByColor(Color.grey),
            ]

            textureMinimal = TextureMinimal(char=char,
                                            movementX=d * 2,
                                            movementY=0,
                                            timeArr=timeArr,
                                            colorArr=colorArr)
            renderableMinimal = RenderableMinimal(texture=textureMinimal,
                                                  coordinates=c)
            self.addRenderableMinimal(renderableMinimal)
예제 #16
0
파일: game.py 프로젝트: dobin/nkeyrollover
    def createBg(self, width, height, uni=False):
        box = []
        width = self.viewport.win._buffer._width
        height = self.viewport.win._buffer._height

        fg, attr = ColorPalette.getColorByColor(Color.black)
        bg, _ = ColorPalette.getColorByColor(Color.black)
        w = 1

        tl = (ord(u"┌"), fg, attr, bg, w)
        tr = (ord(u"┐"), fg, attr, bg, w)
        bl = (ord(u"└"), fg, attr, bg, w)
        br = (ord(u"┘"), fg, attr, bg, w)
        h = (ord(u"─"), fg, attr, bg, w)
        v = (ord(u"│"), fg, attr, bg, w)
        s = (ord(u" "), fg, attr, bg, w)

        meWidth = Config.columns
        meHeight = Config.rows

        # top line
        line = []
        line.append(tl)
        n = 0
        while n < meWidth - 2:
            line.append(h)
            n += 1
        line.append(tr)
        while n < width:
            line.append(s)
            n += 1
        box.append(line)

        # middle
        line = []
        line.append(v)
        n = 0
        while n < meWidth - 2:
            line.append(s)
            n += 1
        line.append(v)

        # rest
        while n < width:
            line.append(s)
            n += 1
        for _ in range(meHeight - 2):
            box.append(line)

        # bottom line
        line = []
        line.append(bl)
        n = 0
        while n < meWidth - 2:
            line.append(h)
            n += 1
        line.append(br)
        while n < width:
            line.append(s)
            n += 1
        box.append(line)

        # rest
        y = meHeight
        while y < height:
            line = []
            n = 0
            while n < width:
                line.append(s)
                n += 1
            y += 1
            box.append(line)

        return box
예제 #17
0
    def handleState(self):
        # interactive, aka a hack, but it works
        if (self.state is IntroSceneState.wait1
                or self.state is IntroSceneState.flydown
                or self.state is IntroSceneState.drop
                or self.state is IntroSceneState.flyup):

            c1, a1 = ColorPalette.getColorByColor(Color.blue)
            c2, a2 = ColorPalette.getColorByColor(Color.brightblue)
            self.viewport.addstr(5, 40,  "N Key Rollover", c1, a1)
            self.viewport.addstr(6, 40,  "Escape from Hong Kong", c2, a2)

            c3, a3 = ColorPalette.getColorByColor(Color.cyan)
            self.viewport.addstr(8, 40,  "Moving: arrow keys, shift to strafe", c3, a3)
            self.viewport.addstr(9, 40,  "Select attack: 1 2 3 4", c3, a3)
            self.viewport.addstr(10, 40, "Attack       : space", c3, a3)
            self.viewport.addstr(11, 40, "Skills       : q w e r", c3, a3)
            self.viewport.addstr(12, 40, "Heal, Port   : f g", c3, a3)

        # state
        if self.state is IntroSceneState.wait1:
            # for next scene: Flydown
            if self.myTimer.timeIsUp():
                self.state = IntroSceneState.flydown
                self.myTimer.setTimer(0.1)
                self.renderableCopter.setActive(True)
                logger.debug("Scene: Go to State: Flydown")

        elif self.state is IntroSceneState.flydown:
            if self.myTimer.timeIsUp():
                self.myTimer.reset()
                self.renderableCopter.coordinates.y += 1

            # for next scene: Drop
            if self.renderableCopter.coordinates.y == 8:
                self.myTimer.setTimer(0.1)
                logger.debug("Scene: Go to State: Drop")
                self.addRenderable(self.renderablePlayer)
                self.state = IntroSceneState.drop

        elif self.state is IntroSceneState.drop:
            # for next scene: Flyup
            if self.myTimer.timeIsUp():
                self.myTimer.reset()
                self.renderableCopter.coordinates.y -= 1

            if self.renderableCopter.coordinates.y == self.copterSpawn.y:
                self.myTimer.setTimer(0.1)
                self.addRenderable(self.renderableEnemy)
                self.renderableEnemy.texture.changeAnimation(
                    CharacterAnimationType.walking, direction=Direction.left)
                self.state = IntroSceneState.spawnenemy
                self.isShowMap = True
                logger.info("Scene: Go to State: SpawnEnemy")

        elif self.state is IntroSceneState.spawnenemy:
            if self.myTimer.timeIsUp():
                self.myTimer.reset()
                self.renderableEnemy.coordinates.x -= 1
                self.renderableEnemy.advanceStep()

            if (self.renderableEnemy.coordinates.x
                    == self.renderablePlayer.coordinates.x + 15):
                self.renderableEnemy.texture.changeAnimation(
                    CharacterAnimationType.standing, direction=Direction.none)
                self.myTimer.setTimer(2.0)
                self.state = IntroSceneState.speakenemy
                self.textureEmiter.showSpeechBubble(
                    'The princess is in another castle...',
                    time=3.0,
                    parentRenderable=self.renderableEnemy
                )

        elif self.state is IntroSceneState.speakenemy:
            if self.myTimer.timeIsUp():

                self.state = IntroSceneState.leaveenemy
                self.renderableEnemy.texture.changeAnimation(
                    CharacterAnimationType.walking, direction=Direction.right)
                self.myTimer.setTimer(0.1)

        elif self.state is IntroSceneState.leaveenemy:
            if self.myTimer.timeIsUp():
                self.myTimer.reset()
                self.renderableEnemy.advanceStep()
                self.renderableEnemy.coordinates.x += 1

            if (self.renderableEnemy.coordinates.x
                    == Config.columns):
                self.state = IntroSceneState.done


        elif self.state is IntroSceneState.done:
            pass
예제 #18
0
    def generateCity(self, width, height):
        # generate complete map
        color, attr = ColorPalette.getColorByColor(Color.black)
        bg, _ = ColorPalette.getColorByColor(Color.black)
        emptyCell = {
            'keycode': '',
            'color': color,
            'bgcolor': bg,
            'attr': attr,
        }
        cells = [[copy.copy(emptyCell) for i in range(height)] for j in range(width)]

        # fill map

        # street border
        y = self.topborder
        x = 0
        while x < width:
            cells[x][y]['keycode'] = '─'
            cells[x][y]['attr'] = Screen.A_BOLD
            x += 1

        # street horizontal
        y = self.topborder + 7
        x = 0
        while x < width:
            if x % 6 == 0:
                cells[x][y]['keycode'] = '─'
                cells[x + 1][y]['keycode'] = '─'
                cells[x][y]['attr'] = Screen.A_BOLD
                cells[x + 1][y]['attr'] = Screen.A_BOLD
            x += 1

        # street vertical/diagonal
        y = self.topborder + 7
        x = 0
        logging.info("W: {}".format(width))
        while x < width - 40:
            if x % 80 == 0:
                # logging.info("L: {}".format(x))
                xx = 0
                yy = 24
                while xx < 16:
                    cells[x + xx][yy]['keycode'] = '/'
                    cells[x + xx][yy]['attr'] = Screen.A_BOLD
                    xx += 1
                    yy -= 1
            x += 1

        # buildings
        x = 0
        while x < width - 8:
            xOff = random.choice([-1, -2, 0, 1, 2])
            theight = random.choice([4, 5, 6, 7])
            yOff = self.topborder - theight + 1
            twidth = random.choice([4, 5, 6])

            x += xOff
            y = yOff
            #logging.info("Create tower: {}/{}  height: {}  width: {}".format(
            #    x, y, height, width
            #))
            #logging.info("Y: {} {} {}".format(self.topborder, theight, y))

            c = self.createTower(theight, twidth)
            self.insertArr(cells, c, x, y)

            x += twidth

        return cells
예제 #19
0
    def createTower(self, height, width):
        color, attr = ColorPalette.getColorByColor(Color.black)
        bg, _ = ColorPalette.getColorByColor(Color.black)
        emptyCell = {
            'keycode': '',
            'color': color,
            'bgcolor': bg,
            'attr': attr,
        }
        cells = [[copy.copy(emptyCell) for i in range(height)] for j in range(width)]

        m = ' '
        bgColors = random.choice([
            ColorPalette.getColorByColor(Color.blue),
            ColorPalette.getColorByColor(Color.blue),
            ColorPalette.getColorByColor(Color.black),
        ])
        # attr = random.choice(
        #     None,
        #     Screen.A_BOLD
        # )

        x = 0
        while x < len(cells):
            y = 0
            while y < len(cells[x]):
                # corner bottom left
                if x == 0 and y == len(cells[0]) - 1:
                    cells[x][y]['keycode'] = '┴'
                    cells[x][y]['attr'] = Screen.A_BOLD
                # corner top left
                elif x == 0 and y == 0:
                    cells[x][y]['keycode'] = '┌'
                    cells[x][y]['attr'] = Screen.A_BOLD

                # corner bottom right
                elif x == len(cells) - 1 and y == len(cells[0]) - 1:
                    cells[x][y]['keycode'] = '┴'
                    cells[x][y]['attr'] = Screen.A_BOLD
                # corner top right
                elif x == len(cells) - 1 and y == 0:
                    cells[x][y]['keycode'] = '┐'
                    cells[x][y]['attr'] = Screen.A_BOLD

                elif x == 0:
                    cells[x][y]['keycode'] = '│'
                    cells[x][y]['attr'] = Screen.A_BOLD
                elif x == len(cells) - 1:
                    cells[x][y]['keycode'] = '│'
                    cells[x][y]['attr'] = Screen.A_BOLD
                elif y == 0:
                    cells[x][y]['keycode'] = '─'
                    cells[x][y]['attr'] = Screen.A_BOLD
                elif y == len(cells[0]) - 1:
                    cells[x][y]['keycode'] = '─'
                    cells[x][y]['attr'] = Screen.A_BOLD

                else:
                    cells[x][y]['keycode'] = m
                    cells[x][y]['bgcolor'] = bgColors[0]  # no attr for bgcolor

                y += 1

            x += 1

        # logging.info("A: {}".format(cells))
        return cells
예제 #20
0
    def checkReceiveDamage(self):
        healthUpdated = False

        for msg in directMessaging.getByType(DirectMessageType.receiveDamage):
            entity = EntityFinder.findAttackableByGroupId(
                self.world, msg.groupId)
            if entity is None:
                # May be already deleted?
                continue

            meRenderable = self.world.component_for_entity(entity, Renderable)
            meAttackable = self.world.component_for_entity(entity, Attackable)
            meGroupId = self.world.component_for_entity(entity, GroupId)

            damage = msg.data['damage']
            byPlayer = msg.data['byPlayer']
            direction = msg.data['direction']
            knockback = msg.data['knockback']
            stun = msg.data['stun']
            isPlayer = self.world.has_component(entity, Player)

            # enemy can attack player, and vice-versa
            if not byPlayer ^ isPlayer:
                continue

            if damage == 0:
                continue

            # change health
            meAttackable.adjustHealth(-1 * damage)
            healthUpdated = True
            logger.info("{} got {} damage, new health: {}".format(
                meRenderable, damage, meAttackable.getHealth()))

            # gfx: show floating damage numbers
            if Config.showEnemyDamageNumbers:
                messaging.add(type=MessageType.EmitMirageParticleEffect,
                              data={
                                  'location':
                                  meRenderable.getLocationTopCenter(),
                                  'effectType':
                                  ParticleEffectType.floatingDamage,
                                  'damage': damage,
                                  'byPlayer': byPlayer,
                                  'direction': Direction.none,
                              })

            # gfx: emit on-hit particles
            if Config.showBurstOnImpact:
                if damage > Config.showBurstOnImpactDamage:
                    messaging.add(
                        type=MessageType.EmitMirageParticleEffect,
                        data={
                            'location':
                            meRenderable.getAttackBaseLocationInverted(),
                            'effectType':
                            ParticleEffectType.hitBurst,
                            'damage':
                            0,
                            'byPlayer':
                            byPlayer,
                            'direction':
                            direction,
                        })

            # no stun, knockdown, knockback, or new color if there is no health left
            # (animations may overwrite each other)
            if meAttackable.getHealth() <= 0.0:
                continue

            # gfx: set texture color
            healthPercentage = meAttackable.getHealthPercentage()
            if healthPercentage > 0.5:
                meRenderable.texture.setOverwriteColorFor(
                    Config.overwriteColorTime,
                    ColorPalette.getColorByColor(Color.yellow))
            else:
                meRenderable.texture.setOverwriteColorFor(
                    Config.overwriteColorTime,
                    ColorPalette.getColorByColor(Color.red))

            # handle: stun
            if stun and meAttackable.isStunnable():
                stunTime = meAttackable.stunTime
                meAttackable.stunTimer.setTimer(timerValue=stunTime)
                meAttackable.stunTimer.start()
                meAttackable.isStunned = True
                meAttackable.addStun(stunTime=stunTime)

                # handle: knockdown
                if random.random() < meAttackable.knockdownChance:
                    messaging.add(
                        type=MessageType.EntityKnockdown,
                        data={},
                        groupId=meGroupId.getId(),
                    )
                else:
                    messaging.add(
                        type=MessageType.EntityStun,
                        data={
                            'timerValue': stunTime,
                        },
                        groupId=meGroupId.getId(),
                    )

                # no additional effects
                continue

            # handle: knockback
            if knockback and random.random() < meAttackable.knockbackChance:
                if direction is Direction.left:
                    x = -2
                else:
                    x = 2

                if isPlayer:
                    directMessaging.add(
                        groupId=meGroupId.getId(),
                        type=DirectMessageType.movePlayer,
                        data={
                            'x': x,
                            'y': 0,
                            'dontChangeDirection': True,
                            'whenMoved': "showOnKnockback",
                        },
                    )
                else:
                    directMessaging.add(
                        groupId=meGroupId.getId(),
                        type=DirectMessageType.moveEnemy,
                        data={
                            'x': x,
                            'y': 0,
                            'dontChangeDirection': True,
                            'updateTexture': False,
                            'force': True,
                        },
                    )

                # no additional effects
                continue

        return healthUpdated