Adjust the time of spawns of items and enemies in the map to when the player first enters the room.

将地图中物品,敌人生成的时机调整到玩家首次进入房间时。
This commit is contained in:
Cold-Mint 2024-10-08 16:11:51 +08:00
parent 5371d31d99
commit d80c49ab02
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
14 changed files with 200 additions and 196 deletions

View File

@ -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")

View File

@ -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")

View File

@ -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"

View File

@ -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"

View File

@ -9,12 +9,6 @@ namespace ColdMint.scripts;
/// </summary>
public static class EventBus
{
/// <summary>
/// <para>Event when the AI character is generated</para>
/// <para>AI角色生成事件</para>
/// </summary>
public static Action<AiCharacterGenerateEvent>? AiCharacterGenerateEvent;
/// <summary>
/// <para>Game Over Event</para>
/// <para>游戏结束事件</para>

View File

@ -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;
/// <para>Ai character generation point</para>
/// <para>Ai角色生成点</para>
/// </summary>
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<PackedScene>(ResPath);
_packedScene = GD.Load<PackedScene>(_resPath);
}
EventBus.AiCharacterGenerateEvent += OnAiCharacterGenerateEvent;
}
/// <summary>
/// <para>When an event is triggered</para>
/// <para>当触发事件时</para>
/// </summary>
/// <param name="aiCharacterGenerateEvent"></param>
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<AiCharacter>(_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();
}
}

View File

@ -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;
/// <para>ItemSpawn</para>
/// <para>物品出生点</para>
/// </summary>
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();
}
}

View File

@ -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);
}

View File

@ -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;
/// <para>PlayerSpawn</para>
/// <para>玩家出生点</para>
/// </summary>
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;
}
SpawnPlayer();
Spawn();
}
/// <summary>
/// <para>Generate player instance</para>
/// <para>生成玩家实例</para>
/// </summary>
private void 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();
}
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<Player>(_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();
}
}

View File

@ -1,23 +0,0 @@
namespace ColdMint.scripts.map.events;
/// <summary>
/// <para>AiCharacterGenerateEvent</para>
/// <para>Ai角色生成事件</para>
/// </summary>
public class AiCharacterGenerateEvent
{
/// <summary>
/// <para>Map generation completed Tag</para>
/// <para>地图生成完成的Tag</para>
/// </summary>
public const string TagMapGenerationComplete = "MapGenerationComplete";
/// <summary>
/// <para>The Tag used to generate the role</para>
/// <para>生成角色时使用的Tag</para>
/// </summary>
// ReSharper disable UnusedAutoPropertyAccessor.Global
public string? Tag { get; set; }
// ReSharper restore UnusedAutoPropertyAccessor.Global
}

View File

@ -19,7 +19,11 @@ public interface IEnterRoomEventHandler
/// <para>When entering the room</para>
/// <para>当进入房间时</para>
/// </summary>
/// <param name="playerRoomVisitCount">
///<para>The number of times the player visits the room, 1 is the first visit.</para>
///<para>玩家访问房间的次数为1则代表首次访问。</para>
/// </param>
/// <param name="node"></param>
/// <param name="room"></param>
void OnEnterRoom(Node node,Room room);
void OnEnterRoom(int playerRoomVisitCount,Node node,Room room);
}

View File

@ -0,0 +1,33 @@
using Godot;
namespace ColdMint.scripts.map.room;
/// <summary>
/// <para>The tag of the generated entity</para>
/// <para>生成实体的标记</para>
/// </summary>
public interface ISpawnMarker
{
/// <summary>
/// <para>Generating entity</para>
/// <para>生成实体</para>
/// </summary>
/// <remarks>
///<para>Return the result of the generation. If null is returned, the generation fails.</para>
///<para>返回生成结果为null则生成失败。</para>
/// </remarks>
Node2D? Spawn();
/// <summary>
/// <para>Can Queue Free</para>
/// <para>可释放节点吗</para>
/// </summary>
/// <returns></returns>
bool CanQueueFree();
/// <summary>
/// <para>Execute Queue Free</para>
/// <para>执行释放节点</para>
/// </summary>
void DoQueueFree();
}

View File

