Map editor: wheel/MMB scrolling, characters and items tabs to place NPCs, enemies and pickups, events saved to map_events with other files and mods shown locked, LF output

This commit is contained in:
cthangover committed 2026-10-23 19:09:37 +02:00
1 parent eaf58c0d38
commit 1d6904bb16
14 files changed
+863 -67

No files matched your search

+100 -6
View File
@@ -58,7 +58,7 @@ namespace Cthangover.Tools.RpgEditor.Tests
document.AddEvent(3, 1).Sprite = "village/elder";
ModAssert.True(document.Undo(), "undo the event");
ModAssert.Equal(0, document.Map.Events.Count, "event gone");
ModAssert.Equal(0, document.Events.Count, "event gone");
ModAssert.Equal(9, document.Grid.GetTile(RpgLayer.Front, 0, 0), "the earlier paint stays");
ModAssert.True(document.Undo(), "undo the paint");
ModAssert.Equal(-1, document.Grid.GetTile(RpgLayer.Front, 0, 0), "paint gone");
@@ -66,7 +66,7 @@ namespace Cthangover.Tools.RpgEditor.Tests
ModAssert.True(document.Redo(), "redo the paint");
ModAssert.True(document.Redo(), "redo the event");
ModAssert.Equal("village/elder", document.Map.Events[0].Sprite, "event back with its data");
ModAssert.Equal("village/elder", document.Events[0].Sprite, "event back with its data");
document.Undo();
document.BeginChange();
@@ -87,6 +87,95 @@ namespace Cthangover.Tools.RpgEditor.Tests
ModAssert.Equal("event_1", document.AddEvent(0, 0).Id, "a freed id is used again");
}
[ModTest("document/characters_become_npc_and_enemy_events")]
static void CharactersBecomeNpcAndEnemyEvents(ModTestContext ctx)
{
var document = NewMap();
var npc = document.AddCharacter(1, 2, "rpg_elder", "village/elder", false);
var enemy = document.AddCharacter(3, 1, "rpg_slime", "village/slime", true);
var second = document.AddCharacter(4, 1, "rpg_slime", null, true);
ModAssert.Equal("rpg_elder", npc.Id, "the character id is the event id");
ModAssert.Equal("village/elder", npc.Sprite, "the charset is the look");
ModAssert.Equal(1, npc.X, "x");
ModAssert.Equal(2, npc.Y, "y");
ModAssert.True(npc.Commands == null && npc.Condition == null, "an NPC gets no lines and no condition");
ModAssert.Equal("Action", npc.Trigger.ToString(), "talked to on E/F");
ModAssert.Equal("action rpg.battle enemies=rpg_slime win_flag=test_map_rpg_slime_won", enemy.Commands[0], "an enemy starts a battle with itself");
ModAssert.Equal("end", enemy.Commands[1], "and ends the dialog");
ModAssert.Equal("test_map_rpg_slime_won != 1", enemy.Condition, "hidden once beaten");
ModAssert.Equal("rpg_slime_2", second.Id, "a taken id gets a number");
ModAssert.Null(second.Sprite, "no charset: no look");
ModAssert.Equal("test_map_rpg_slime_2_won != 1", second.Condition, "each placed enemy is beaten separately");
ModAssert.Equal(0, document.Validate().Count, "the ids are valid");
var loaded = RpgMapDocument.Load(document.ToJson(), document.EventsToJson());
var restored = loaded.Events[1];
ModAssert.Equal("rpg_slime", restored.Id, "id survives the file");
ModAssert.Equal("village/slime", restored.Sprite, "sprite survives the file");
ModAssert.Equal(2, restored.Commands.Count, "commands survive the file");
ModAssert.Equal(enemy.Commands[0], restored.Commands[0], "battle line survives the file");
ModAssert.Equal(enemy.Condition, restored.Condition, "condition survives the file");
}
[ModTest("document/items_become_pickups")]
static void ItemsBecomePickups(ModTestContext ctx)
{
var document = NewMap();
var first = document.AddItem(2, 1, "rpg_demo/branches", "rpg_demo/branch_bundle", 3, "Found \"branches\"");
var second = document.AddItem(3, 1, "rpg_demo/branches", null, 0, "x");
ModAssert.Equal("rpg_demo_branches", first.Id, "the id holds no slash");
ModAssert.Equal("rpg_demo/branch_bundle", first.Icon, "the item picture is the look");
ModAssert.Equal("Touch", first.Trigger.ToString(), "picked up by stepping on it");
ModAssert.False(first.IsSolid, "the player can step on it");
ModAssert.Equal("ptext \"Found 'branches'\"", first.Commands[0], "the line, quotes made safe");
ModAssert.Equal("action item.add id=rpg_demo/branches count=3", first.Commands[1], "adds the stack");
ModAssert.Equal("flag test_map_rpg_demo_branches_taken 1", first.Commands[2], "remembers it was taken");
ModAssert.Equal("end", first.Commands[3], "ends the dialog");
ModAssert.Equal("test_map_rpg_demo_branches_taken != 1", first.Condition, "gone once taken");
ModAssert.Equal("rpg_demo_branches_2", second.Id, "a taken id gets a number");
ModAssert.Equal("action item.add id=rpg_demo/branches count=1", second.Commands[1], "at least one item");
ModAssert.Equal("test_map_rpg_demo_branches_2_taken != 1", second.Condition, "each pickup has its own flag");
var restored = RpgMapDocument.Load(document.ToJson(), document.EventsToJson()).Events[0];
ModAssert.Equal("rpg_demo/branch_bundle", restored.Icon, "icon survives the file");
ModAssert.Equal("Touch", restored.Trigger.ToString(), "trigger survives the file");
ModAssert.False(restored.IsSolid, "solidity survives the file");
ModAssert.Equal(4, restored.Commands.Count, "commands survive the file");
ModAssert.Equal(first.Condition, restored.Condition, "condition survives the file");
}
[ModTest("document/locked_events_are_shown_not_edited")]
static void LockedEventsAreShownNotEdited(ModTestContext ctx)
{
var document = NewMap();
document.SetLockedEvents(new[]
{
new RpgMapEvent { Id = "event_1", X = 1, Y = 1, ModId = "quest_mod", File = "map_events/test_map/quest.json" },
new RpgMapEvent { Id = "rpg_slime", X = 2, Y = 2, ModId = "quest_mod", File = "map_events/test_map/quest.json" }
});
ModAssert.Equal(0, document.EventsAt(1, 1).Count, "a locked event is not picked on its cell");
ModAssert.Equal("event_2", document.AddEvent(1, 1).Id, "a new event does not take a locked id");
ModAssert.Equal("rpg_slime_2", document.AddCharacter(0, 0, "rpg_slime", null, true).Id, "nor does a placed character");
document.BeginChange();
document.Events[0].Id = "EVENT_1";
var problems = document.Validate();
ModAssert.Equal(1, problems.Count, "renaming onto a locked id is a problem");
ModAssert.Equal("tools/rpg_editor/problem_event_locked", problems[0].Key, "the problem says why");
ModAssert.True(problems[0].Argument.Contains("quest_mod"), "and names the mod");
document.Undo();
ModAssert.Equal(2, document.LockedEvents.Count, "undo keeps the locked events");
ModAssert.Equal("event_2", document.Events[0].Id, "and restores the own ones");
ModAssert.False(document.EventsToJson().Contains("quest"), "locked events are not written");
}
[ModTest("document/resize_pulls_events_inside")]
static void ResizePullsEventsInside(ModTestContext ctx)
{
@@ -128,15 +217,20 @@ namespace Cthangover.Tools.RpgEditor.Tests
mapEvent.Teleport = new RpgTeleport { Map = "other", X = 4, Y = 5, Dir = RpgDirection.Left };
var json = document.ToJson();
var loaded = RpgMapDocument.Load(json);
var eventsJson = document.EventsToJson();
var loaded = RpgMapDocument.Load(json, eventsJson);
ModAssert.True(json.Contains("\"Kind\": \"rpg_map\""), "the file declares its kind");
ModAssert.False(json.Contains("\"Scenario\""), "empty fields are left out");
ModAssert.False(json.Contains("\"Events\""), "the map file holds no events");
ModAssert.True(eventsJson.Contains("\"Events\""), "the events file holds them");
ModAssert.False(eventsJson.Contains("\"Scenario\""), "empty fields are left out");
ModAssert.Equal("map_events/test_map/events.json", RpgMapDocument.EventsPath("test_map"), "the editor's events file of the map");
ModAssert.Equal(0, RpgMapDocument.Load(json).Events.Count, "a map without an events file has no events");
ModAssert.Equal("test_map", loaded.Map.ID, "id");
ModAssert.Equal(16, loaded.Grid.GetTile(RpgLayer.Main, 3, 2), "main tile");
ModAssert.Equal("o", loaded.Grid.GetCollision(0, 3).ToString(), "collision");
ModAssert.Equal("Touch", loaded.Map.Events[0].Trigger.ToString(), "trigger");
ModAssert.Equal("other", loaded.Map.Events[0].Teleport.Map, "teleport");
ModAssert.Equal("Touch", loaded.Events[0].Trigger.ToString(), "trigger");
ModAssert.Equal("other", loaded.Events[0].Teleport.Map, "teleport");
ModAssert.Throws(() => RpgMapDocument.Load("{\"Kind\":\"rpg_map\",\"Items\":[]}"), "a file without maps", anyException: true);
}
}