Skip to content

Commit

Permalink
World maps now use a smaller player sprite
Browse files Browse the repository at this point in the history
  • Loading branch information
Pyrdacor committed Aug 19, 2020
1 parent b79ba3f commit 7705e18
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 27 deletions.
6 changes: 4 additions & 2 deletions Ambermoon.Core/MapExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
42 changes: 26 additions & 16 deletions Ambermoon.Core/Render/Character2D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,43 +22,50 @@ 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;
uint lastFrameReset = 0u;
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;
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down
28 changes: 19 additions & 9 deletions Ambermoon.Core/Render/Player2D.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Ambermoon.Data;
using System;

namespace Ambermoon.Render
{
Expand All @@ -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;
Expand All @@ -28,22 +29,28 @@ 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
{
while (newX < 0)
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)
{
Expand Down Expand Up @@ -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)
Expand All @@ -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
{
Expand Down Expand Up @@ -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);
Expand Down
31 changes: 31 additions & 0 deletions Ambermoon.Core/Render/TextureAtlasManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Ambermoon.Data.Common/IGameData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public interface IGameData

void Load(string folderPath);
Character2DAnimationInfo PlayerAnimationInfo { get; }
Character2DAnimationInfo WorldPlayerAnimationInfo { get; }
}
}
13 changes: 13 additions & 0 deletions Ambermoon.Data.Legacy/GameData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
}

0 comments on commit 7705e18

Please sign in to comment.