2024-04-28 13:55:19 +00:00
using System ;
using System.Collections.Generic ;
2024-07-26 14:38:18 +00:00
using System.Linq ;
2024-08-25 10:49:48 +00:00
using ColdMint.scripts.character ;
2024-04-28 13:55:19 +00:00
using ColdMint.scripts.debug ;
using ColdMint.scripts.map.dateBean ;
using ColdMint.scripts.utils ;
using Godot ;
namespace ColdMint.scripts.map.room ;
/// <summary>
/// <para>Room</para>
/// <para>房间模板</para>
/// </summary>
/// <remarks>
///<para>The room template is like a jigsaw puzzle and participates in the map building process.</para>
///<para>房间模板就像一个拼图,参与到地图的构建过程中。</para>
/// </remarks>
2024-05-20 14:38:41 +00:00
public class Room
2024-04-28 13:55:19 +00:00
{
2024-05-08 10:22:04 +00:00
private Node2D ? _rootNode ;
private RoomSlot ? [ ] ? _roomSlots ;
2024-07-26 14:38:18 +00:00
private List < TileMapLayer > ? _tileMapLayers ;
2024-10-11 06:52:39 +00:00
private Area2D ? _roomArea2D ;
private Area2D ? _spawnArea2D ;
2024-08-24 14:49:59 +00:00
private PointLight2D ? _pointLight2D ;
2024-05-25 16:02:00 +00:00
private CollisionShape2D ? _collisionShape2D ;
2024-08-25 10:49:48 +00:00
private bool _hasPlayer ;
2024-10-11 06:52:39 +00:00
/// <summary>
/// <para>All the characters in the room</para>
/// <para>房间内的所有角色</para>
/// </summary>
private readonly List < CharacterTemplate > _allCharacter = [ ] ;
/// <summary>
/// <para>A character automatically generated by the room</para>
/// <para>由房间自动生成的角色</para>
/// </summary>
private readonly List < CharacterTemplate > _spawnedCharacter = [ ] ;
2024-10-08 14:04:37 +00:00
2024-10-08 08:11:51 +00:00
/// <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 ;
2024-10-11 06:52:39 +00:00
/// <summary>
/// <para>hasSpawnedEntity</para>
/// <para>是否生成过实体</para>
/// </summary>
private bool _hasSpawnedEntity ;
2024-10-08 14:04:37 +00:00
/// <summary>
/// <para>Current generated wave number</para>
/// <para>当前生成波数</para>
/// </summary>
private int _currentWaveNumber ;
/// <summary>
/// <para>Max generated wave number</para>
/// <para>最大的生成波数</para>
/// </summary>
private int _maxWaveNumber ;
2024-10-08 08:11:51 +00:00
/// <summary>
/// <para>The number of times the player visits the room</para>
/// <para>玩家访问房间的次数</para>
/// </summary>
private int PlayerRoomVisitCount { get ; set ; }
2024-05-25 16:02:00 +00:00
2024-06-02 13:48:38 +00:00
public string? EnterRoomEventHandlerId { get ; set ; }
public string? ExitRoomEventHandlerId { get ; set ; }
2024-07-26 14:38:18 +00:00
/// <summary>
/// <para>Get the corresponding tile layer node based on the tile name</para>
/// <para>根据瓦片名称获取对应的瓦片图层节点</para>
/// </summary>
/// <param name="layerName">
///<para>We recommend using the constants defined in <see cref="Config.TileMapLayerName"/>.</para>
///<para>建议使用<see cref="Config.TileMapLayerName"/>内定义的常量。</para>
/// </param>
/// <returns>
/// <para>Return a Layer node with the same name if it is found, otherwise null.</para>
///<para>如果找到了与其名称相同的Layer节点则返回它, 否则返回null。</para>
/// </returns>
public TileMapLayer ? GetTileMapLayer ( string layerName )
{
return _tileMapLayers ? . FirstOrDefault ( tileMapLayer = > tileMapLayer . Name = = layerName ) ;
}
2024-10-08 02:52:42 +00:00
/// <summary>
2024-10-08 08:11:51 +00:00
/// <para>Place barriers in all slots</para>
/// <para>在所有的槽位放置屏障</para>
2024-10-08 02:52:42 +00:00
/// </summary>
2024-10-08 08:11:51 +00:00
public void PlaceBarriersInAllSlots ( )
2024-10-08 02:52:42 +00:00
{
2024-10-08 08:11:51 +00:00
var barrier = GetTileMapLayer ( Config . TileMapLayerName . Barrier ) ;
if ( barrier = = null )
2024-10-08 02:52:42 +00:00
{
2024-10-08 08:11:51 +00:00
return ;
}
barrier . Enabled = true ;
2024-10-08 02:52:42 +00:00
}
/// <summary>
2024-10-08 08:11:51 +00:00
/// <para>Clear all matched barriers</para>
/// <para>清空所有已匹配位置的屏障</para>
2024-10-08 02:52:42 +00:00
/// </summary>
2024-10-08 08:11:51 +00:00
public void ClearAllMatchedBarriers ( )
2024-10-08 02:52:42 +00:00
{
2024-10-08 08:11:51 +00:00
var barrier = GetTileMapLayer ( Config . TileMapLayerName . Barrier ) ;
if ( barrier = = null )
2024-10-08 02:52:42 +00:00
{
2024-10-08 08:11:51 +00:00
return ;
}
barrier . Enabled = false ;
2024-10-08 02:52:42 +00:00
}
/// <summary>
/// <para>Places barriers in slots that do not have a match.</para>
/// <para>在未匹配的槽位放置屏障。</para>
/// </summary>
public void PlaceBarrierInUnmatchedSlots ( )
{
var ground = GetTileMapLayer ( Config . TileMapLayerName . Ground ) ;
var barrier = GetTileMapLayer ( Config . TileMapLayerName . Barrier ) ;
if ( ground = = null | | barrier = = null )
{
return ;
}
if ( RoomSlots = = null | | RoomSlots . Length = = 0 )
{
return ;
}
foreach ( var roomSlot in RoomSlots )
{
if ( roomSlot = = null )
{
continue ;
}
2024-10-08 08:11:51 +00:00
2024-10-08 02:52:42 +00:00
//Place the corresponding coordinate tiles of the barrier layer on the ground level.
//将屏障层的对应坐标瓦片放到地面层。
CoordinateUtils . ForEachCell ( roomSlot . StartPosition , roomSlot . EndPosition , i = >
{
2024-10-08 08:11:51 +00:00
if ( roomSlot . Matched )
{
return ;
}
var cellSourceId = barrier . GetCellSourceId ( i ) ;
if ( cellSourceId = = - 1 )
{
return ;
}
ground . SetCell ( i , cellSourceId , barrier . GetCellAtlasCoords ( i ) , barrier . GetCellAlternativeTile ( i ) ) ;
2024-10-08 02:52:42 +00:00
} ) ;
}
2024-10-08 08:11:51 +00:00
barrier . Enabled = false ;
2024-10-08 02:52:42 +00:00
}
2024-10-08 08:11:51 +00:00
2024-08-25 10:49:48 +00:00
/// <summary>
/// <para>ShowAllCharacterTemplate</para>
/// <para>显示所有角色模板</para>
/// </summary>
private void ShowAllCharacterTemplate ( )
{
LogCat . LogWithFormat ( "show_all_node" , LogCat . LogLabel . Room , LogCat . UploadFormat ,
2024-10-11 06:52:39 +00:00
_allCharacter . Count ) ;
foreach ( var characterTemplate in _allCharacter )
2024-08-25 10:49:48 +00:00
{
characterTemplate . Show ( ) ;
}
}
/// <summary>
/// <para>HideAllCharacterTemplate</para>
/// <para>隐藏所有角色模板</para>
/// </summary>
private void HideAllCharacterTemplate ( )
{
LogCat . LogWithFormat ( "hide_all_node" , LogCat . LogLabel . Room , LogCat . UploadFormat ,
2024-10-11 06:52:39 +00:00
_allCharacter . Count ) ;
foreach ( var characterTemplate in _allCharacter )
2024-08-25 10:49:48 +00:00
{
characterTemplate . Hide ( ) ;
}
}
2024-10-08 14:04:37 +00:00
/// <summary>
/// <para>Spawn a wave of entities</para>
/// <para>生成一波实体</para>
/// </summary>
private void SpawnEnemyWave ( )
{
2024-10-11 06:52:39 +00:00
if ( _autoSpawn = = null )
2024-10-08 14:04:37 +00:00
{
return ;
}
if ( _maxWaveNumber > 0 & & _currentWaveNumber = = _maxWaveNumber )
{
//Complete all waves.
//完成所有的波次。
ClearAllMatchedBarriers ( ) ;
2024-10-11 06:52:39 +00:00
GameSceneDepend . ShowMiniMap ( ) ;
2024-10-08 14:04:37 +00:00
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 ( ) ) ;
2024-10-11 06:52:39 +00:00
_spawnedCharacter . Add ( characterTemplate ) ;
2024-10-08 14:04:37 +00:00
characterTemplate . TreeExited + = ( ) = >
{
2024-10-11 06:52:39 +00:00
_spawnedCharacter . Remove ( characterTemplate ) ;
if ( _spawnedCharacter . Count = = 0 )
2024-10-08 14:04:37 +00:00
{
//All the creatures they summoned are dead.
//召唤的生物全死了。
_currentWaveNumber + + ;
AddTimer ( SpawnEnemyWave ) ;
}
} ;
}
return false ;
} ) ;
2024-10-11 06:52:39 +00:00
if ( _spawnedCharacter . Count > 0 )
2024-10-08 14:04:37 +00:00
{
2024-10-11 06:52:39 +00:00
GameSceneDepend . HideMiniMap ( ) ;
2024-10-08 14:04:37 +00:00
AddTimer ( PlaceBarriersInAllSlots ) ;
}
}
2024-10-11 06:52:39 +00:00
2024-10-08 14:04:37 +00:00
/// <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 ) ;
}
2024-08-25 10:49:48 +00:00
2024-06-02 13:48:38 +00:00
/// <summary>
/// <para>When a node enters the room</para>
/// <para>当有节点进入房间时</para>
/// </summary>
/// <param name="node"></param>
private void OnEnterRoom ( Node node )
{
if ( _rootNode ! = null )
{
2024-07-26 14:38:18 +00:00
LogCat . LogWithFormat ( "enter_the_room_debug" , LogCat . LogLabel . Default , LogCat . UploadFormat , node . Name ,
_rootNode . Name ) ;
2024-06-02 13:48:38 +00:00
}
2024-08-25 10:49:48 +00:00
if ( node is Player player )
2024-08-24 14:49:59 +00:00
{
2024-10-11 06:52:39 +00:00
_allCharacter . Add ( player ) ;
2024-08-25 10:49:48 +00:00
_hasPlayer = true ;
2024-10-08 08:11:51 +00:00
PlayerRoomVisitCount + + ;
2024-08-24 14:49:59 +00:00
//The player enters the room, opening up their view.
//玩家进入了房间,开放视野。
2024-08-25 10:49:48 +00:00
if ( _pointLight2D ! = null )
{
_pointLight2D . Show ( ) ;
_pointLight2D . Texture = AssetHolder . White100 ;
}
2024-10-11 06:52:39 +00:00
GameSceneDepend . ShowMiniMap ( ) ;
2024-09-08 01:45:14 +00:00
GameSceneDepend . MiniMap ? . ShowRoomPreview ( this ) ;
2024-08-25 10:49:48 +00:00
ShowAllCharacterTemplate ( ) ;
2024-09-08 01:45:14 +00:00
}
else if ( node is CharacterTemplate characterTemplate )
2024-08-25 10:49:48 +00:00
{
if ( _hasPlayer )
{
characterTemplate . Show ( ) ;
}
else
{
characterTemplate . Hide ( ) ;
}
2024-10-11 06:52:39 +00:00
_allCharacter . Add ( characterTemplate ) ;
2024-08-24 14:49:59 +00:00
}
2024-06-02 13:48:38 +00:00
if ( string . IsNullOrEmpty ( EnterRoomEventHandlerId ) )
{
return ;
}
var enterRoomEventHandler = RoomEventManager . GetEnterRoomEventHandler ( EnterRoomEventHandlerId ) ;
2024-10-08 08:11:51 +00:00
enterRoomEventHandler ? . OnEnterRoom ( PlayerRoomVisitCount , node , this ) ;
2024-06-02 13:48:38 +00:00
}
2024-08-24 14:49:59 +00:00
2024-06-02 13:48:38 +00:00
/// <summary>
/// <para>When a node exits the room</para>
/// <para>当有节点退出房间时</para>
/// </summary>
/// <param name="node"></param>
private void OnExitRoom ( Node node )
{
if ( _rootNode ! = null )
{
2024-07-26 14:38:18 +00:00
LogCat . LogWithFormat ( "exit_the_room_debug" , LogCat . LogLabel . Default , LogCat . UploadFormat , node . Name ,
_rootNode . Name ) ;
2024-06-02 13:48:38 +00:00
}
2024-08-25 10:49:48 +00:00
if ( node is Player player )
2024-08-24 14:49:59 +00:00
{
2024-08-25 10:49:48 +00:00
//The player leaves the room, hiding the view.
//玩家离开了房间,隐藏视野。
2024-10-11 06:52:39 +00:00
_allCharacter . Remove ( player ) ;
2024-08-25 10:49:48 +00:00
_hasPlayer = false ;
if ( _pointLight2D ! = null )
{
_pointLight2D . Show ( ) ;
_pointLight2D . Texture = AssetHolder . White25 ;
}
HideAllCharacterTemplate ( ) ;
2024-10-08 08:11:51 +00:00
if ( PlayerRoomVisitCount = = 1 & & _autoSpawn ! = null )
{
NodeUtils . ForEachNode < ISpawnMarker > ( _autoSpawn , marker = >
{
if ( marker . CanQueueFree ( ) )
{
marker . DoQueueFree ( ) ;
}
return false ;
} ) ;
}
2024-08-25 10:49:48 +00:00
}
else if ( node is CharacterTemplate characterTemplate & & characterTemplate . Visible )
{
2024-10-11 06:52:39 +00:00
_allCharacter . Remove ( characterTemplate ) ;
2024-08-24 14:49:59 +00:00
}
2024-06-02 13:48:38 +00:00
if ( string . IsNullOrEmpty ( ExitRoomEventHandlerId ) )
{
return ;
}
var exitRoomEventHandler = RoomEventManager . GetExitRoomEventHandler ( ExitRoomEventHandlerId ) ;
exitRoomEventHandler ? . OnExitRoom ( node , this ) ;
}
2024-05-25 16:02:00 +00:00
/// <summary>
/// <para>The collision shape of the room</para>
/// <para>房间的碰撞形状</para>
/// </summary>
public CollisionShape2D ? RoomCollisionShape2D
{
get = > _collisionShape2D ;
set = > _collisionShape2D = value ;
}
2024-06-02 13:48:38 +00:00
2024-10-11 06:52:39 +00:00
public Area2D ? RoomArea2D
2024-05-25 16:02:00 +00:00
{
2024-10-11 06:52:39 +00:00
get = > _roomArea2D ;
set = > _roomArea2D = value ;
2024-05-25 16:02:00 +00:00
}
2024-04-28 13:55:19 +00:00
2024-05-08 10:22:04 +00:00
public PackedScene ? RoomScene
2024-04-28 13:55:19 +00:00
{
get = > default ;
set = > AnalyzeRoomData ( value ) ;
}
/// <summary>
/// <para>Analyze the data of the room</para>
/// <para>解析房间的数据</para>
/// </summary>
/// <param name="packedScene"></param>
2024-05-08 10:22:04 +00:00
private void AnalyzeRoomData ( PackedScene ? packedScene )
2024-04-28 13:55:19 +00:00
{
2024-06-10 13:05:18 +00:00
if ( packedScene = = null )
2024-04-28 13:55:19 +00:00
{
2024-06-10 13:05:18 +00:00
return ;
}
2024-07-02 15:16:04 +00:00
2024-10-08 08:11:51 +00:00
var rootNode = NodeUtils . InstantiatePackedScene < Node2D > ( packedScene ) ;
if ( rootNode = = null )
2024-06-10 13:05:18 +00:00
{
//The room node is not of Node2D type. An exception is thrown
2024-04-28 13:55:19 +00:00
//房间节点不是Node2D类型, 抛出异常
LogCat . LogError ( "room_root_node_must_be_node2d" ) ;
return ;
}
2024-10-08 08:11:51 +00:00
_rootNode = rootNode ;
var tileMapNode = rootNode . GetNode < Node2D > ( "TileMap" ) ;
2024-07-26 14:38:18 +00:00
NodeUtils . ForEachNode < TileMapLayer > ( tileMapNode , node = >
{
_tileMapLayers ? ? = [ ] ;
_tileMapLayers . Add ( node ) ;
return false ;
} ) ;
2024-10-11 06:52:39 +00:00
_roomArea2D = rootNode . GetNode < Area2D > ( "RoomArea" ) ;
_roomArea2D . Monitoring = true ;
_roomArea2D . SetCollisionLayerValue ( Config . LayerNumber . RoomArea , true ) ;
_roomArea2D . SetCollisionMaskValue ( Config . LayerNumber . Player , true ) ;
_roomArea2D . SetCollisionMaskValue ( Config . LayerNumber . Mob , true ) ;
_roomArea2D . BodyExited + = OnExitRoom ;
_roomArea2D . BodyEntered + = OnEnterRoom ;
_spawnArea2D = rootNode . GetNodeOrNull < Area2D > ( "SpawnArea" ) ;
if ( _spawnArea2D ! = null )
{
_spawnArea2D . SetCollisionMaskValue ( Config . LayerNumber . Player , true ) ;
_spawnArea2D . BodyEntered + = OnEnterSpawnArea ;
}
_collisionShape2D = _roomArea2D . GetChild < CollisionShape2D > ( 0 ) ;
_roomSlots = GetRoomSlots ( GetTileMapLayer ( Config . TileMapLayerName . Ground ) , _roomArea2D ,
2024-10-08 08:11:51 +00:00
rootNode . GetNode < Node2D > ( "RoomSlotList" ) ) ;
_pointLight2D = rootNode . GetNodeOrNull < PointLight2D > ( "PointLight2D" ) ;
2024-08-25 10:49:48 +00:00
if ( _pointLight2D ! = null )
{
_pointLight2D . BlendMode = Light2D . BlendModeEnum . Mix ;
}
2024-10-08 08:11:51 +00:00
_autoSpawn = rootNode . GetNodeOrNull < Node2D > ( "AutoSpawn" ) ;
2024-04-28 13:55:19 +00:00
}
2024-10-11 06:52:39 +00:00
private void OnEnterSpawnArea ( Node2D body )
{
if ( _hasSpawnedEntity )
{
return ;
}
if ( body is not Player )
{
return ;
}
SpawnEnemyWave ( ) ;
_hasSpawnedEntity = true ;
}
2024-05-08 10:22:04 +00:00
public Node2D ? RootNode = > _rootNode ;
2024-04-28 13:55:19 +00:00
2024-05-08 10:22:04 +00:00
public RoomSlot ? [ ] ? RoomSlots = > _roomSlots ;
2024-04-28 13:55:19 +00:00
/// <summary>
/// <para>GetRoomSlots</para>
/// <para>在房间内获取所有插槽</para>
/// </summary>
2024-07-26 14:38:18 +00:00
/// <param name="tileMapLayer"></param>
2024-05-08 10:22:04 +00:00
/// <param name="roomArea2D"></param>
2024-04-28 13:55:19 +00:00
/// <param name="slotList"></param>
/// <returns></returns>
2024-07-26 14:38:18 +00:00
private RoomSlot ? [ ] ? GetRoomSlots ( TileMapLayer ? tileMapLayer , Area2D roomArea2D , Node2D slotList )
2024-04-28 13:55:19 +00:00
{
2024-07-26 14:38:18 +00:00
if ( tileMapLayer = = null )
2024-05-08 10:22:04 +00:00
{
return null ;
}
2024-04-28 13:55:19 +00:00
var slotCount = slotList . GetChildCount ( ) ;
if ( slotCount = = 0 )
{
return null ;
}
2024-05-24 14:58:52 +00:00
//We calculate the midpoint of the room
//我们计算房间的中点
2024-04-28 13:55:19 +00:00
var roomAreaCollisionShape2D = roomArea2D . GetChild < CollisionShape2D > ( 0 ) ;
var roomAreaRect2 = roomAreaCollisionShape2D . Shape . GetRect ( ) ;
var midpoint = roomAreaCollisionShape2D . Position + roomAreaRect2 . Position + roomAreaRect2 . Size / 2 ;
var roomSlots = new List < RoomSlot > ( ) ;
2024-05-22 14:39:41 +00:00
for ( var i = 0 ; i < slotCount ; i + + )
2024-04-28 13:55:19 +00:00
{
2024-05-24 14:58:52 +00:00
//Got the object in the room slot
//拿到了房间槽对象
2024-04-28 13:55:19 +00:00
var area2D = slotList . GetChild < Area2D > ( i ) ;
2024-05-25 16:02:00 +00:00
//Prevent other areas from detecting the room slot
//禁止其他区域检测到房间槽
area2D . Monitorable = false ;
2024-06-02 13:48:38 +00:00
2024-04-28 13:55:19 +00:00
var collisionShape2D = area2D . GetChild < CollisionShape2D > ( 0 ) ;
var rect2 = collisionShape2D . Shape . GetRect ( ) ;
2024-05-24 14:58:52 +00:00
//Round the size of the impactor to the tile size For example, the impactor size 44 is converted to the tile size 44/32=1.375 rounded to 1
2024-04-28 13:55:19 +00:00
//将碰撞体的尺寸四舍五入到瓦片尺寸例如: 碰撞体尺寸44, 转为瓦片尺寸为 44/32=1.375 四舍五入为1
var intSize = new Vector2I ( ( int ) Math . Round ( rect2 . Size . X / Config . CellSize ) ,
( int ) Math . Round ( rect2 . Size . Y / Config . CellSize ) ) ;
if ( intSize . X > 1 & & intSize . Y > 1 )
{
LogCat . LogError ( "width_or_height_of_room_slot_must_be_1" ) ;
continue ;
}
2024-05-24 14:58:52 +00:00
//Gets the absolute position of the slot
2024-04-28 13:55:19 +00:00
//获取槽位的绝对位置
var startPosition = area2D . Position + collisionShape2D . Position + rect2 . Position ;
var endPosition = area2D . Position + collisionShape2D . Position + rect2 . Position + rect2 . Size ;
var midpointOfRoomSlots = area2D . Position + collisionShape2D . Position + rect2 . Position + rect2 . Size / 2 ;
2024-05-24 14:58:52 +00:00
//Convert to tile map coordinates (midpoint)
2024-04-28 13:55:19 +00:00
//转为瓦片地图的坐标(中点)
2024-07-26 14:38:18 +00:00
var tileMapStartPosition = tileMapLayer . LocalToMap ( startPosition ) ;
var tileMapEndPosition = tileMapLayer . LocalToMap ( endPosition ) ;
2024-05-22 14:39:41 +00:00
var roomSlot = new RoomSlot
{
EndPosition = tileMapEndPosition ,
StartPosition = tileMapStartPosition ,
2024-05-24 14:58:52 +00:00
//Calculate the orientation of the slot (the midpoint of the room is the origin, the vector pointing to the midpoint of the slot)
2024-05-22 14:39:41 +00:00
//计算槽位的方向(房间中点为原点,指向槽位中点的向量)
DistanceToMidpointOfRoom = CoordinateUtils . VectorToOrientationArray ( midpoint , midpointOfRoomSlots )
} ;
2024-04-28 13:55:19 +00:00
roomSlots . Add ( roomSlot ) ;
}
return roomSlots . ToArray ( ) ;
}
}