Map lighting by the time of day, lit tiles and events; A2 autotiles
This commit is contained in:
1 parent
0027c8ded0
commit
88ab26dcec
19 files changed
+573
-10
No files matched your search
@@ -0,0 +1,64 @@
|
||||
namespace Cthangover.Mods.Rpg.Tests
|
||||
{
|
||||
using Cthangover.Core.Mods.Testing;
|
||||
using Godot;
|
||||
|
||||
/// <summary>Autotile cells are assembled from quarters of the A2 block picked by their neighbours.</summary>
|
||||
public class RpgAutotilesTests
|
||||
{
|
||||
private const int Water = 40;
|
||||
|
||||
private static bool Is(Vector2I[] quarters, params int[] xy)
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
if (quarters[i] != new Vector2I(xy[i * 2], xy[i * 2 + 1]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[ModTest("autotiles/quarters_by_neighbours")]
|
||||
static void QuartersByNeighbours(ModTestContext ctx)
|
||||
{
|
||||
ModAssert.True(Is(RpgAutotiles.Quarters(false, false, false, false, false, false, false, false), 0, 2, 3, 2, 0, 5, 3, 5),
|
||||
"alone: the four outer corners");
|
||||
ModAssert.True(Is(RpgAutotiles.Quarters(true, true, true, true, true, true, true, true), 2, 4, 1, 4, 2, 3, 1, 3),
|
||||
"surrounded: the fill of the patch");
|
||||
ModAssert.True(Is(RpgAutotiles.Quarters(true, true, true, true, false, false, false, false), 2, 0, 3, 0, 2, 1, 3, 1),
|
||||
"sides alike, corners not: the inner corners");
|
||||
ModAssert.True(Is(RpgAutotiles.Quarters(false, true, true, true, false, false, true, true), 2, 2, 1, 2, 2, 3, 1, 3),
|
||||
"the top row of a pond: top edge above, fill below");
|
||||
ModAssert.True(Is(RpgAutotiles.Quarters(true, true, false, true, false, true, false, true), 0, 4, 1, 4, 0, 3, 1, 3),
|
||||
"the left column: left edge");
|
||||
var corner = RpgAutotiles.Quarters(true, true, true, true, false, true, true, true);
|
||||
ModAssert.True(corner[0] == new Vector2I(2, 0) && corner[3] == new Vector2I(1, 3), "only the missing corner turns inner");
|
||||
}
|
||||
|
||||
[ModTest("autotiles/cells_read_their_layer_and_the_map_edge_counts_alike")]
|
||||
static void QuartersAt(ModTestContext ctx)
|
||||
{
|
||||
var grid = new RpgMapGrid(3, 3);
|
||||
grid.SetTile(RpgLayer.Back, 0, 0, Water);
|
||||
grid.SetTile(RpgLayer.Back, 1, 0, Water);
|
||||
grid.SetTile(RpgLayer.Main, 0, 1, Water);
|
||||
|
||||
var corner = RpgAutotiles.QuartersAt(grid, RpgLayer.Back, 0, 0, Water);
|
||||
ModAssert.True(corner[0] == new Vector2I(2, 4), "off the map counts as water: the top-left quarter is fill");
|
||||
ModAssert.True(corner[2] == new Vector2I(2, 5), "below lies grass on this layer: bottom edge (the main layer is not read)");
|
||||
ModAssert.True(corner[1] == new Vector2I(1, 4), "to the right is water");
|
||||
|
||||
var end = RpgAutotiles.QuartersAt(grid, RpgLayer.Back, 1, 0, Water);
|
||||
ModAssert.True(end[1] == new Vector2I(3, 4), "the right end: right edge");
|
||||
ModAssert.True(end[3] == new Vector2I(3, 5), "and its outer bottom-right corner");
|
||||
}
|
||||
|
||||
[ModTest("autotiles/source_and_preview_rects")]
|
||||
static void Rects(ModTestContext ctx)
|
||||
{
|
||||
ModAssert.True(RpgAutotiles.SourceRect(new Vector2I(3, 5), 32) == new Rect2(48, 80, 16, 16), "quarters are half tiles");
|
||||
ModAssert.True(RpgAutotiles.SourceRect(Vector2I.Zero, 48) == new Rect2(0, 0, 24, 24), "of any tile size");
|
||||
ModAssert.True(RpgAutotiles.PreviewRect(32) == new Rect2(0, 0, 32, 32), "the preview is the top-left tile");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Cthangover.Mods.Rpg.Tests
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
/// <summary>Event pages pick the last page whose condition holds; routes resolve their steps.</summary>
|
||||
/// <summary>Event pages pick the last page whose condition holds; routes resolve their steps; a page light replaces the event's.</summary>
|
||||
public class RpgEventPagesTests
|
||||
{
|
||||
private static RpgMapEvent Chest() => JsonSerializer.Deserialize<RpgMapEvent>(@"{
|
||||
@@ -87,5 +87,27 @@ namespace Cthangover.Mods.Rpg.Tests
|
||||
ModAssert.True(mapEvent.Route.Repeat, "repeats by default");
|
||||
ModAssert.Null(mapEvent.Pages, "no pages");
|
||||
}
|
||||
|
||||
[ModTest("lights/page_light_over_event_light_round_trip")]
|
||||
static void PageLightOverEventLight(ModTestContext ctx)
|
||||
{
|
||||
var lamp = JsonSerializer.Deserialize<RpgMapEvent>(@"{
|
||||
""Id"": ""lamp"", ""Tile"": 12, ""Light"": { ""Radius"": 2.5, ""Color"": ""#ff8040"" },
|
||||
""Pages"": [ { ""Condition"": ""lamp_out == 1"", ""Tile"": 13, ""Light"": { ""Radius"": 1 } }, { ""Condition"": ""door == 1"" } ]
|
||||
}", JsonOptionsProvider.ReadOptions);
|
||||
ModAssert.CloseTo(2.5, lamp.Light.Radius, what: "the event's light");
|
||||
ModAssert.Equal("#ff8040", lamp.Light.Color, "the event's colour");
|
||||
|
||||
var dim = RpgEventPages.Effective(lamp, 1);
|
||||
ModAssert.CloseTo(1, dim.Light.Radius, what: "the page's light replaces it");
|
||||
ModAssert.Equal("#ffd8a0", dim.Light.Color, "a page light without a colour is warm by default");
|
||||
ModAssert.CloseTo(2.5, RpgEventPages.Effective(lamp, 2).Light.Radius, what: "a page without a light keeps the event's");
|
||||
|
||||
var again = JsonSerializer.Deserialize<RpgMapEvent>(JsonSerializer.Serialize(lamp), JsonOptionsProvider.ReadOptions);
|
||||
ModAssert.CloseTo(2.5, again.Light.Radius, what: "radius survives the round trip");
|
||||
ModAssert.Equal("#ff8040", again.Light.Color, "colour survives the round trip");
|
||||
ModAssert.CloseTo(1, again.Pages[0].Light.Radius, what: "the page light survives the round trip");
|
||||
ModAssert.Null(JsonSerializer.Deserialize<RpgMapEvent>(@"{ ""Id"": ""dark"" }", JsonOptionsProvider.ReadOptions).Light, "no light by default");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace Cthangover.Mods.Rpg.Tests
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Cthangover.Core.Mods.Testing;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
/// <summary>The map's light: ambient weights from the clock, lit tiles, the fixed ambient of a map.</summary>
|
||||
public class RpgLightingTests
|
||||
{
|
||||
[ModTest("lights/weights_follow_clock_dark_mode_and_switch")]
|
||||
static void WeightsFollowClock(ModTestContext ctx)
|
||||
{
|
||||
ModAssert.CloseTo(1, RpgLighting.Weights(13f, false, true).Y, what: "noon is day");
|
||||
ModAssert.CloseTo(1, RpgLighting.Weights(0.5f, false, true).W, what: "midnight is night");
|
||||
ModAssert.CloseTo(1, RpgLighting.Weights(13f, true, true).W, what: "dark mode is night at noon");
|
||||
ModAssert.CloseTo(1, RpgLighting.Weights(0.5f, false, false).Y, what: "lighting switched off keeps day at midnight");
|
||||
var dawn = RpgLighting.Weights(5f, false, true);
|
||||
ModAssert.CloseTo(1, dawn.X + dawn.W, what: "dawn blends night into morning");
|
||||
ModAssert.True(dawn.X > 0 && dawn.W > 0, "both phases at dawn");
|
||||
}
|
||||
|
||||
[ModTest("lights/tile_lights_on_any_layer_once_per_cell")]
|
||||
static void TileLights(ModTestContext ctx)
|
||||
{
|
||||
var tileset = new RpgTileset
|
||||
{
|
||||
ID = "lit",
|
||||
Tiles = new List<RpgTileInfo>
|
||||
{
|
||||
new() { Index = 3, Light = new RpgLight { Radius = 4 } },
|
||||
new() { Index = 7, Light = new RpgLight { Radius = 1.5f, Color = "#ffd27a" } },
|
||||
new() { Index = 9, Solid = true },
|
||||
},
|
||||
};
|
||||
var grid = new RpgMapGrid(4, 3);
|
||||
grid.SetTile(RpgLayer.Front, 1, 0, 3);
|
||||
grid.SetTile(RpgLayer.Main, 2, 2, 7);
|
||||
grid.SetTile(RpgLayer.Back, 2, 2, 3);
|
||||
grid.SetTile(RpgLayer.Main, 0, 1, 9);
|
||||
|
||||
var lights = RpgLighting.TileLights(grid, tileset);
|
||||
ModAssert.Equal(2, lights.Count, "one light per lit cell, unlit tiles give none");
|
||||
ModAssert.True(lights.Exists(l => l.Cell == new Vector2I(1, 0) && Mathf.IsEqualApprox(l.Light.Radius, 4f)), "a front-layer lamp");
|
||||
ModAssert.True(lights.Exists(l => l.Cell == new Vector2I(2, 2)), "a cell lit on two layers counts once");
|
||||
}
|
||||
|
||||
[ModTest("lights/map_ambient_json")]
|
||||
static void MapAmbientJson(ModTestContext ctx)
|
||||
{
|
||||
var room = JsonSerializer.Deserialize<RpgMap>(@"{ ""ID"": ""room"", ""Ambient"": ""#e8dcc8"" }", JsonOptionsProvider.ReadOptions);
|
||||
ModAssert.Equal("#e8dcc8", room.Ambient, "a lit room");
|
||||
var again = JsonSerializer.Deserialize<RpgMap>(JsonSerializer.Serialize(room), JsonOptionsProvider.ReadOptions);
|
||||
ModAssert.Equal("#e8dcc8", again.Ambient, "ambient survives the round trip");
|
||||
ModAssert.Null(JsonSerializer.Deserialize<RpgMap>(@"{ ""ID"": ""field"" }", JsonOptionsProvider.ReadOptions).Ambient, "outdoors by default");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,26 @@ namespace Cthangover.Mods.Rpg.Tests
|
||||
ModAssert.False(tileset.IsSolid(-1), "empty cell");
|
||||
}
|
||||
|
||||
[ModTest("rules/tileset_light_and_autotile_lookup_round_trip")]
|
||||
static void TilesetLightAndAutotileLookup(ModTestContext ctx)
|
||||
{
|
||||
var tileset = System.Text.Json.JsonSerializer.Deserialize<RpgTileset>(@"{
|
||||
""Id"": ""lit"", ""Tiles"": [
|
||||
{ ""Index"": 29, ""Tag"": ""lamp_top"", ""Light"": { ""Radius"": 4, ""Color"": ""#ffcf80"" } },
|
||||
{ ""Index"": 40, ""Solid"": true, ""Tag"": ""water"", ""Autotile"": ""village/water_auto"" }
|
||||
] }", Cthangover.Core.Utils.JsonOptionsProvider.ReadOptions);
|
||||
tileset = System.Text.Json.JsonSerializer.Deserialize<RpgTileset>(System.Text.Json.JsonSerializer.Serialize(tileset), Cthangover.Core.Utils.JsonOptionsProvider.ReadOptions);
|
||||
|
||||
ModAssert.CloseTo(4, tileset.LightOf(29).Radius, what: "the lamp's light survives the round trip");
|
||||
ModAssert.Equal("#ffcf80", tileset.LightOf(29).Color, "with its colour");
|
||||
ModAssert.Null(tileset.LightOf(40), "the water gives no light");
|
||||
ModAssert.Null(tileset.LightOf(-1), "an empty cell gives no light");
|
||||
ModAssert.Equal("village/water_auto", tileset.AutotileOf(40)?.Autotile, "the autotile entry and its block");
|
||||
ModAssert.Null(tileset.AutotileOf(29), "a plain tile is no autotile");
|
||||
ModAssert.Null(tileset.AutotileOf(-1), "nor an empty cell");
|
||||
ModAssert.True(tileset.IsSolid(40), "an autotile is solid like any tile");
|
||||
}
|
||||
|
||||
[ModTest("rules/encounter_off_without_table")]
|
||||
static void EncounterOffWithoutTable(ModTestContext ctx)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user