From 7705e18014ffeca1fb5a3345a53935e5812b71b6 Mon Sep 17 00:00:00 2001 From: Pyrdacor Date: Wed, 19 Aug 2020 16:17:12 +0200 Subject: [PATCH] World maps now use a smaller player sprite --- Ambermoon.Core/MapExtensions.cs | 6 ++- Ambermoon.Core/Render/Character2D.cs | 42 ++++++++++++-------- Ambermoon.Core/Render/Player2D.cs | 28 ++++++++----- Ambermoon.Core/Render/TextureAtlasManager.cs | 31 +++++++++++++++ Ambermoon.Data.Common/IGameData.cs | 1 + Ambermoon.Data.Legacy/GameData.cs | 13 ++++++ 6 files changed, 94 insertions(+), 27 deletions(-) diff --git a/Ambermoon.Core/MapExtensions.cs b/Ambermoon.Core/MapExtensions.cs index 49a9e140..a209916d 100644 --- a/Ambermoon.Core/MapExtensions.cs +++ b/Ambermoon.Core/MapExtensions.cs @@ -32,8 +32,10 @@ static MapEvent ExecuteEvent(Map map, Game game, IRenderPlayer player, MapEventT var newMap = mapManager.GetMap(mapChangeEvent.MapIndex); // The position (x, y) is 1-based in the data so we subtract 1. - // Moreover the players position is 1 tile below its drawing position in 2D so subtract another 1 from y. - player.MoveTo(newMap, mapChangeEvent.X - 1, mapChangeEvent.Y - (newMap.Type == MapType.Map2D ? 2u : 1u), + // Moreover the players position is 1 tile below its drawing position + // in non-world 2D so subtract another 1 from y. + player.MoveTo(newMap, mapChangeEvent.X - 1, + mapChangeEvent.Y - (newMap.Type == MapType.Map2D && !newMap.IsWorldMap ? 2u : 1u), ticks, true, mapChangeEvent.Direction); break; diff --git a/Ambermoon.Core/Render/Character2D.cs b/Ambermoon.Core/Render/Character2D.cs index ecb5a973..3d2fa632 100644 --- a/Ambermoon.Core/Render/Character2D.cs +++ b/Ambermoon.Core/Render/Character2D.cs @@ -22,6 +22,9 @@ public enum State readonly ITextureAtlas textureAtlas; readonly IAnimatedSprite sprite; readonly Character2DAnimationInfo animationInfo; + readonly Character2DAnimationInfo worldAnimationInfo; + Character2DAnimationInfo CurrentAnimationInfo => Map.Map.IsWorldMap + ? worldAnimationInfo : animationInfo; public uint CurrentBaseFrameIndex { get; private set; } public uint CurrentFrameIndex { get; private set; } public uint CurrentFrame => sprite.CurrentFrame; @@ -29,36 +32,40 @@ public enum State public CharacterDirection Direction { get; private set; } = CharacterDirection.Down; public RenderMap2D Map { get; private set; } // Note: No character will appear on world maps so the map is always a non-world map (exception is the player) public Position Position { get; } // in Tiles + public Position Padding { get; } = new Position(0, 0); // left and top padding in pixels inside the tile public bool Visible { get => sprite.Visible; set => sprite.Visible = value; } public State CurrentState { get; private set; } - public uint NumFrames => CurrentState switch + public uint NumFrames => Math.Max(1, CurrentState switch { - State.Stand => animationInfo.NumStandFrames, - State.Sit => animationInfo.NumSitFrames, - State.Sleep => animationInfo.NumSleepFrames, + State.Stand => CurrentAnimationInfo.NumStandFrames, + State.Sit => CurrentAnimationInfo.NumSitFrames, + State.Sleep => CurrentAnimationInfo.NumSleepFrames, _ => throw new ArgumentOutOfRangeException("Invalid character state") - }; + }); public Character2D(Game game, IRenderLayer layer, ITextureAtlas textureAtlas, ISpriteFactory spriteFactory, - Character2DAnimationInfo animationInfo, RenderMap2D map, Position startPosition, uint paletteIndex) + Character2DAnimationInfo animationInfo, RenderMap2D map, Position startPosition, uint paletteIndex, + Character2DAnimationInfo? worldAnimationInfo = null) { this.game = game; + Map = map; this.textureAtlas = textureAtlas; this.animationInfo = animationInfo; - CurrentBaseFrameIndex = CurrentFrameIndex = animationInfo.StandFrameIndex; + this.worldAnimationInfo = worldAnimationInfo ?? animationInfo; + var currentAnimationInfo = CurrentAnimationInfo; + CurrentBaseFrameIndex = CurrentFrameIndex = currentAnimationInfo.StandFrameIndex; var textureOffset = textureAtlas.GetOffset(CurrentFrameIndex); - sprite = spriteFactory.CreateAnimated(animationInfo.FrameWidth, animationInfo.FrameHeight, - textureOffset.X, textureOffset.Y, textureAtlas.Texture.Width, animationInfo.NumStandFrames); + sprite = spriteFactory.CreateAnimated(currentAnimationInfo.FrameWidth, currentAnimationInfo.FrameHeight, + textureOffset.X, textureOffset.Y, textureAtlas.Texture.Width, currentAnimationInfo.NumStandFrames); sprite.Layer = layer; - sprite.X = Global.Map2DViewX + (startPosition.X - (int)map.ScrollX) * RenderMap2D.TILE_WIDTH; - sprite.Y = Global.Map2DViewY + (startPosition.Y - (int)map.ScrollY) * RenderMap2D.TILE_HEIGHT; - sprite.BaseLineOffset = 1; + sprite.X = Global.Map2DViewX + (startPosition.X - (int)map.ScrollX) * RenderMap2D.TILE_WIDTH + Padding.X; + sprite.Y = Global.Map2DViewY + (startPosition.Y - (int)map.ScrollY) * RenderMap2D.TILE_HEIGHT + Padding.Y; + sprite.BaseLineOffset = 1 - Padding.Y + Math.Max(0, RenderMap2D.TILE_HEIGHT - currentAnimationInfo.FrameHeight % RenderMap2D.TILE_HEIGHT); sprite.PaletteIndex = (byte)paletteIndex; - Map = map; Position = startPosition; } @@ -114,7 +121,9 @@ public virtual void MoveTo(Map map, uint x, uint y, uint ticks, bool frameReset, Direction = newDirection.Value; } + var animationInfo = CurrentAnimationInfo; var tileType = Map[x, y + 1].Type; + sprite.Resize(animationInfo.FrameWidth, animationInfo.FrameHeight); CurrentState = tileType switch { Data.Map.TileType.ChairUp => State.Sit, @@ -145,14 +154,15 @@ public virtual void MoveTo(Map map, uint x, uint y, uint ticks, bool frameReset, lastFrameReset = ticks; Position.X = (int)x; Position.Y = (int)y; - sprite.X = Global.Map2DViewX + (Position.X - (int)Map.ScrollX) * RenderMap2D.TILE_WIDTH; - sprite.Y = Global.Map2DViewY + (Position.Y - (int)Map.ScrollY) * RenderMap2D.TILE_HEIGHT; + sprite.X = Global.Map2DViewX + (Position.X - (int)Map.ScrollX) * RenderMap2D.TILE_WIDTH + Padding.X; + sprite.Y = Global.Map2DViewY + (Position.Y - (int)Map.ScrollY) * RenderMap2D.TILE_HEIGHT + Padding.Y; + sprite.BaseLineOffset = 1 - Padding.Y + Math.Max(0, RenderMap2D.TILE_HEIGHT - animationInfo.FrameHeight % RenderMap2D.TILE_HEIGHT); ; } public virtual void Update(uint ticks) { uint elapsedTicks = ticks - lastFrameReset; - sprite.CurrentFrame = elapsedTicks / animationInfo.TicksPerFrame; // this will take care of modulo frame count + sprite.CurrentFrame = elapsedTicks / CurrentAnimationInfo.TicksPerFrame; // this will take care of modulo frame count CurrentFrameIndex = CurrentBaseFrameIndex + sprite.CurrentFrame; } diff --git a/Ambermoon.Core/Render/Player2D.cs b/Ambermoon.Core/Render/Player2D.cs index 65398374..9f24ccff 100644 --- a/Ambermoon.Core/Render/Player2D.cs +++ b/Ambermoon.Core/Render/Player2D.cs @@ -1,5 +1,4 @@ using Ambermoon.Data; -using System; namespace Ambermoon.Render { @@ -8,11 +7,13 @@ internal class Player2D : Character2D, IRenderPlayer readonly Player player; readonly IMapManager mapManager; + // TODO: gameData.PlayerAnimationInfo is only for Lyramion. Offsets need to be increased by World * 17 later. public Player2D(Game game, IRenderLayer layer, Player player, RenderMap2D map, ISpriteFactory spriteFactory, IGameData gameData, Position startPosition, IMapManager mapManager) : base(game, layer, TextureAtlasManager.Instance.GetOrCreate(Layer.Characters), - spriteFactory, gameData.PlayerAnimationInfo, map, startPosition, 7u) + spriteFactory, gameData.PlayerAnimationInfo, map, startPosition, 7u, + gameData.WorldPlayerAnimationInfo) { this.player = player; this.mapManager = mapManager; @@ -28,12 +29,18 @@ public bool Move(int x, int y, uint ticks, CharacterDirection? prevDirection = n int newX = Position.X + x; int newY = Position.Y + y; var map = Map.Map; + Map.Tile tile; if (!map.IsWorldMap) { - // Each map should have a border of 1 (walls) + // Don't leave the map. + // Note that the player is 2 tiles tall in non-world maps + // and the position is the upper tile so he is allowed to + // move up to y = -1 and only down to y = map.Height - 1. if (newX < 0 || newY < -1 || newX >= map.Width || newY >= map.Height - 1) canMove = false; + + tile = Map[(uint)newX, (uint)newY + 1]; } else { @@ -41,9 +48,9 @@ public bool Move(int x, int y, uint ticks, CharacterDirection? prevDirection = n newX += map.Width; while (newY < 0) newY += map.Height; - } - var tile = Map[(uint)newX, (uint)newY + 1]; + tile = Map[(uint)newX, (uint)newY]; + } if (canMove) { @@ -91,9 +98,9 @@ public bool Move(int x, int y, uint ticks, CharacterDirection? prevDirection = n else if (x < 0 && (map.IsWorldMap || (newX <= map.Width - 7 && newX >= 5))) scrollX = -1; - if (y > 0 && (map.IsWorldMap || (newY >= 5 && newY <= map.Height - 5))) + if (y > 0 && (map.IsWorldMap || (newY >= 4 && newY <= map.Height - 5))) scrollY = 1; - else if (y < 0 && (map.IsWorldMap || (newY <= map.Height - 6 && newY >= 4))) + else if (y < 0 && (map.IsWorldMap || (newY <= map.Height - 7 && newY >= 3))) scrollY = -1; if (y > 0) @@ -113,8 +120,9 @@ public bool Move(int x, int y, uint ticks, CharacterDirection? prevDirection = n var prevState = CurrentState; MoveTo(oldMap, (uint)newX, (uint)newY, ticks, frameReset, null); - // We trigger with our lower half so add 1 to y - Map.TriggerEvents(this, MapEventTrigger.Move, (uint)newX, (uint)newY + 1, mapManager, ticks); + // We trigger with our lower half so add 1 to y in non-world maps. + Map.TriggerEvents(this, MapEventTrigger.Move, (uint)newX, + (uint)newY + (oldMap.IsWorldMap ? 0u : 1u), mapManager, ticks); if (oldMap == Map.Map) // might have changed by map change events { @@ -170,6 +178,8 @@ public override void MoveTo(Map map, uint x, uint y, uint ticks, bool frameReset if (Map.Map != map) { Visible = true; // reset visibility before changing map + + Padding.Y = map.IsWorldMap ? -4 : 0; } base.MoveTo(map, x, y, ticks, frameReset, newDirection); diff --git a/Ambermoon.Core/Render/TextureAtlasManager.cs b/Ambermoon.Core/Render/TextureAtlasManager.cs index c22cc076..fe1fed6e 100644 --- a/Ambermoon.Core/Render/TextureAtlasManager.cs +++ b/Ambermoon.Core/Render/TextureAtlasManager.cs @@ -138,6 +138,37 @@ public void AddAll(IGameData gameData, IGraphicProvider graphicProvider, IFontPr for (int i = 0; i < playerGraphics.Count; ++i) AddTexture(Layer.Characters, (uint)i, playerGraphics[i]); + // TODO: On world maps another smaller image (12x24?) is used. It isn't part of some file + // so I guess it is inside the AM2_CPU somewhere. For now we use a shrinked version of the indoor graphic. + // On world map only 4 sprites are used (one for each direction). + Graphic Shrink(Graphic graphic) + { + var shrinked = new Graphic + { + Width = 12, + Height = 24, + IndexedGraphic = graphic.IndexedGraphic + }; + + shrinked.Data = new byte[shrinked.Width * shrinked.Height]; + + for (int y = 0; y < shrinked.Height; ++y) + { + for (int x = 0; x < shrinked.Width; ++x) + { + int index = x + y * shrinked.Width; + int sourceIndex = Math.Min(15, Math.Max(0, x * 3 / 2 - 1)) + Math.Min(31, Math.Max(0, y * 3 / 2 - 1)) * graphic.Width; + shrinked.Data[index] = graphic.Data[sourceIndex]; + } + } + + return shrinked; + } + AddTexture(Layer.Characters, (uint)(playerGraphics.Count + 0), Shrink(playerGraphics[1])); + AddTexture(Layer.Characters, (uint)(playerGraphics.Count + 1), Shrink(playerGraphics[4])); + AddTexture(Layer.Characters, (uint)(playerGraphics.Count + 2), Shrink(playerGraphics[7])); + AddTexture(Layer.Characters, (uint)(playerGraphics.Count + 3), Shrink(playerGraphics[11])); + #endregion #region UI Layout diff --git a/Ambermoon.Data.Common/IGameData.cs b/Ambermoon.Data.Common/IGameData.cs index fa31c830..98cf2ac1 100644 --- a/Ambermoon.Data.Common/IGameData.cs +++ b/Ambermoon.Data.Common/IGameData.cs @@ -9,5 +9,6 @@ public interface IGameData void Load(string folderPath); Character2DAnimationInfo PlayerAnimationInfo { get; } + Character2DAnimationInfo WorldPlayerAnimationInfo { get; } } } diff --git a/Ambermoon.Data.Legacy/GameData.cs b/Ambermoon.Data.Legacy/GameData.cs index 59fac13f..f3cde270 100644 --- a/Ambermoon.Data.Legacy/GameData.cs +++ b/Ambermoon.Data.Legacy/GameData.cs @@ -194,5 +194,18 @@ void HandleFileNotFound(string file) NumSleepFrames = 1, TicksPerFrame = 0 }; + + public Character2DAnimationInfo WorldPlayerAnimationInfo => new Character2DAnimationInfo + { + FrameWidth = 12, + FrameHeight = 24, + StandFrameIndex = 3 * 17, + SitFrameIndex = 0, + SleepFrameIndex = 0, + NumStandFrames = 1, + NumSitFrames = 0, + NumSleepFrames = 0, + TicksPerFrame = 0 + }; } }