@ -29,6 +29,18 @@ public class Room
private bool _hasPlayer;
private readonly List<CharacterTemplate> _characterTemplateList = [];
/// <summary>
/// <para>When the player first enters the room, all <see cref="ISpawnMarker"/> nodes under this node are executed</para>
/// <para>当玩家首次进入房间时,会执行此节点下所有<see cref="ISpawnMarker"/>节点</para>
/// </summary>
private Node2D? _autoSpawn;
/// <summary>
/// <para>The number of times the player visits the room</para>
/// <para>玩家访问房间的次数</para>
/// </summary>
private int PlayerRoomVisitCount { get; set; }
public string? EnterRoomEventHandlerId { get; set; }
public string? ExitRoomEventHandlerId { get; set; }
@ -51,47 +63,31 @@ public class Room
}
/// <summary>
/// <para>Places a barrier in a slot where a match has been found.</para>
/// <para>在找到匹配的槽位放置屏障。</para>
/// <para>Place barriers in all slots</para>
/// <para>在所有的槽位放置屏障</para>
/// </summary>
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;
}
/// <summary>
/// <para>Clear the barrier that finds a matching slot</para>
/// <para>清空找到匹配的槽位的屏障。</para>
/// <para>Clear all matched barriers</para>
/// <para>清空所有已匹配位置的屏障</para>
/// </summary>
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;
}
/// <summary>
@ -99,32 +95,6 @@ public class Room
/// <para>在未匹配的槽位放置屏障。</para>
/// </summary>
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));
});
}
/// <summary>
/// <para>Executes a callback for each room slot, providing the corresponding coordinates in the barrier and ground layers.</para>
/// <para>对每个房间槽位执行回调提供barrier层和ground层对应的坐标。</para>
/// </summary>
/// <param name="action">
///<para>The callback action to be executed, which takes the barrier layer, ground layer, and coordinates as parameters.</para>
///<para>要执行的回调操作,它以屏障层、底层和坐标作为参数。</para>
/// </param>
private void ProcessRoomSlots(Action<RoomSlot, TileMapLayer, TileMapLayer, Vector2I> action)
{
var ground = GetTileMapLayer(Config.TileMapLayerName.Ground);
var barrier = GetTileMapLayer(Config.TileMapLayerName.Barrier);
@ -149,12 +119,22 @@ public class Room
//将屏障层的对应坐标瓦片放到地面层。
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;
}
/// <summary>
/// <para>ShowAllCharacterTemplate</para>
/// <para>显示所有角色模板</para>
@ -200,6 +180,15 @@ public class Room
{
_characterTemplateList.Add(player);
_hasPlayer = true;
PlayerRoomVisitCount++;
if (PlayerRoomVisitCount == 1 && _autoSpawn != null)
{
NodeUtils.ForEachNode<ISpawnMarker>(_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<ISpawnMarker>(_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<Node2D>(packedScene);
if (node2D == null)
var rootNode = NodeUtils.InstantiatePackedScene<Node2D>(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<Node2D>("TileMap");
_rootNode = rootNode;
var tileMapNode = rootNode.GetNode<Node2D>("TileMap");
NodeUtils.ForEachNode<TileMapLayer>(tileMapNode, node =>
{
_tileMapLayers ??= [];
_tileMapLayers.Add(node);
return false;
});
_area2D = node2D.GetNode<Area2D>("RoomArea");
_area2D = rootNode.GetNode<Area2D>("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<CollisionShape2D>(0);
_roomSlots = GetRoomSlots(GetTileMapLayer(Config.TileMapLayerName.Ground), _area2D,
node2D.GetNode<Node2D>("RoomSlotList"));
_pointLight2D = node2D.GetNodeOrNull<PointLight2D>("PointLight2D");
rootNode.GetNode<Node2D>("RoomSlotList"));
_pointLight2D = rootNode.GetNodeOrNull<PointLight2D>("PointLight2D");
if (_pointLight2D != null)
{
_pointLight2D.BlendMode = Light2D.BlendModeEnum.Mix;
}
_autoSpawn = rootNode.GetNodeOrNull<Node2D>("AutoSpawn");
}
public Node2D? RootNode => _rootNode;

View File

@ -131,10 +131,10 @@ public static class NodeUtils
///<para>用于处理回调的函数返回true终止遍历节点</para>
/// </param>
/// <typeparam name="T">
///<para>When the type is specified as Node, all child nodes are returned.</para>
///<para>当指定类型为Node时将返回所有子节点。</para>
///<para>Specifies the generic type</para>
///<para>指定的泛型</para>
/// </typeparam>
public static void ForEachNode<T>(Node parent, Func<T, bool> func) where T : Node
public static void ForEachNode<T>(Node parent, Func<T, bool> func) where T : class
{
var count = parent.GetChildCount();
if (count <= 0)