This update adds a basic animation manager and the first derived animation: Animation_ActorTalk.  This is used to display the amount of damage done when hit as text that floats up above the player and fades away.

Animations

Animations are managed by a static AnimationMgr object.  This object is fairly straightforward, tracking a list of all active animations and tying into the Update and Render loops to manage the animations.  Animations derive from an abstract Animation class and implement Completed() and Render() overrides in order to hook into the AnimationMgr’s loops.  Here’s the full text for the Animation_ActorTalk class:

    /// <summary>
    /// Floats a message above a specific Actor.
    /// </summary>
    public class Animation_ActorTalk : Animation
    {
        /// <summary>
        /// Constructor.  Track the actor and message to display
        /// </summary>
        /// <param name="actor">The actor above whom the text will display</param>
        /// <param name="message">The message to display</param>
        /// <param name="color">The color in which the message will be displayed</param>
        public Animation_ActorTalk(Actor actor, string message, Color color)
            : base()
        {
            _actor = actor;
            _color = color;

            // Choose a random horz offset at which to display the message
            _xOffset = (16 - Rand.Next(48)) * 3 / 4;

            // Track the message to display and its width.
            _message = message;
            _msgSize = RenderMgr.MeasureString(_message, _font);
        }

        /// <summary>
        /// Return true when the animation has completed and should be removed
        /// </summary>
        /// <returns>True when the animation has completed</returns>
        public override bool Completed()
        {
            return (AnimationMgr.CurrentTime - this.StartTime).TotalMilliseconds >= _msToDisplay;
        }

        /// <summary>
        /// Render the message above the Actor
        /// </summary>
        public override void Render()
        {
            // Don't render if player can't see the actor
            if (!Player.Current.ThisTurnVisibleCells.Contains(_actor.Location))
                return;

            // Determine how long we've been floating
            int msGoneBy = (int)((AnimationMgr.CurrentTime - this.StartTime).TotalMilliseconds);

            // Determine how high we should have floated by now.
            int pixelFloat = _heightToFloat * msGoneBy / _msToDisplay;

            // Determine the center point of the message
            Point actorMiddleTop = GameState_InDungeon.MapCellToScreenCoords(_actor.Location);
            actorMiddleTop.X += 4;
            Point messageCenter = new Point(actorMiddleTop.X - (int)_msgSize.X / 2 + _xOffset,
                                            actorMiddleTop.Y - pixelFloat);

            // Determine the color (we fade the message near the top)
            Color clr = _color;
            if (pixelFloat > _heightToFloatBeforeFading)
                clr *= 1 - ((float)msGoneBy - 1000) / _msToDisplay;

            // Draw so that it looks outlined.  tbd-later: replace with 'real' font.
            RenderMgr.DrawString(_message, _font, Color.Black, messageCenter.X + 1, messageCenter.Y + 1);
            RenderMgr.DrawString(_message, _font, Color.Black, messageCenter.X - 1, messageCenter.Y - 1);
            RenderMgr.DrawString(_message, _font, Color.Black, messageCenter.X + 1, messageCenter.Y - 1);
            RenderMgr.DrawString(_message, _font, Color.Black, messageCenter.X - 1, messageCenter.Y + 1);
            RenderMgr.DrawString(_message, _font, clr, messageCenter.X, messageCenter.Y);
        }

        // _actor: The actor above whom this message will display
        Actor _actor;

        // _message: The message to display
        string _message;

        // _msgSize: Size of the message to display
        Vector2 _msgSize;

        // _color: The color in which the message will be displayed
        Color _color;

        // _xOffset: Small random horizontal offset for the message (for variety)
        int _xOffset;

        // _font: The Font in which all messages are displayed
        static SpriteFont _font = AssetMgr.ActorTalkFont;

        // _msToDisplay: Total time to display the message (in milliseconds)
        static int _msToDisplay = 2000;

        // _heightToFloat: Total distance in pixels to float up
        static int _heightToFloat = 48;

        // _heightToFloatBeforeFading: How many pixels to float up before starting to fade
        static int _heightToFloatBeforeFading = 32;
    }

Now in order to float any text above any actor, you just create a new Animation_ActorTalk and add it to the AnimationMgr. In Actor.TakeDamage, I’ve added the following code:

            Color color = this is Player ? Color.Red : Color.Orange;
            AnimationMgr.AddAnimation(new Animation_ActorTalk(this, "-" + amountOfDamage, color));

Given that, we now have the equivalent of WoW’s “floating combat text”:

It overlaps text which is a problem if a bunch of things happen at once (e.g. getting hit by 3 monsters at once), but it’ll suffice for now.

Health Bars

As you can see above, this update also adds a healthbar that displays over any Actor that doesn’t have full health.  This required the addition of a “PostRenderTile” virtual function that is called at the end of RenderableObject.Render; derived objects can override it to display overlays – Actors do that to render the health bar.

Updated source: http://wanderlinggames.com/files/dungeon/dungeon-3-21-11.zip

Updated executable: http://wanderlinggames.com/files/dungeon/dungeonexe-3-21-11.zip

Advertisements