diff --git a/prefab/roomTemplates/dungeon/chest.tscn b/prefab/roomTemplates/dungeon/chest.tscn index b728ccd..2206510 100644 --- a/prefab/roomTemplates/dungeon/chest.tscn +++ b/prefab/roomTemplates/dungeon/chest.tscn @@ -55,7 +55,6 @@ shape = SubResource("RectangleShape2D_7tsse") [node name="Marker2D" type="Marker2D" parent="."] position = Vector2(260, 87) script = ExtResource("1_y2vgj") -ResPath = "res://prefab/entitys/DelivererOfDarkMagic.tscn" [node name="NavigationRegion2D" type="NavigationRegion2D" parent="."] navigation_polygon = SubResource("NavigationPolygon_rh1gx") diff --git a/prefab/roomTemplates/dungeon/horizontalCorridor.tscn b/prefab/roomTemplates/dungeon/horizontalCorridor.tscn index ae366e1..454d1f1 100644 --- a/prefab/roomTemplates/dungeon/horizontalCorridor.tscn +++ b/prefab/roomTemplates/dungeon/horizontalCorridor.tscn @@ -54,7 +54,6 @@ shape = SubResource("RectangleShape2D_7tsse") [node name="Marker2D" type="Marker2D" parent="."] position = Vector2(260, 87) script = ExtResource("2_wamhd") -ResPath = "res://prefab/entitys/DelivererOfDarkMagic.tscn" [node name="NavigationRegion2D" type="NavigationRegion2D" parent="."] navigation_polygon = SubResource("NavigationPolygon_rh1gx") diff --git a/prefab/roomTemplates/tutorials/spellEditor.tscn b/prefab/roomTemplates/tutorials/spellEditor.tscn index fead826..b4880fc 100644 --- a/prefab/roomTemplates/tutorials/spellEditor.tscn +++ b/prefab/roomTemplates/tutorials/spellEditor.tscn @@ -37,7 +37,7 @@ position = Vector2(751.5, 289.438) shape = SubResource("RectangleShape2D_jxmys") debug_color = Color(0, 0.6, 0.701961, 0.419608) -[node name="Marker2D" type="Marker2D" parent="."] +[node name="PlayerSpawn" type="Marker2D" parent="."] position = Vector2(54, 256) script = ExtResource("1_q04qt") @@ -73,16 +73,6 @@ position = Vector2(386, 178) scale = Vector2(23.9375, 11.0625) texture = ExtResource("3_atgj7") -[node name="Marker2D2" type="Marker2D" parent="."] -position = Vector2(100, 250) -script = ExtResource("4_6ihp7") -ItemId = "staff_necromancy" - -[node name="Marker2D3" type="Marker2D" parent="."] -position = Vector2(134, 248) -script = ExtResource("4_6ihp7") -ItemId = "necromancy" - [node name="Label" type="Label" parent="."] offset_left = 37.0 offset_top = 46.0 @@ -115,3 +105,15 @@ position = Vector2(715, 162) [node name="WoodenBox4" parent="." instance=ExtResource("7_jybe6")] position = Vector2(714, 122) + +[node name="AutoSpawn" type="Node2D" parent="."] + +[node name="necromancy" type="Marker2D" parent="AutoSpawn"] +position = Vector2(134, 248) +script = ExtResource("4_6ihp7") +ItemId = "necromancy" + +[node name="staff_necromancy" type="Marker2D" parent="AutoSpawn"] +position = Vector2(100, 250) +script = ExtResource("4_6ihp7") +ItemId = "staff_necromancy" diff --git a/prefab/roomTemplates/tutorials/tripleShotSpell.tscn b/prefab/roomTemplates/tutorials/tripleShotSpell.tscn index a340d0a..db85fef 100644 --- a/prefab/roomTemplates/tutorials/tripleShotSpell.tscn +++ b/prefab/roomTemplates/tutorials/tripleShotSpell.tscn @@ -91,11 +91,6 @@ offset_right = 453.0 offset_bottom = 151.0 text = "ui_tutorial_combine_more_powerful_spells" -[node name="Marker2D" type="Marker2D" parent="."] -position = Vector2(434, 105) -script = ExtResource("4_fh50l") -ItemId = "x3" - [node name="WoodenBox" parent="." instance=ExtResource("4_60glh")] position = Vector2(711, 179) @@ -129,3 +124,10 @@ position = Vector2(473, 285) [node name="TripleShotSpell" type="Sprite2D" parent="."] position = Vector2(637, 85) texture = ExtResource("6_2qcf3") + +[node name="AutoSpawn" type="Node2D" parent="."] + +[node name="x3" type="Marker2D" parent="AutoSpawn"] +position = Vector2(434, 105) +script = ExtResource("4_fh50l") +ItemId = "x3" diff --git a/scripts/EventBus.cs b/scripts/EventBus.cs index ab0e9fb..46da7a6 100644 --- a/scripts/EventBus.cs +++ b/scripts/EventBus.cs @@ -9,12 +9,6 @@ namespace ColdMint.scripts; /// public static class EventBus { - /// - /// Event when the AI character is generated - /// AI角色生成事件 - /// - public static Action? AiCharacterGenerateEvent; - /// /// Game Over Event /// 游戏结束事件 diff --git a/scripts/map/AiCharacterSpawn.cs b/scripts/map/AiCharacterSpawn.cs index 846b42e..3e6548d 100644 --- a/scripts/map/AiCharacterSpawn.cs +++ b/scripts/map/AiCharacterSpawn.cs @@ -1,5 +1,5 @@ using ColdMint.scripts.character; -using ColdMint.scripts.map.events; +using ColdMint.scripts.map.room; using ColdMint.scripts.utils; using Godot; @@ -9,52 +9,45 @@ namespace ColdMint.scripts.map; /// Ai character generation point /// Ai角色生成点 /// -public partial class AiCharacterSpawn : Marker2D +public partial class AiCharacterSpawn : Marker2D, ISpawnMarker { private PackedScene? _packedScene; - [Export] - public string? ResPath; + [Export] private string? _resPath; public override void _Ready() { base._Ready(); - if (!string.IsNullOrEmpty(ResPath)) + if (!string.IsNullOrEmpty(_resPath)) { - _packedScene = GD.Load(ResPath); + _packedScene = GD.Load(_resPath); } - - EventBus.AiCharacterGenerateEvent += OnAiCharacterGenerateEvent; } - /// - /// When an event is triggered - /// 当触发事件时 - /// - /// - public void OnAiCharacterGenerateEvent(AiCharacterGenerateEvent aiCharacterGenerateEvent) + public Node2D? Spawn() { - if (GameSceneDepend.AiCharacterContainer == null) + if (GameSceneDepend.AiCharacterContainer == null || _packedScene == null) { - return; - } - - if (_packedScene == null) - { - return; + return null; } var aiCharacter = NodeUtils.InstantiatePackedScene(_packedScene); if (aiCharacter == null) { - return; + return null; } NodeUtils.CallDeferredAddChild(GameSceneDepend.AiCharacterContainer, aiCharacter); aiCharacter.GlobalPosition = GlobalPosition; + return aiCharacter; } - public override void _ExitTree() + public bool CanQueueFree() { - EventBus.AiCharacterGenerateEvent -= OnAiCharacterGenerateEvent; + return true; + } + + public void DoQueueFree() + { + QueueFree(); } } \ No newline at end of file diff --git a/scripts/map/ItemSpawn.cs b/scripts/map/ItemSpawn.cs index 15c6495..a99008d 100644 --- a/scripts/map/ItemSpawn.cs +++ b/scripts/map/ItemSpawn.cs @@ -1,6 +1,6 @@ using ColdMint.scripts.debug; using ColdMint.scripts.inventory; -using ColdMint.scripts.map.events; +using ColdMint.scripts.map.room; using Godot; namespace ColdMint.scripts.map; @@ -9,36 +9,34 @@ namespace ColdMint.scripts.map; /// ItemSpawn /// 物品出生点 /// -public partial class ItemSpawn : Marker2D +public partial class ItemSpawn : Marker2D, ISpawnMarker { - [Export] public string? ItemId { get; private set; } + [Export] private string? ItemId { get; set; } - public override void _Ready() + public Node2D? Spawn() { - base._Ready(); - EventBus.MapGenerationCompleteEvent += MapGenerationCompleteEvent; - } - - private void MapGenerationCompleteEvent(MapGenerationCompleteEvent mapGenerationCompleteEvent) - { - //After the map is generated, create the item instance. - //当地图生成完成后,创建物品实例。 - if (ItemId == null) + if (string.IsNullOrEmpty(ItemId)) { - return; + return null; } var item = ItemTypeManager.CreateItem(ItemId, this); - LogCat.LogWithFormat("generated_item_is_empty",LogCat.LogLabel.ItemSpawn,true,ItemId,item == null); - if (item is Node2D node2D) + LogCat.LogWithFormat("generated_item_is_empty", LogCat.LogLabel.ItemSpawn, true, ItemId, item == null); + if (item is not Node2D node2D) { - node2D.GlobalPosition = GlobalPosition; + return null; } + node2D.GlobalPosition = GlobalPosition; + return node2D; } - public override void _ExitTree() + public bool CanQueueFree() { - base._ExitTree(); - EventBus.MapGenerationCompleteEvent -= MapGenerationCompleteEvent; + return true; + } + + public void DoQueueFree() + { + QueueFree(); } } \ No newline at end of file diff --git a/scripts/map/MapGenerator.cs b/scripts/map/MapGenerator.cs index 116a8bb..89247e9 100644 --- a/scripts/map/MapGenerator.cs +++ b/scripts/map/MapGenerator.cs @@ -311,11 +311,6 @@ public static class MapGenerator RoomDictionary = roomDictionary }; EventBus.MapGenerationCompleteEvent?.Invoke(eventObj); - var aiCharacterGenerateEvent = new AiCharacterGenerateEvent - { - Tag = AiCharacterGenerateEvent.TagMapGenerationComplete - }; - EventBus.AiCharacterGenerateEvent?.Invoke(aiCharacterGenerateEvent); } diff --git a/scripts/map/PlayerSpawn.cs b/scripts/map/PlayerSpawn.cs index beaed5b..cbb76f8 100644 --- a/scripts/map/PlayerSpawn.cs +++ b/scripts/map/PlayerSpawn.cs @@ -2,6 +2,7 @@ using ColdMint.scripts.character; using ColdMint.scripts.debug; using ColdMint.scripts.map.events; +using ColdMint.scripts.map.room; using ColdMint.scripts.utils; using Godot; @@ -11,7 +12,7 @@ namespace ColdMint.scripts.map; /// PlayerSpawn /// 玩家出生点 /// -public partial class PlayerSpawn : Marker2D +public partial class PlayerSpawn : Marker2D,ISpawnMarker { private PackedScene? _playerPackedScene; @@ -31,32 +32,49 @@ public partial class PlayerSpawn : Marker2D GameSceneDepend.Player.GlobalPosition = GlobalPosition; return; } + Spawn(); + } + - SpawnPlayer(); + private void MapGenerationCompleteEvent(MapGenerationCompleteEvent mapGenerationCompleteEvent) + { + //After the map is generated, create the player instance. + //当地图生成完成后,创建玩家实例。 + if (GameSceneDepend.Player != null) + { + //An existing player instance will not be created. + //已经存在玩家实例,不再创建。 + return; + } + + Spawn(); } - /// - /// Generate player instance - /// 生成玩家实例 - /// - private void SpawnPlayer() + public override void _ExitTree() + { + base._ExitTree(); + EventBus.MapGenerationCompleteEvent -= MapGenerationCompleteEvent; + EventBus.GameReplayEvent -= GameReplayEvent; + } + + public Node2D? Spawn() { if (GameSceneDepend.PlayerContainer == null) { - return; + return null; } if (_playerPackedScene == null) { LogCat.LogError("player_packed_scene_not_exist"); - return; + return null; } var playerNode = NodeUtils.InstantiatePackedScene(_playerPackedScene); if (playerNode == null) { - return; + return null; } //The player's parent node must be GameSceneDepend PlayerContainer. @@ -73,26 +91,16 @@ public partial class PlayerSpawn : Marker2D playerNode.ItemContainer = itemContainer; GameSceneDepend.Player = playerNode; playerNode.GlobalPosition = GlobalPosition; + return playerNode; } - private void MapGenerationCompleteEvent(MapGenerationCompleteEvent mapGenerationCompleteEvent) + public bool CanQueueFree() { - //After the map is generated, create the player instance. - //当地图生成完成后,创建玩家实例。 - if (GameSceneDepend.Player != null) - { - //An existing player instance will not be created. - //已经存在玩家实例,不再创建。 - return; - } - - SpawnPlayer(); + return false; } - public override void _ExitTree() + public void DoQueueFree() { - base._ExitTree(); - EventBus.MapGenerationCompleteEvent -= MapGenerationCompleteEvent; - EventBus.GameReplayEvent -= GameReplayEvent; + QueueFree(); } } \ No newline at end of file diff --git a/scripts/map/events/AiCharacterGenerateEvent.cs b/scripts/map/events/AiCharacterGenerateEvent.cs deleted file mode 100644 index 37ad9bd..0000000 --- a/scripts/map/events/AiCharacterGenerateEvent.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ColdMint.scripts.map.events; - -/// -/// AiCharacterGenerateEvent -/// Ai角色生成事件 -/// -public class AiCharacterGenerateEvent -{ - /// - /// Map generation completed Tag - /// 地图生成完成的Tag - /// - public const string TagMapGenerationComplete = "MapGenerationComplete"; - - - /// - /// The Tag used to generate the role - /// 生成角色时使用的Tag - /// - // ReSharper disable UnusedAutoPropertyAccessor.Global - public string? Tag { get; set; } - // ReSharper restore UnusedAutoPropertyAccessor.Global -} \ No newline at end of file diff --git a/scripts/map/room/IEnterRoomEventHandler.cs b/scripts/map/room/IEnterRoomEventHandler.cs index 63e7008..3ec660f 100644 --- a/scripts/map/room/IEnterRoomEventHandler.cs +++ b/scripts/map/room/IEnterRoomEventHandler.cs @@ -19,7 +19,11 @@ public interface IEnterRoomEventHandler /// When entering the room /// 当进入房间时 /// + /// + ///The number of times the player visits the room, 1 is the first visit. + ///玩家访问房间的次数,为1则代表首次访问。 + /// /// /// - void OnEnterRoom(Node node,Room room); + void OnEnterRoom(int playerRoomVisitCount,Node node,Room room); } \ No newline at end of file diff --git a/scripts/map/room/ISpawnMarker.cs b/scripts/map/room/ISpawnMarker.cs new file mode 100644 index 0000000..5950f58 --- /dev/null +++ b/scripts/map/room/ISpawnMarker.cs @@ -0,0 +1,33 @@ +using Godot; + +namespace ColdMint.scripts.map.room; + +/// +/// The tag of the generated entity +/// 生成实体的标记 +/// +public interface ISpawnMarker +{ + /// + /// Generating entity + /// 生成实体 + /// + /// + ///Return the result of the generation. If null is returned, the generation fails. + ///返回生成结果,为null则生成失败。 + /// + Node2D? Spawn(); + + /// + /// Can Queue Free + /// 可释放节点吗 + /// + /// + bool CanQueueFree(); + + /// + /// Execute Queue Free + /// 执行释放节点 + /// + void DoQueueFree(); +} \ No newline at end of file diff --git a/scripts/map/room/Room.cs b/scripts/map/room/Room.cs index a9f97a4..e26d383 100644 --- a/scripts/map/room/Room.cs +++ b/scripts/map/room/Room.cs @@ -28,6 +28,18 @@ public class Room private CollisionShape2D? _collisionShape2D; private bool _hasPlayer; private readonly List _characterTemplateList = []; + + /// + /// When the player first enters the room, all nodes under this node are executed + /// 当玩家首次进入房间时,会执行此节点下所有节点 + /// + private Node2D? _autoSpawn; + + /// + /// The number of times the player visits the room + /// 玩家访问房间的次数 + /// + private int PlayerRoomVisitCount { get; set; } public string? EnterRoomEventHandlerId { get; set; } @@ -51,47 +63,31 @@ public class Room } /// - /// Places a barrier in a slot where a match has been found. - /// 在找到匹配的槽位放置屏障。 + /// Place barriers in all slots + /// 在所有的槽位放置屏障 /// - private void PlaceBarrierInMatchedSlot() + public void PlaceBarriersInAllSlots() { - ProcessRoomSlots(action: (roomSlot, ground, barrier, i) => + var barrier = GetTileMapLayer(Config.TileMapLayerName.Barrier); + if (barrier == null) { - if (!roomSlot.Matched) - { - return; - } - var cellSourceId = barrier.GetCellSourceId(i); - if (cellSourceId == -1) - { - return; - } - - ground.SetCell(i, cellSourceId, barrier.GetCellAtlasCoords(i), barrier.GetCellAlternativeTile(i)); - }); + return; + } + barrier.Enabled = true; } /// - /// Clear the barrier that finds a matching slot - /// 清空找到匹配的槽位的屏障。 + /// Clear all matched barriers + /// 清空所有已匹配位置的屏障 /// - private void ClearBarriersInMatchedSlots() + public void ClearAllMatchedBarriers() { - ProcessRoomSlots(action: (roomSlot, ground, barrier, i) => + var barrier = GetTileMapLayer(Config.TileMapLayerName.Barrier); + if (barrier == null) { - if (!roomSlot.Matched) - { - return; - } - var cellSourceId = barrier.GetCellSourceId(i); - if (cellSourceId == -1) - { - return; - } - - ground.SetCell(i, cellSourceId, barrier.GetCellAtlasCoords(i), barrier.GetCellAlternativeTile(i)); - }); + return; + } + barrier.Enabled = false; } /// @@ -99,32 +95,6 @@ public class Room /// 在未匹配的槽位放置屏障。 /// public void PlaceBarrierInUnmatchedSlots() - { - ProcessRoomSlots(action: (roomSlot, ground, barrier, i) => - { - if (roomSlot.Matched) - { - return; - } - var cellSourceId = barrier.GetCellSourceId(i); - if (cellSourceId == -1) - { - return; - } - - ground.SetCell(i, cellSourceId, barrier.GetCellAtlasCoords(i), barrier.GetCellAlternativeTile(i)); - }); - } - - /// - /// Executes a callback for each room slot, providing the corresponding coordinates in the barrier and ground layers. - /// 对每个房间槽位执行回调,提供barrier层和ground层对应的坐标。 - /// - /// - ///The callback action to be executed, which takes the barrier layer, ground layer, and coordinates as parameters. - ///要执行的回调操作,它以屏障层、底层和坐标作为参数。 - /// - private void ProcessRoomSlots(Action action) { var ground = GetTileMapLayer(Config.TileMapLayerName.Ground); var barrier = GetTileMapLayer(Config.TileMapLayerName.Barrier); @@ -144,17 +114,27 @@ public class Room { continue; } - + //Place the corresponding coordinate tiles of the barrier layer on the ground level. //将屏障层的对应坐标瓦片放到地面层。 CoordinateUtils.ForEachCell(roomSlot.StartPosition, roomSlot.EndPosition, i => { - action.Invoke(roomSlot, ground, barrier, i); + if (roomSlot.Matched) + { + return; + } + var cellSourceId = barrier.GetCellSourceId(i); + if (cellSourceId == -1) + { + return; + } + ground.SetCell(i, cellSourceId, barrier.GetCellAtlasCoords(i), barrier.GetCellAlternativeTile(i)); }); } - barrier.QueueFree(); + barrier.Enabled = false; } + /// /// ShowAllCharacterTemplate /// 显示所有角色模板 @@ -200,6 +180,15 @@ public class Room { _characterTemplateList.Add(player); _hasPlayer = true; + PlayerRoomVisitCount++; + if (PlayerRoomVisitCount == 1 && _autoSpawn != null) + { + NodeUtils.ForEachNode(_autoSpawn, marker => + { + marker.Spawn(); + return false; + }); + } //The player enters the room, opening up their view. //玩家进入了房间,开放视野。 if (_pointLight2D != null) @@ -231,7 +220,7 @@ public class Room } var enterRoomEventHandler = RoomEventManager.GetEnterRoomEventHandler(EnterRoomEventHandlerId); - enterRoomEventHandler?.OnEnterRoom(node, this); + enterRoomEventHandler?.OnEnterRoom(PlayerRoomVisitCount, node, this); } @@ -260,8 +249,18 @@ public class Room _pointLight2D.Show(); _pointLight2D.Texture = AssetHolder.White25; } - HideAllCharacterTemplate(); + if (PlayerRoomVisitCount == 1 && _autoSpawn != null) + { + NodeUtils.ForEachNode(_autoSpawn, marker => + { + if (marker.CanQueueFree()) + { + marker.DoQueueFree(); + } + return false; + }); + } } else if (node is CharacterTemplate characterTemplate && characterTemplate.Visible) { @@ -312,8 +311,8 @@ public class Room return; } - var node2D = NodeUtils.InstantiatePackedScene(packedScene); - if (node2D == null) + var rootNode = NodeUtils.InstantiatePackedScene(packedScene); + if (rootNode == null) { //The room node is not of Node2D type. An exception is thrown //房间节点不是Node2D类型,抛出异常 @@ -321,15 +320,15 @@ public class Room return; } - _rootNode = node2D; - var tileMapNode = node2D.GetNode("TileMap"); + _rootNode = rootNode; + var tileMapNode = rootNode.GetNode("TileMap"); NodeUtils.ForEachNode(tileMapNode, node => { _tileMapLayers ??= []; _tileMapLayers.Add(node); return false; }); - _area2D = node2D.GetNode("RoomArea"); + _area2D = rootNode.GetNode("RoomArea"); _area2D.Monitoring = true; _area2D.SetCollisionLayerValue(Config.LayerNumber.RoomArea, true); //Sets the collision layer that can be detected in the current room area. @@ -340,12 +339,13 @@ public class Room _area2D.BodyEntered += OnEnterRoom; _collisionShape2D = _area2D.GetChild(0); _roomSlots = GetRoomSlots(GetTileMapLayer(Config.TileMapLayerName.Ground), _area2D, - node2D.GetNode("RoomSlotList")); - _pointLight2D = node2D.GetNodeOrNull("PointLight2D"); + rootNode.GetNode("RoomSlotList")); + _pointLight2D = rootNode.GetNodeOrNull("PointLight2D"); if (_pointLight2D != null) { _pointLight2D.BlendMode = Light2D.BlendModeEnum.Mix; } + _autoSpawn = rootNode.GetNodeOrNull("AutoSpawn"); } public Node2D? RootNode => _rootNode; diff --git a/scripts/utils/NodeUtils.cs b/scripts/utils/NodeUtils.cs index 0e65f7a..43b6200 100644 --- a/scripts/utils/NodeUtils.cs +++ b/scripts/utils/NodeUtils.cs @@ -131,17 +131,17 @@ public static class NodeUtils ///用于处理回调的函数,返回true终止遍历节点 /// /// - ///When the type is specified as Node, all child nodes are returned. - ///当指定类型为Node时,将返回所有子节点。 + ///Specifies the generic type + ///指定的泛型 /// - public static void ForEachNode(Node parent, Func func) where T : Node + public static void ForEachNode(Node parent, Func func) where T : class { var count = parent.GetChildCount(); if (count <= 0) { return; } - + for (var i = 0; i < count; i++) { var node = parent.GetChild(i);