Support split-wave Spawn monsters now.

支持分波次刷出怪物了。
This commit is contained in:
Cold-Mint 2024-10-08 22:04:37 +08:00
parent d80c49ab02
commit 53889ef2fd
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
11 changed files with 187 additions and 82 deletions

View File

@ -31,7 +31,7 @@ room_node_data_list:
title: 房间3
description: ''
room_template_set:
- res://prefab/roomTemplates/dungeon/
- res://prefab/roomTemplates/dungeon/utilityRoom.tscn
tags:
room_injection_processor_data: ''
enter_room_event_handler_id:

View File

@ -1,36 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://bdxgx5vcof8em"]
[ext_resource type="Script" path="res://scripts/projectile/Projectile.cs" id="1_ib3qh"]
[ext_resource type="Texture2D" uid="uid://b1twcink38sh0" path="res://sprites/Player.png" id="2_dxg46"]
[sub_resource type="CircleShape2D" id="CircleShape2D_dgro2"]
[sub_resource type="CircleShape2D" id="CircleShape2D_8117d"]
radius = 11.0
[node name="curseOfTheUndead" type="CharacterBody2D"]
collision_layer = 16
collision_mask = 102
slide_on_ceiling = false
platform_floor_layers = 4294967042
platform_wall_layers = 32
script = ExtResource("1_ib3qh")
Speed = 300.0
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_dgro2")
[node name="CollisionDetectionArea" type="Area2D" parent="."]
collision_layer = 16
collision_mask = 78
[node name="CollisionShape2D" type="CollisionShape2D" parent="CollisionDetectionArea"]
shape = SubResource("CircleShape2D_8117d")
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
bus = &"SoundEffect"
area_mask = 16
[node name="Player" type="Sprite2D" parent="."]
scale = Vector2(0.3, 0.3)
texture = ExtResource("2_dxg46")

View File

@ -11,8 +11,8 @@ collision_mask = 0
script = ExtResource("1_ib3qh")
_life = 5000
_durability = 1.0
_maxDamage = 10
_minDamage = 1
_maxDamage = 7
_minDamage = 3
_damageType = 2
Speed = 500.0
_targetDiesDestroyProjectile = true

View File

