diff --git a/Blarg.GameFramework/Blarg.GameFramework.csproj b/Blarg.GameFramework/Blarg.GameFramework.csproj index 5385b18..a2c894a 100644 --- a/Blarg.GameFramework/Blarg.GameFramework.csproj +++ b/Blarg.GameFramework/Blarg.GameFramework.csproj @@ -195,6 +195,7 @@ + @@ -219,6 +220,7 @@ + diff --git a/Blarg.GameFramework/Math/MathHelpers.cs b/Blarg.GameFramework/Math/MathHelpers.cs index f35cb6c..7e2d64f 100644 --- a/Blarg.GameFramework/Math/MathHelpers.cs +++ b/Blarg.GameFramework/Math/MathHelpers.cs @@ -302,6 +302,33 @@ namespace Blarg.GameFramework return temp; } + /// + /// Clamps a value to a given range, but if the value is outside the range + /// instead of returning the low/high end of the range, this will continue + /// counting after moving to the opposite end of the range to arrive at a + /// final value. + /// + /// the clamped value + /// the value to be clamped + /// the low end of the range to clamp to + /// the high end of the range to clamp to + public static int RolloverClamp(int value, int low, int high) + { + int temp = value; + // TODO: this is really shitty... make it better + do + { + int range = Math.Abs(high - low); + if (temp < low) + temp = temp + range; + if (value > high) + temp = temp - range; + } + while (temp < low || temp > high); // loop through as many times as necessary to put the value within the low/high range + + return temp; + } + /// /// Re-scales a given value from an old min/max range to a new and /// different min/max range such that the value is approximately diff --git a/Blarg.GameFramework/TileMap/Tile.cs b/Blarg.GameFramework/TileMap/Tile.cs new file mode 100644 index 0000000..c324757 --- /dev/null +++ b/Blarg.GameFramework/TileMap/Tile.cs @@ -0,0 +1,256 @@ +using System; +using Blarg.GameFramework.Graphics; +using Blarg.GameFramework.Support; + +namespace Blarg.GameFramework.TileMap +{ + public class Tile + { + static readonly Matrix4x4 FaceNorthRotation = Matrix4x4.CreateRotationY(MathConstants.Radians0); + static readonly Matrix4x4 FaceEastRotation = Matrix4x4.CreateRotationY(MathConstants.Radians90); + static readonly Matrix4x4 FaceSouthRotation = Matrix4x4.CreateRotationY(MathConstants.Radians180); + static readonly Matrix4x4 FaceWestRotation = Matrix4x4.CreateRotationY(MathConstants.Radians270); + + public const byte ROTATION_0 = 0; + public const byte ROTATION_90 = 1; + public const byte ROTATION_180 = 2; + public const byte ROTATION_270 = 3; + + public const short NO_TILE = 0; + + public const byte LIGHT_VALUE_MAX = 15; + public const byte LIGHT_VALUE_SKY = LIGHT_VALUE_MAX; + + public const short FLAG_COLLIDEABLE = 1; + public const short FLAG_ROTATED = 2; + public const short FLAG_LARGE_TILE = 4; + public const short FLAG_LARGE_TILE_OWNER = 8; + public const short FLAG_CUSTOM_COLOR = 16; + public const short FLAG_FRICTION_SLIPPERY = 32; + public const short FLAG_LIGHT_SKY = 64; + public const short FLAG_WALKABLE_SURFACE = 128; + + public short TileIndex; + public short Flags; + public byte TileLight; + public byte SkyLight; + public byte Rotation; + public byte ParentTileOffsetX; + public byte ParentTileOffsetY; + public byte ParentTileOffsetZ; + public byte ParentTileWidth; + public byte ParentTileHeight; + public byte ParentTileDepth; + public int Color; + + public float Brightness + { + get + { + if (TileLight > SkyLight) + return GetBrightness(TileLight); + else + return GetBrightness(SkyLight); + } + } + + public float RotationAngle + { + get + { + if (Rotation < 0 || Rotation > 3) + return 0.0f; + else + return Rotation * MathConstants.Radians90; + } + } + + public bool IsEmptySpace + { + get { return TileIndex == NO_TILE; } + } + + public bool IsCollideable + { + get { return Flags.IsBitSet(FLAG_COLLIDEABLE); } + } + + public bool HasCustomColor + { + get { return Flags.IsBitSet(FLAG_CUSTOM_COLOR); } + } + + public bool IsSlippery + { + get { return Flags.IsBitSet(FLAG_FRICTION_SLIPPERY); } + } + + public bool IsSkyLit + { + get { return Flags.IsBitSet(FLAG_LIGHT_SKY); } + } + + public bool IsRotated + { + get { return Flags.IsBitSet(FLAG_ROTATED); } + } + + public bool IsLargeTile + { + get { return Flags.IsBitSet(FLAG_LARGE_TILE); } + } + + public bool IsLargeTileRoot + { + get { return Flags.IsBitSet(FLAG_LARGE_TILE_OWNER); } + } + + public Tile() + { + TileIndex = NO_TILE; + } + + public Tile Set(short tileIndex) + { + TileIndex = tileIndex; + return this; + } + + public Tile Set(short tileIndex, short flags) + { + TileIndex = tileIndex; + Flags = flags; + return this; + } + + public Tile Set(short tileIndex, short flags, Color color) + { + return Set(tileIndex, flags, color.RGBA); + } + + public Tile Set(short tileIndex, short flags, int color) + { + TileIndex = tileIndex; + Flags = Flags.SetBit(FLAG_CUSTOM_COLOR); + Color = color; + return this; + } + + public Tile Set(Tile other) + { + TileIndex = other.TileIndex; + Flags = other.Flags; + TileLight = other.TileLight; + SkyLight = other.SkyLight; + Rotation = other.Rotation; + ParentTileOffsetX = other.ParentTileOffsetX; + ParentTileOffsetY = other.ParentTileOffsetY; + ParentTileOffsetZ = other.ParentTileOffsetZ; + ParentTileWidth = other.ParentTileWidth; + ParentTileHeight = other.ParentTileHeight; + ParentTileDepth = other.ParentTileDepth; + Color = other.Color; + return this; + } + + public Tile SetCustomColor(Color color) + { + return SetCustomColor(color.RGBA); + } + + public Tile SetCustomColor(int color) + { + Flags = Flags.SetBit(FLAG_CUSTOM_COLOR); + this.Color = Color; + return this; + } + + public Tile ClearCustomColor() + { + Flags = Flags.ClearBit(FLAG_CUSTOM_COLOR); + Color = 0; + return this; + } + + public Tile Rotate(byte facingDirection) + { + if (facingDirection < 0 || facingDirection > 3) + throw new ArgumentException("Use one of the ROTATION_X constants."); + Flags = Flags.SetBit(FLAG_ROTATED); + Rotation = facingDirection; + return this; + } + + public Tile RotateClockwise() + { + Flags = Flags.SetBit(FLAG_ROTATED); + Rotation -= 1; + if (Rotation < ROTATION_0) + Rotation = ROTATION_270; + return this; + } + + public Tile RotateClockwise(int times) + { + Flags = Flags.SetBit(FLAG_ROTATED); + Rotation = (byte)MathHelpers.RolloverClamp((int)Rotation - times, (int)ROTATION_0, (int)ROTATION_270 + 1); + return this; + } + + public Tile RotateCounterClockwise() + { + Flags = Flags.SetBit(FLAG_ROTATED); + Rotation += 1; + if (Rotation > ROTATION_270) + Rotation = ROTATION_0; + return this; + } + + public Tile RotateCounterClockwise(int times) + { + Flags = Flags.SetBit(FLAG_ROTATED); + Rotation = (byte)MathHelpers.RolloverClamp((int)Rotation + times, (int)ROTATION_0, (int)ROTATION_270 + 1); + return this; + } + + public static float GetBrightness(byte light) + { + // this is a copy of the brightness formula listed here: + // http://gamedev.stackexchange.com/a/21247 + const float BASE_BRIGHTNESS = 0.086f; + + float normalizedLightValue = (float)light / (float)(LIGHT_VALUE_MAX + 1); + return (float)Math.Pow((float)normalizedLightValue, 1.4f) + BASE_BRIGHTNESS; + } + + public static byte AdjustLightForTranslucency(byte light, float translucency) + { + return (byte)Math.Round((float)light * (1.0f - translucency)); + } + + public static bool GetTransformationFor(Tile tile, ref Matrix4x4 rotation) + { + if (!tile.IsRotated) + return false; + + switch (tile.Rotation) + { + case 0: + rotation = FaceNorthRotation; + return true; + case 1: + rotation = FaceEastRotation; + return true; + case 2: + rotation = FaceSouthRotation; + return true; + case 3: + rotation = FaceWestRotation; + return true; + default: + return false; + } + } + } +} +