@ -1,7 +1,8 @@
[gd_scene load_steps=8 format=4 uid="uid://c57cc1tyreybb"]
[gd_scene load_steps=9 format=4 uid="uid://c57cc1tyreybb"]
[ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_rn2om"]
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="2_1ctsj"]
[ext_resource type="Script" path="res://scripts/map/AiCharacterSpawn.cs" id="3_u5h84"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
size = Vector2(728, 509)
@ -72,7 +73,6 @@ tile_map_data = PackedByteArray("AAAAAAAAAQAAAAMAAAAAAAEAAQACAAEAAAAAAAIAAQACAAE
tile_set = ExtResource("1_rn2om")
[node name="Barrier" type="TileMapLayer" parent="TileMap"]
visible = false
use_parent_material = true
tile_map_data = PackedByteArray("AAAWAA0AAQAAAAEAAAAWAA4AAQAAAAEAAAAWAAEAAQAAAAEAAAAWAAIAAQAAAAEAAAAAAA0AAQACAAEAAAAAAA4AAQACAAEAAAA=")
tile_set = ExtResource("1_rn2om")
@ -82,3 +82,15 @@ visible = false
position = Vector2(367.5, 258)
scale = Vector2(23.0938, 16.25)
texture = ExtResource("2_1ctsj")
[node name="AutoSpawn" type="Node2D" parent="."]
[node name="Marker2D" type="Marker2D" parent="AutoSpawn"]
position = Vector2(121, 178)
script = ExtResource("3_u5h84")
_resPathArray = PackedStringArray("res://prefab/entitys/BlackenedAboriginalWarrior.tscn", "res://prefab/entitys/BlackenedAboriginalWarrior.tscn", "", "res://prefab/entitys/BlackenedAboriginalWarrior.tscn")
[node name="Marker2D2" type="Marker2D" parent="AutoSpawn"]
position = Vector2(545, 438)
script = ExtResource("3_u5h84")
_resPathArray = PackedStringArray("res://prefab/entitys/BlackenedAboriginalWarrior.tscn", "", "res://prefab/entitys/BlackenedAboriginalWarrior.tscn", "")

View File

@ -9,7 +9,7 @@
[ext_resource type="PackedScene" uid="uid://dld3qttpsdjpe" path="res://prefab/furnitures/WoodenBox.tscn" id="7_jybe6"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
size = Vector2(709, 296)
size = Vector2(758, 342)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_jxmys"]
size = Vector2(23, 54.875)
@ -25,7 +25,7 @@ source_geometry_group_name = &"navigation_polygon_source_group"
[node name="RoomArea" type="Area2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="RoomArea"]
position = Vector2(383.5, 176)
position = Vector2(383, 175)
shape = SubResource("RectangleShape2D_kiih8")
[node name="RoomSlotList" type="Node2D" parent="."]
@ -111,9 +111,9 @@ position = Vector2(714, 122)
[node name="necromancy" type="Marker2D" parent="AutoSpawn"]
position = Vector2(134, 248)
script = ExtResource("4_6ihp7")
ItemId = "necromancy"
_itemIdList = PackedStringArray("necromancy")
[node name="staff_necromancy" type="Marker2D" parent="AutoSpawn"]
position = Vector2(100, 250)
script = ExtResource("4_6ihp7")
ItemId = "staff_necromancy"
_itemIdList = PackedStringArray("staff_necromancy")

View File

@ -8,7 +8,7 @@
[ext_resource type="Texture2D" uid="uid://kgodvs3ilxbs" path="res://sprites/tutorials/tripleShotSpell.png" id="6_2qcf3"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
size = Vector2(709, 296)
size = Vector2(755, 340)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_jxmys"]
size = Vector2(23, 54.875)
@ -130,4 +130,4 @@ texture = ExtResource("6_2qcf3")
[node name="x3" type="Marker2D" parent="AutoSpawn"]
position = Vector2(434, 105)
script = ExtResource("4_fh50l")
ItemId = "x3"
_itemIdList = PackedStringArray("x3")

View File

@ -11,26 +11,33 @@ namespace ColdMint.scripts.map;
/// </summary>
public partial class AiCharacterSpawn : Marker2D, ISpawnMarker
{
private PackedScene? _packedScene;
[Export] private string? _resPath;
[Export] private string[]? _resPathArray;
public override void _Ready()
public Node2D? Spawn(int waveNumber)
{
base._Ready();
if (!string.IsNullOrEmpty(_resPath))
{
_packedScene = GD.Load<PackedScene>(_resPath);
}
}
public Node2D? Spawn()
{
if (GameSceneDepend.AiCharacterContainer == null || _packedScene == null)
if (GameSceneDepend.AiCharacterContainer == null)
{
return null;
}
var aiCharacter = NodeUtils.InstantiatePackedScene<AiCharacter>(_packedScene);
if (_resPathArray == null)
{
return null;
}
if (waveNumber < 0 || waveNumber >= _resPathArray.Length)
{
return null;
}
var resPath = _resPathArray[waveNumber];
if (string.IsNullOrEmpty(resPath))
{
return null;
}
var packedScene = GD.Load<PackedScene>(resPath);
if (packedScene == null)
{
return null;
}
var aiCharacter = NodeUtils.InstantiatePackedScene<AiCharacter>(packedScene);
if (aiCharacter == null)
{
return null;
@ -41,6 +48,11 @@ public partial class AiCharacterSpawn : Marker2D, ISpawnMarker
return aiCharacter;
}
public int GetMaxWaveNumber()
{
return _resPathArray?.Length ?? 0;
}
public bool CanQueueFree()
{
return true;

View File

@ -11,17 +11,26 @@ namespace ColdMint.scripts.map;
/// </summary>
public partial class ItemSpawn : Marker2D, ISpawnMarker
{
[Export] private string? ItemId { get; set; }
[Export] private string[]? _itemIdList;
public Node2D? Spawn()
public Node2D? Spawn(int waveNumber)
{
if (string.IsNullOrEmpty(ItemId))
if (_itemIdList == null)
{
return null;
}
var item = ItemTypeManager.CreateItem(ItemId, this);
LogCat.LogWithFormat("generated_item_is_empty", LogCat.LogLabel.ItemSpawn, true, ItemId, item == null);
if (waveNumber < 0 || waveNumber >= _itemIdList.Length)
{
return null;
}
var itemId = _itemIdList[waveNumber];
if (string.IsNullOrEmpty(itemId))
{
return null;
}
var item = ItemTypeManager.CreateItem(itemId, this);
LogCat.LogWithFormat("generated_item_is_empty", LogCat.LogLabel.ItemSpawn, true, itemId, item == null);
if (item is not Node2D node2D)
{
return null;
@ -30,6 +39,11 @@ public partial class ItemSpawn : Marker2D, ISpawnMarker
return node2D;
}
public int GetMaxWaveNumber()
{
return _itemIdList?.Length ?? 0;
}
public bool CanQueueFree()
{
return true;

View File

@ -15,6 +15,11 @@ namespace ColdMint.scripts.map;
public partial class PlayerSpawn : Marker2D, ISpawnMarker
{
private PackedScene? _playerPackedScene;
/// <summary>
/// <para>The player's generated wave count</para>
/// <para>玩家的生成波数</para>
/// </summary>
private static readonly int PlayerWaveNumber = 1;
public override void _Ready()
{
@ -32,7 +37,7 @@ public partial class PlayerSpawn : Marker2D,ISpawnMarker
GameSceneDepend.Player.GlobalPosition = GlobalPosition;
return;
}
Spawn();
Spawn(PlayerWaveNumber);
}
@ -47,7 +52,7 @@ public partial class PlayerSpawn : Marker2D,ISpawnMarker
return;
}
Spawn();
Spawn(PlayerWaveNumber);
}
public override void _ExitTree()
@ -57,8 +62,13 @@ public partial class PlayerSpawn : Marker2D,ISpawnMarker
EventBus.GameReplayEvent -= GameReplayEvent;
}
public Node2D? Spawn()
public Node2D? Spawn(int waveNumber)
{
if (waveNumber != PlayerWaveNumber)
{
return null;
}
if (GameSceneDepend.PlayerContainer == null)
{
return null;
@ -94,6 +104,11 @@ public partial class PlayerSpawn : Marker2D,ISpawnMarker
return playerNode;
}
public int GetMaxWaveNumber()
{
return PlayerWaveNumber;
}
public bool CanQueueFree()
{
return false;

View File

@ -12,11 +12,22 @@ public interface ISpawnMarker
/// <para>Generating entity</para>
/// <para>生成实体</para>
/// </summary>
/// <param name="waveNumber">
///<para>Spawning waves</para>
///<para>刷怪的波次</para>
/// </param>
/// <remarks>
///<para>Return the result of the generation. If null is returned, the generation fails.</para>
///<para>返回生成结果为null则生成失败。</para>
/// </remarks>
Node2D? Spawn();
Node2D? Spawn(int waveNumber);
/// <summary>
/// <para>GetMaxWaveNumber</para>
/// <para>获取生成器支持的最大生成波数</para>
/// </summary>
/// <returns></returns>
int GetMaxWaveNumber();
/// <summary>
/// <para>Can Queue Free</para>

View File

@ -35,6 +35,20 @@ public class Room
/// </summary>
private Node2D? _autoSpawn;
/// <summary>
/// <para>Current generated wave number</para>
/// <para>当前生成波数</para>
/// </summary>
private int _currentWaveNumber;
private readonly List<CharacterTemplate> _spawnedCharacterTemplateList = [];
/// <summary>
/// <para>Max generated wave number</para>
/// <para>最大的生成波数</para>
/// </summary>
private int _maxWaveNumber;
/// <summary>
/// <para>The number of times the player visits the room</para>
/// <para>玩家访问房间的次数</para>
@ -162,6 +176,76 @@ public class Room
characterTemplate.Hide();
}
}
/// <summary>
/// <para>Spawn a wave of entities</para>
/// <para>生成一波实体</para>
/// </summary>
private void SpawnEnemyWave()
{
if (PlayerRoomVisitCount != 1 || _autoSpawn == null)
{
return;
}
if (_maxWaveNumber > 0 && _currentWaveNumber == _maxWaveNumber)
{
//Complete all waves.
//完成所有的波次。
ClearAllMatchedBarriers();
GameSceneDepend.MiniMap?.Show();
return;
}
NodeUtils.ForEachNode<ISpawnMarker>(_autoSpawn, marker =>
{
var node2D = marker.Spawn(_currentWaveNumber);
if (node2D is CharacterTemplate characterTemplate)
{
//The maximum wave number should be the maximum wave number produced by living organisms.For now, the player's condition for the next step is to kill all enemies.
//最大波数应该是生物生成的最大波数。就目前而言,玩家进入下一步的条件是杀死所有敌人。
_maxWaveNumber = Math.Max(_maxWaveNumber, marker.GetMaxWaveNumber());
_spawnedCharacterTemplateList.Add(characterTemplate);
characterTemplate.TreeExited += () =>
{
_spawnedCharacterTemplateList.Remove(characterTemplate);
if (_spawnedCharacterTemplateList.Count == 0)
{
//All the creatures they summoned are dead.
//召唤的生物全死了。
_currentWaveNumber++;
AddTimer(SpawnEnemyWave);
}
};
}
return false;
});
if (_spawnedCharacterTemplateList.Count > 0)
{
GameSceneDepend.MiniMap?.Hide();
AddTimer(PlaceBarriersInAllSlots);
}
}
/// <summary>
/// <para>Add a timer node to handle some events</para>
/// <para>添加定时器节点处理一些事件</para>
/// </summary>
/// <param name="timeoutAction"></param>
private void AddTimer(Action timeoutAction)
{
if (_rootNode == null)
{
return;
}
var timer = new Timer();
timer.Autostart = true;
timer.OneShot = true;
timer.WaitTime = 0.3f;
timer.Timeout += () =>
{
timeoutAction.Invoke();
timer.QueueFree();
};
NodeUtils.CallDeferredAddChild(_rootNode, timer);
}
/// <summary>
/// <para>When a node enters the room</para>
@ -181,14 +265,7 @@ public class Room
_characterTemplateList.Add(player);
_hasPlayer = true;
PlayerRoomVisitCount++;
if (PlayerRoomVisitCount == 1 && _autoSpawn != null)
{
NodeUtils.ForEachNode<ISpawnMarker>(_autoSpawn, marker =>
{
marker.Spawn();
return false;
});
}
SpawnEnemyWave();
//The player enters the room, opening up their view.
//玩家进入了房间,开放视野。
if (_pointLight2D != null)