From 88ed35e757ae69c2171c4bec48f19e6a70289c0b Mon Sep 17 00:00:00 2001 From: basil <2428390463@qq.com> Date: Tue, 15 Sep 2026 11:07:01 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=B8=85=E7=90=86=E6=95=8C?= =?UTF-8?q?=E4=BA=BA=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameMain/DataTables/tbentityconfig.bytes | 5 +- .../GameMain/DataTables/tbentityconfig.json | 4 + .../GameMain/DataTables/tbglobalconfig.bytes | Bin 25 -> 87 bytes .../GameMain/DataTables/tbglobalconfig.json | 18 +- ...BulletHandgun.prefab => Projectile.prefab} | 2 +- ...gun.prefab.meta => Projectile.prefab.meta} | 0 .../Base/EntityData/Enemy/EnemyData.cs | 34 ++-- .../EntityData/Enemy/EnemyProjectileData.cs | 2 +- .../Scripts/Base/Gen/EnemyStateType.cs | 46 +++++ .../Scripts/Base/Gen/EnemyStateType.cs.meta | 11 + .../GameMain/Scripts/Base/Gen/GlobalConfig.cs | 122 ++++++++++- .../Scripts/Base/Gen/TbGlobalConfig.cs | 68 ++++++- .../GameMain/Scripts/Base/Gen/WeaponConfig.cs | 2 +- .../Scripts/Procedure/Game/GameStateBattle.cs | 21 +- .../AsyncTask/EntityAsyncExtension.cs | 79 +++++++- .../EnemyManager/EnemyManagerComponent.cs | 12 +- .../EnemyManager/EnemySpawnScheduler.cs | 2 +- .../RandomCircleSpawnStrategy.cs | 13 +- .../Simulation/SimulationWorld.EntitySync.cs | 14 +- .../Runtime/EntityLogic/Enemy/EnemyBase.cs | 143 +++++++++---- .../EntityLogic/Enemy/EnemyProjectile.cs | 1 - .../Runtime/EntityLogic/Enemy/MeleeEnemy.cs | 145 +++----------- .../Runtime/EntityLogic/Enemy/RemoteEnemy.cs | 189 ++---------------- .../Runtime/EntityLogic/Weapon/WeaponBase.cs | 32 ++- .../WeaponHandgun/WeaponHandgunAttackState.cs | 2 +- .../WeaponKnife/WeaponKnifeAttackState.cs | 2 +- .../WeaponLance/WeaponLanceAttackState.cs | 2 +- .../WeaponLightningAttackState.cs | 2 +- .../WeaponSlash/WeaponSlashAttackState.cs | 2 +- .../Scripts/Runtime/Utility/AIUtility.cs | 14 +- Assets/Launcher.unity | 36 ++-- 数据表/Datas/EntityConfig_实体配置.xlsx | Bin 10211 -> 10250 bytes 数据表/Datas/GlobalConfig_全局配置.xlsx | Bin 10455 -> 11181 bytes 数据表/Datas/__enums__.xlsx | Bin 15370 -> 15551 bytes 34 files changed, 568 insertions(+), 457 deletions(-) rename Assets/GameMain/Entities/{BulletHandgun.prefab => Projectile.prefab} (99%) rename Assets/GameMain/Entities/{BulletHandgun.prefab.meta => Projectile.prefab.meta} (100%) create mode 100644 Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs create mode 100644 Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta diff --git a/Assets/GameMain/DataTables/tbentityconfig.bytes b/Assets/GameMain/DataTables/tbentityconfig.bytes index ceb9e6f..0734001 100644 --- a/Assets/GameMain/DataTables/tbentityconfig.bytes +++ b/Assets/GameMain/DataTables/tbentityconfig.bytes @@ -1,3 +1,4 @@ - FollowCameraFollowCamera.prefabPlayer Player.prefab + FollowCameraFollowCamera.prefabPlayer Player.prefab MeleeEnemyMeleeEnemy.prefab RemoteEnemyRemoteEnemy.prefab WeaponKnifeWeaponKnife.prefab WeaponHandgunWeaponHandgun.prefab WeaponSlashWeaponSlash.prefabWeaponLightningWeaponLightning.prefab WeaponLanceWeaponLance.prefab -CoinEntityCoinEntity.prefab ExpEntityExpEntity.prefab \ No newline at end of file +CoinEntityCoinEntity.prefab ExpEntityExpEntity.prefab +ProjectileProjectile.prefab \ No newline at end of file diff --git a/Assets/GameMain/DataTables/tbentityconfig.json b/Assets/GameMain/DataTables/tbentityconfig.json index 10629f4..017781d 100644 --- a/Assets/GameMain/DataTables/tbentityconfig.json +++ b/Assets/GameMain/DataTables/tbentityconfig.json @@ -42,5 +42,9 @@ { "assetName": "ExpEntity", "prefabPath": "ExpEntity.prefab" + }, + { + "assetName": "Projectile", + "prefabPath": "Projectile.prefab" } ] \ No newline at end of file diff --git a/Assets/GameMain/DataTables/tbglobalconfig.bytes b/Assets/GameMain/DataTables/tbglobalconfig.bytes index 6135738f42b9b364aefa053eeb33bad056b790de..87dcf6ab7e7017421e44c0060d3e423da693c042 100644 GIT binary patch literal 87 zcmZQ%`|uwK*aC7AD^rUaby$oTm{=xvY};_aEnsp7RG8H@FEzK4E1)PpD>b _enemyConfig.EnemyType; + public EnemyType EnemyType => Config.EnemyType; - public string LogicType => _enemyConfig.LogicType; + public string LogicType => Config.LogicType; public override int MaxHealthBase { get; } - public int AttackDamage => _enemyConfig.AttackDamage; + public int AttackDamage => Config.AttackDamage; - public float AttackCooldown => _enemyConfig.AttackCooldown; + public float AttackCooldown => Config.AttackCooldown; - public float AttackRange => _enemyConfig.AttackRange; + public float AttackRange => Config.AttackRange; - public float SpeedBase => _enemyConfig.Speed; + public float SpeedBase => Config.Speed; - public int DropCoin => _enemyConfig.CoinDrop; + public int DropCoin => Config.CoinDrop; - public int DropExp => _enemyConfig.ExpDrop; + public int DropExp => Config.ExpDrop; - public float DropPercent => _enemyConfig.DropPercent; + public float DropPercent => Config.DropPercent; - public float Scale => _enemyConfig.Scale; + public float Scale => Config.Scale; public Color Color { get { // 空/非法颜色值视为无染色(乘以白色)。 - return ColorUtility.TryParseHtmlString(_enemyConfig.Color, out Color color) + return ColorUtility.TryParseHtmlString(Config.Color, out Color color) ? color : Color.white; } } - - public IReadOnlyDictionary Params => null; - - public bool TryGetParam(string key, out string value) - { - value = null; - return false; - } } } diff --git a/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs b/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs index d8da270..ffd7711 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs +++ b/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs @@ -8,7 +8,7 @@ namespace SepCore.Entity public class EnemyProjectileData : EntityDataBase { public EnemyProjectileData(int entityId, int ownerEntityId, CampType ownerCamp, int attackDamage, - float speed, float lifeTime, Vector3 direction, string assetName = "BulletHandgun") + float speed, float lifeTime, Vector3 direction, string assetName = "Projectile") : base(assetName, entityId) { OwnerEntityId = ownerEntityId; diff --git a/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs new file mode 100644 index 0000000..047a284 --- /dev/null +++ b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs @@ -0,0 +1,46 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + + +namespace SepCore.Definition +{ + /// + /// 敌人状态 + /// + public enum EnemyStateType : byte + { + /// + /// 无 + /// + None = 0, + /// + /// 攻击 + /// + Attack = 1, + /// + /// 待机 + /// + Idle = 2, + /// + /// 搜索 + /// + Seek = 3, + /// + /// 追逐 + /// + Chase = 4, + /// + /// 死亡 + /// + Dead = 5, + } + +} + diff --git a/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta new file mode 100644 index 0000000..08f905d --- /dev/null +++ b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d55e30fdec251d141bab19b1b87806b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs index 47c990d..6ffec1a 100644 --- a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs @@ -18,15 +18,31 @@ public sealed partial class GlobalConfig : Luban.BeanBase { WeaponCapacity = _buf.ReadInt(); PropMaxCount = _buf.ReadInt(); + PlayerEntity = _buf.ReadString(); + PlayerEntity_Ref = null; WeaponRecycleRate = _buf.ReadInt(); ShopGoodsCount = _buf.ReadInt(); ShopPriceRandomPercent = _buf.ReadInt(); ShopMinPrice = _buf.ReadInt(); LevelUpRefreshPriceBase = _buf.ReadInt(); LevelUpRewardCount = _buf.ReadInt(); - EnemyGroupName = _buf.ReadString(); SpawnEnemyMaxCount = _buf.ReadInt(); - SpawnDistanceFromPlayer = _buf.ReadInt(); + EnemySpawnDistanceMin = _buf.ReadInt(); + EnemySpawnDistanceMax = _buf.ReadInt(); + WeaponRotateSpeed = _buf.ReadInt(); + PlayerGroupName = _buf.ReadString(); + EnemyGroupName = _buf.ReadString(); + ProjectileGroupName = _buf.ReadString(); + DropGroupName = _buf.ReadString(); + EffectGroupNmae = _buf.ReadString(); + MinDamage = _buf.ReadInt(); + MaxDodgeRate = _buf.ReadInt(); + MinSpawnRateScale = _buf.ReadInt(); + ProjectileLifeTime = _buf.ReadInt(); + ProjectileHeightOffset = _buf.ReadInt(); + ProjectileSpeed = _buf.ReadInt(); + ProjectileEntity = _buf.ReadString(); + ProjectileEntity_Ref = null; } public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf) @@ -43,6 +59,11 @@ public sealed partial class GlobalConfig : Luban.BeanBase /// public readonly int PropMaxCount; /// + /// 玩家实体 + /// + public readonly string PlayerEntity; + public EntityConfig PlayerEntity_Ref; + /// /// 武器回收价比例 /// public readonly int WeaponRecycleRate; @@ -67,17 +88,70 @@ public sealed partial class GlobalConfig : Luban.BeanBase /// public readonly int LevelUpRewardCount; /// - /// 敌人对象池名 - /// - public readonly string EnemyGroupName; - /// /// 最大敌人数量 /// public readonly int SpawnEnemyMaxCount; /// - /// 敌人生成距离 + /// 敌人生成最小距离 /// - public readonly int SpawnDistanceFromPlayer; + public readonly int EnemySpawnDistanceMin; + /// + /// 敌人生成最大距离 + /// + public readonly int EnemySpawnDistanceMax; + /// + /// 武器旋转速度 + /// + public readonly int WeaponRotateSpeed; + /// + /// 玩家对象组名 + /// + public readonly string PlayerGroupName; + /// + /// 敌人对象组名 + /// + public readonly string EnemyGroupName; + /// + /// 投射物对象组名 + /// + public readonly string ProjectileGroupName; + /// + /// 掉落物对象组名 + /// + public readonly string DropGroupName; + /// + /// 特效对象组名 + /// + public readonly string EffectGroupNmae; + /// + /// 最小伤害 + /// + public readonly int MinDamage; + /// + /// 闪避率上限 + /// + public readonly int MaxDodgeRate; + /// + /// 最小刷怪速度倍数 + /// + public readonly int MinSpawnRateScale; + /// + /// 投射物生命周期 + /// + public readonly int ProjectileLifeTime; + /// + /// 投射物生成高度 + /// + public readonly int ProjectileHeightOffset; + /// + /// 投射物移动速度 + /// + public readonly int ProjectileSpeed; + /// + /// 投射物实体 + /// + public readonly string ProjectileEntity; + public EntityConfig ProjectileEntity_Ref; public const int __ID__ = -958250779; public override int GetTypeId() => __ID__; @@ -86,6 +160,7 @@ public sealed partial class GlobalConfig : Luban.BeanBase { + PlayerEntity_Ref = tables.TbEntityConfig.GetOrDefault(PlayerEntity); @@ -95,6 +170,19 @@ public sealed partial class GlobalConfig : Luban.BeanBase + + + + + + + + + + + + + ProjectileEntity_Ref = tables.TbEntityConfig.GetOrDefault(ProjectileEntity); } public override string ToString() @@ -102,15 +190,29 @@ public sealed partial class GlobalConfig : Luban.BeanBase return "{ " + "weaponCapacity:" + WeaponCapacity + "," + "propMaxCount:" + PropMaxCount + "," + + "playerEntity:" + PlayerEntity + "," + "weaponRecycleRate:" + WeaponRecycleRate + "," + "shopGoodsCount:" + ShopGoodsCount + "," + "shopPriceRandomPercent:" + ShopPriceRandomPercent + "," + "shopMinPrice:" + ShopMinPrice + "," + "levelUpRefreshPriceBase:" + LevelUpRefreshPriceBase + "," + "levelUpRewardCount:" + LevelUpRewardCount + "," - + "enemyGroupName:" + EnemyGroupName + "," + "spawnEnemyMaxCount:" + SpawnEnemyMaxCount + "," - + "spawnDistanceFromPlayer:" + SpawnDistanceFromPlayer + "," + + "enemySpawnDistanceMin:" + EnemySpawnDistanceMin + "," + + "enemySpawnDistanceMax:" + EnemySpawnDistanceMax + "," + + "weaponRotateSpeed:" + WeaponRotateSpeed + "," + + "playerGroupName:" + PlayerGroupName + "," + + "enemyGroupName:" + EnemyGroupName + "," + + "projectileGroupName:" + ProjectileGroupName + "," + + "dropGroupName:" + DropGroupName + "," + + "effectGroupNmae:" + EffectGroupNmae + "," + + "minDamage:" + MinDamage + "," + + "maxDodgeRate:" + MaxDodgeRate + "," + + "minSpawnRateScale:" + MinSpawnRateScale + "," + + "projectileLifeTime:" + ProjectileLifeTime + "," + + "projectileHeightOffset:" + ProjectileHeightOffset + "," + + "projectileSpeed:" + ProjectileSpeed + "," + + "projectileEntity:" + ProjectileEntity + "," + "}"; } } diff --git a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs index a992005..4c6b621 100644 --- a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs @@ -39,6 +39,10 @@ public partial class TbGlobalConfig /// public int PropMaxCount => _data.PropMaxCount; /// + /// 玩家实体 + /// + public string PlayerEntity => _data.PlayerEntity; + /// /// 武器回收价比例 /// public int WeaponRecycleRate => _data.WeaponRecycleRate; @@ -63,17 +67,69 @@ public partial class TbGlobalConfig /// public int LevelUpRewardCount => _data.LevelUpRewardCount; /// - /// 敌人对象池名 - /// - public string EnemyGroupName => _data.EnemyGroupName; - /// /// 最大敌人数量 /// public int SpawnEnemyMaxCount => _data.SpawnEnemyMaxCount; /// - /// 敌人生成距离 + /// 敌人生成最小距离 /// - public int SpawnDistanceFromPlayer => _data.SpawnDistanceFromPlayer; + public int EnemySpawnDistanceMin => _data.EnemySpawnDistanceMin; + /// + /// 敌人生成最大距离 + /// + public int EnemySpawnDistanceMax => _data.EnemySpawnDistanceMax; + /// + /// 武器旋转速度 + /// + public int WeaponRotateSpeed => _data.WeaponRotateSpeed; + /// + /// 玩家对象组名 + /// + public string PlayerGroupName => _data.PlayerGroupName; + /// + /// 敌人对象组名 + /// + public string EnemyGroupName => _data.EnemyGroupName; + /// + /// 投射物对象组名 + /// + public string ProjectileGroupName => _data.ProjectileGroupName; + /// + /// 掉落物对象组名 + /// + public string DropGroupName => _data.DropGroupName; + /// + /// 特效对象组名 + /// + public string EffectGroupNmae => _data.EffectGroupNmae; + /// + /// 最小伤害 + /// + public int MinDamage => _data.MinDamage; + /// + /// 闪避率上限 + /// + public int MaxDodgeRate => _data.MaxDodgeRate; + /// + /// 最小刷怪速度倍数 + /// + public int MinSpawnRateScale => _data.MinSpawnRateScale; + /// + /// 投射物生命周期 + /// + public int ProjectileLifeTime => _data.ProjectileLifeTime; + /// + /// 投射物生成高度 + /// + public int ProjectileHeightOffset => _data.ProjectileHeightOffset; + /// + /// 投射物移动速度 + /// + public int ProjectileSpeed => _data.ProjectileSpeed; + /// + /// 投射物实体 + /// + public string ProjectileEntity => _data.ProjectileEntity; public void ResolveRef(Tables tables) { diff --git a/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs b/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs index 3e5c26b..792637f 100644 --- a/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs @@ -62,7 +62,7 @@ public sealed partial class WeaponConfig : Luban.BeanBase /// public readonly int Cooldown; /// - /// 范围 + /// 攻击范围 /// public readonly int AttackRange; /// diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs index 832ecd5..1c418a6 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs @@ -6,6 +6,7 @@ using SepCore.EnemyManager; using SepCore.Simulation; using SepCore.Timer; using UnityEngine; +using UnityGameFramework.Runtime; namespace SepCore.Procedure { @@ -86,16 +87,9 @@ namespace SepCore.Procedure // 停止玩家逻辑 Player.Enable = false; - // 隐藏所有掉落物实体 - var entities = GameEntry.Entity.GetEntityGroup("Drop").GetAllEntities(); - foreach (var entity in entities) - { - GameEntry.Entity.HideEntity(entity.Id); - } - - HideEntityGroup("Bullet"); - HideEntityGroup("Projectile"); - HideEntityGroup("EnemyProjectile"); + // 隐藏所有掉落物与投射物实体 + HideEntityGroup(GameEntry.Luban.Global.DropGroupName); + HideEntityGroup(GameEntry.Luban.Global.ProjectileGroupName); } public override void OnDestroy() @@ -106,13 +100,14 @@ namespace SepCore.Procedure private static void HideEntityGroup(string groupName) { - var entityGroup = GameEntry.Entity.GetEntityGroup(groupName); - var entities = entityGroup?.GetAllEntities(); - if (entities == null) + if (!GameEntry.Entity.HasEntityGroup(groupName)) { + Log.Error("Entity group '{0}' does not exist.", groupName); return; } + var entityGroup = GameEntry.Entity.GetEntityGroup(groupName); + var entities = entityGroup.GetAllEntities(); foreach (var entity in entities) { GameEntry.Entity.HideEntity(entity.Id); diff --git a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs index 473497f..bdd5474 100644 --- a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs +++ b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs @@ -5,6 +5,7 @@ using Cysharp.Threading.Tasks; using SepCore.CustomUtility; using SepCore.Definition; using SepCore.Entity; +using UnityEngine; using UnityGameFramework.Runtime; using HideEntityCompleteEventArgs = UnityGameFramework.Runtime.HideEntityCompleteEventArgs; using ShowEntityFailureEventArgs = UnityGameFramework.Runtime.ShowEntityFailureEventArgs; @@ -149,7 +150,7 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(Player), "Player", + return ShowEntityByDataAsync(entityComponent, typeof(Player), GameEntry.Luban.Global.PlayerGroupName, Constant.AssetPriority.PlayerAsset, data, timeout, cancellationToken); } @@ -165,7 +166,7 @@ namespace SepCore.AsyncTask EntityConfig entityConfig = GameEntry.Luban.Get(data.AssetName); return entityComponent.ShowEntityAsync( - data.Id, enemyType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), "Enemy", + data.Id, enemyType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), GameEntry.Luban.Global.EnemyGroupName, Constant.AssetPriority.EnemyAsset, data, timeout, cancellationToken) .ContinueWith(entity => entity.Logic as EnemyBase); } @@ -182,7 +183,7 @@ namespace SepCore.AsyncTask EntityConfig entityConfig = GameEntry.Luban.Get(data.AssetName); return entityComponent.ShowEntityAsync( - data.Id, weaponType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), "Weapon", + data.Id, weaponType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), GameEntry.Luban.Global.PlayerGroupName, Constant.AssetPriority.WeaponAsset, data, timeout, cancellationToken) .ContinueWith(entity => entity.Logic as WeaponBase); } @@ -195,7 +196,7 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(Effect), "Effect", + return ShowEntityByDataAsync(entityComponent, typeof(Effect), GameEntry.Luban.Global.EffectGroupNmae, Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); } @@ -207,7 +208,7 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(CoinEntity), "Drop", + return ShowEntityByDataAsync(entityComponent, typeof(CoinEntity), GameEntry.Luban.Global.DropGroupName, Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); } @@ -219,10 +220,76 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(ExpEntity), "Drop", + return ShowEntityByDataAsync(entityComponent, typeof(ExpEntity), GameEntry.Luban.Global.DropGroupName, Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); } + /// + /// 异步显示投射物实体。 + /// + public static UniTask ShowProjectileAsync(this EntityComponent entityComponent, + EnemyProjectileData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityByDataAsync(entityComponent, typeof(EnemyProjectile), + GameEntry.Luban.Global.ProjectileGroupName, + Constant.AssetPriority.BulletAsset, data, timeout, cancellationToken); + } + + /// + /// 异步显示投射物实体(直接入参快捷方法)。 + /// + public static UniTask ShowProjectileAsync(this EntityComponent entityComponent, + int ownerEntityId, + CampType ownerCamp, + int attackDamage, + Vector3 position, + Vector3 direction, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + int projectileEntityId = entityComponent.NextId(); + float speed = GameEntry.Luban.Global.ProjectileSpeed / 1000f; + float lifeTime = GameEntry.Luban.Global.ProjectileLifeTime / 1000f; + string assetName = GameEntry.Luban.Global.ProjectileEntity; + + var projectileData = new EnemyProjectileData(projectileEntityId, ownerEntityId, ownerCamp, + attackDamage, speed, lifeTime, direction, assetName) + { + Position = position, + Rotation = Quaternion.LookRotation(direction, Vector3.up) + }; + + return entityComponent.ShowProjectileAsync(projectileData, timeout, cancellationToken); + } + + /// + /// 异步显示投射物实体。 + /// + public static UniTask ShowProjectile(this EntityComponent entityComponent, + EnemyProjectileData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return entityComponent.ShowProjectileAsync(data, timeout, cancellationToken); + } + + /// + /// 异步显示投射物实体(直接入参快捷方法)。 + /// + public static UniTask ShowProjectile(this EntityComponent entityComponent, + int ownerEntityId, + CampType ownerCamp, + int attackDamage, + Vector3 position, + Vector3 direction, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return entityComponent.ShowProjectileAsync(ownerEntityId, ownerCamp, attackDamage, position, direction, timeout, cancellationToken); + } + /// /// 异步隐藏实体 /// diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs index 0ab45c6..4e87b5d 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs @@ -22,7 +22,6 @@ namespace SepCore.EnemyManager public IReadOnlyCollection Enemies => _enemyRegistry.Enemies; private int _spawnEnemyMaxCount; - private int _spawnDistanceFromPlayer; private int _currentSpawnEnemyId; private int _currentLevel; @@ -73,11 +72,18 @@ namespace SepCore.EnemyManager return; } + if (!_entity.HasEntityGroup(EnemyGroupName)) + { + Log.Error("Entity group '{0}' does not exist.", EnemyGroupName); + return; + } + _isStopped = false; _spawnEnemyMaxCount = GameEntry.Luban.Global.SpawnEnemyMaxCount; - _spawnDistanceFromPlayer = GameEntry.Luban.Global.SpawnDistanceFromPlayer / 1000; + float minDistance = GameEntry.Luban.Global.EnemySpawnDistanceMin / 1000f; + float maxDistance = GameEntry.Luban.Global.EnemySpawnDistanceMax / 1000f; _player = player.CachedTransform; - _spawnPositionStrategy = new RandomCircleSpawnStrategy(_spawnDistanceFromPlayer); + _spawnPositionStrategy = new RandomCircleSpawnStrategy(minDistance, maxDistance); _baseDuration = level.Duration; _duration = _baseDuration; diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs index bdfc862..91f2333 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs @@ -8,7 +8,7 @@ namespace SepCore.EnemyManager { public class EnemySpawnScheduler { - private const float MinSpawnRateScale = 0.1f; + private static float MinSpawnRateScale => GameEntry.Luban.Global.MinSpawnRateScale / 1000f; private float _elapsedTime; private float[] _baseIntervals; diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs index 12a31fc..47740b7 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs @@ -5,19 +5,22 @@ namespace SepCore.EnemyManager { public class RandomCircleSpawnStrategy : ISpawnPositionStrategy { - private readonly float _spawnDistance; + private readonly float _minDistance; + private readonly float _maxDistance; - public RandomCircleSpawnStrategy(float spawnDistance) + public RandomCircleSpawnStrategy(float minDistance, float maxDistance) { - _spawnDistance = spawnDistance; + _minDistance = minDistance; + _maxDistance = maxDistance; } public Vector3 GetSpawnPosition(Transform player) { float x = Random.Range(-1f, 1f); float z = Random.Range(-1f, 1f); - Vector3 dir = new Vector3(x, 0, z).normalized; - return player.position + dir * _spawnDistance; + Vector3 dir = new Vector3(x, 0f, z).normalized; + float distance = Random.Range(_minDistance, _maxDistance); + return player.position + dir * distance; } } } diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs index bc156e7..9a77e7a 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs @@ -9,11 +9,9 @@ namespace SepCore.Simulation // Bridges entity show/hide events into simulation state registration. public sealed class EntitySync { - private const string EnemyGroupName = "Enemy"; - private const string DropGroupName = "Drop"; - private const string BulletGroupName = "Bullet"; - private const string ProjectileGroupName = "Projectile"; - private const string EnemyProjectileGroupName = "EnemyProjectile"; + private static string EnemyGroupName => GameEntry.Luban.Global.EnemyGroupName; + private static string DropGroupName => GameEntry.Luban.Global.DropGroupName; + private static string ProjectileGroupName => GameEntry.Luban.Global.ProjectileGroupName; private readonly SimulationWorld _world; private bool _isEventSubscribed; @@ -74,8 +72,7 @@ namespace SepCore.Simulation return; } - if ((groupName == BulletGroupName || groupName == ProjectileGroupName || - groupName == EnemyProjectileGroupName) && + if (groupName == ProjectileGroupName && args.Entity.Logic is EntityBase projectileEntity) { _world.RegisterProjectileLifecycle(projectileEntity, args.UserData); @@ -101,8 +98,7 @@ namespace SepCore.Simulation return; } - if (groupName == BulletGroupName || groupName == ProjectileGroupName || - groupName == EnemyProjectileGroupName) + if (groupName == ProjectileGroupName) { _world.UnregisterProjectileLifecycle(args.EntityId); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs index 4f27cfd..7e6a417 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs @@ -1,61 +1,124 @@ -using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks; using SepCore.AsyncTask; +using SepCore.Components; using SepCore.Definition; -using SepCore.Entity; using UnityEngine; +using UnityGameFramework.Runtime; -public abstract class EnemyBase : TargetableObject +namespace SepCore.Entity { - protected Transform _target; - - public abstract override ImpactData GetImpactData(); - public virtual float AttackRange => 1f; - - public virtual void SetTarget(Transform target) => _target = target; - - protected EnemyData _enemyData; - - protected override void OnShow(object userData) + public abstract class EnemyBase : TargetableObject { - base.OnShow(userData); + protected Transform _target; - if (userData is EnemyData enemyData) + public EnemyData EnemyData { get; private set; } + + public MovementComponent Movement { get; private set; } + + protected override TargetableObjectData _targetableObjectData => EnemyData; + + public virtual float AttackRange => EnemyData.AttackRange; + + public float AttackCooldown => EnemyData.AttackCooldown; + + public int AttackDamage => EnemyData.AttackDamage; + + public override ImpactData GetImpactData() { - ApplyVisualParams(enemyData); + return new ImpactData(EnemyData.Camp, EnemyData.AttackDamage); } - } - protected override void OnDead(EntityBase attacker) - { - if (Random.value < _enemyData.DropPercent) + public virtual void SetTarget(Transform target) => _target = target; + + #region Lifecycle + + protected override void OnInit(object userData) { - var data = new CoinData(_enemyData.DropCoin, GameEntry.Entity.NextId()) + base.OnInit(userData); + + Movement = GetComponent(); + _healthComponent = GetComponent(); + } + + protected override void OnShow(object userData) + { + base.OnShow(userData); + + if (userData is EnemyData enemyData) { - Position = this.CachedTransform.position - }; - GameEntry.Entity.ShowCoinAsync(data).Forget(); - } + EnemyData = enemyData; + _healthComponent.OnInit(enemyData.MaxHealthBase); + Movement.OnInit(enemyData.SpeedBase, CachedTransform, null, true); + Movement.SetMove(true); - if (Random.value < _enemyData.DropPercent) - { - var data = new ExpData(_enemyData.DropExp, GameEntry.Entity.NextId()) + StartAttackCooldown(enemyData.AttackCooldown); + CachedTransform.position = enemyData.Position; + ApplyVisualParams(enemyData); + } + else { - Position = this.CachedTransform.position - }; - GameEntry.Entity.ShowExpAsync(data).Forget(); + Log.Error("Invalid enemy data type: {0}", userData?.GetType()); + } } - base.OnDead(attacker); - } + protected override void OnHide(bool isShutdown, object userData) + { + Movement.OnReset(); + _healthComponent.OnReset(); + CancelAttackCooldown(); + _target = null; - private void ApplyVisualParams(EnemyData data) - { - CachedTransform.localScale = Vector3.one * Mathf.Max(0.1f, data.Scale); + base.OnHide(isShutdown, userData); + } - var renderer = GetComponentInChildren(); - var block = new MaterialPropertyBlock(); - block.SetColor("_BaseColor", data.Color); - renderer.SetPropertyBlock(block); + protected override void OnDead(EntityBase attacker) + { + if (Random.value < EnemyData.DropPercent) + { + var data = new CoinData(EnemyData.DropCoin, GameEntry.Entity.NextId()) + { + Position = CachedTransform.position + }; + GameEntry.Entity.ShowCoinAsync(data).Forget(); + } + + if (Random.value < EnemyData.DropPercent) + { + var data = new ExpData(EnemyData.DropExp, GameEntry.Entity.NextId()) + { + Position = CachedTransform.position + }; + GameEntry.Entity.ShowExpAsync(data).Forget(); + } + + base.OnDead(attacker); + } + + #endregion + + protected Vector3 GetTargetDirection() + { + if (_target == null) + { + return Vector3.zero; + } + + return new Vector3( + _target.position.x - CachedTransform.position.x, + 0f, + _target.position.z - CachedTransform.position.z + ).normalized; + } + + private void ApplyVisualParams(EnemyData data) + { + CachedTransform.localScale = Vector3.one * data.Scale; + + var renderer = GetComponentInChildren(); + var block = new MaterialPropertyBlock(); + block.SetColor("_BaseColor", data.Color); + renderer.SetPropertyBlock(block); + } } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs index afac194..a267618 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs @@ -1,5 +1,4 @@ using SepCore.Definition; -using SepCore.Entity; using UnityEngine; using UnityGameFramework.Runtime; diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs index 304d4ef..904200a 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs @@ -1,83 +1,24 @@ -using SepCore.Components; using SepCore.CustomUtility; using SepCore.Definition; using UnityEngine; -using UnityGameFramework.Runtime; namespace SepCore.Entity { public class MeleeEnemy : EnemyBase { - private enum AttackStateType - { - Idle, - Check_OutRange, - Check_InRange, - Attack - } - - private MovementComponent _movementComponent; - - private float _attackRange = 1f; - private float _attackCooldown = 1f; - private int _attackDamage = 1; - private float _sqrAttackRange; - - private AttackStateType _attackState = AttackStateType.Idle; - - private EnemyData _meleeEnemyData - { - get => _enemyData; - set => _enemyData = value; - } - + private EnemyStateType enemyState = EnemyStateType.Idle; private TargetableObject _targetableTarget; - protected override TargetableObjectData _targetableObjectData => _meleeEnemyData; - public override float AttackRange => _attackRange; - - public override ImpactData GetImpactData() - { - return new ImpactData(_meleeEnemyData.Camp, _attackDamage); - } - - #region FSM - - protected override void OnInit(object userData) - { - base.OnInit(userData); - - _movementComponent = GetComponent(); - _healthComponent = GetComponent(); - } + #region Lifecycle protected override void OnShow(object userData) { base.OnShow(userData); - if (userData is EnemyData enemyData) - { - _meleeEnemyData = enemyData; - _healthComponent.OnInit(enemyData.MaxHealthBase); - _movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform, null, true); - _movementComponent.SetMove(true); - - _attackRange = Mathf.Max(0.1f, _meleeEnemyData.AttackRange); - _attackCooldown = Mathf.Max(0.01f, _meleeEnemyData.AttackCooldown); - _attackDamage = Mathf.Max(1, _meleeEnemyData.AttackDamage); - _sqrAttackRange = _attackRange * _attackRange; - - StartAttackCooldown(_attackCooldown); - _attackState = AttackStateType.Idle; - _targetableTarget = null; - - this.CachedTransform.position = enemyData.Position; - } - else - { - Log.Error($"Invalid data type. Data type: {userData?.GetType()}"); - } + _sqrAttackRange = AttackRange * AttackRange; + enemyState = EnemyStateType.Idle; + _targetableTarget = null; } protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) @@ -89,11 +30,8 @@ namespace SepCore.Entity protected override void OnHide(bool isShutdown, object userData) { - _movementComponent.OnReset(); - _healthComponent.OnReset(); _targetableTarget = null; - CancelAttackCooldown(); - _attackState = AttackStateType.Idle; + enemyState = EnemyStateType.Idle; base.OnHide(isShutdown, userData); } @@ -108,74 +46,73 @@ namespace SepCore.Entity private void UpdateAttackState(float elapseSeconds) { - switch (_attackState) + switch (enemyState) { - case AttackStateType.Idle: - SetMove(false); + case EnemyStateType.Idle: + Movement.SetMove(false); if (HasValidTarget()) { - TransitionTo(AttackStateType.Check_OutRange); + TransitionTo(EnemyStateType.Seek); } break; - case AttackStateType.Check_OutRange: + case EnemyStateType.Seek: if (!HasValidTarget()) { - TransitionTo(AttackStateType.Idle); + TransitionTo(EnemyStateType.Idle); return; } if (IsTargetInRange()) { - TransitionTo(AttackStateType.Check_InRange); + TransitionTo(EnemyStateType.Chase); return; } - SetMove(true); - _movementComponent.SetDirection(GetTargetDirection()); + Movement.SetMove(true); + Movement.SetDirection(GetTargetDirection()); break; - case AttackStateType.Check_InRange: + case EnemyStateType.Chase: if (!HasValidTarget()) { - TransitionTo(AttackStateType.Idle); + TransitionTo(EnemyStateType.Idle); return; } - SetMove(false); + Movement.SetMove(false); if (!IsTargetInRange()) { - TransitionTo(AttackStateType.Check_OutRange); + TransitionTo(EnemyStateType.Seek); return; } if (_canAttack) { - ResetAttackCooldown(_attackCooldown); - TransitionTo(AttackStateType.Attack); + ResetAttackCooldown(AttackCooldown); + TransitionTo(EnemyStateType.Attack); } break; } } - private void TransitionTo(AttackStateType newState) + private void TransitionTo(EnemyStateType newState) { - _attackState = newState; + enemyState = newState; - if (_attackState == AttackStateType.Check_InRange) + if (enemyState == EnemyStateType.Chase) { - SetMove(false); + Movement.SetMove(false); } - if (_attackState == AttackStateType.Attack) + if (enemyState == EnemyStateType.Attack) { - SetMove(false); + Movement.SetMove(false); ExecuteAttack(); - TransitionTo(AttackStateType.Check_InRange); + TransitionTo(EnemyStateType.Chase); } } - private bool HasValidTarget() { if (_target == null) return false; @@ -192,7 +129,7 @@ namespace SepCore.Entity { if (_target == null) return false; - Vector3 delta = _target.position - this.CachedTransform.position; + Vector3 delta = _target.position - CachedTransform.position; delta.y = 0f; return delta.sqrMagnitude <= _sqrAttackRange; } @@ -208,31 +145,9 @@ namespace SepCore.Entity return; } - int damage = AIUtility.CalcDamageHP(_attackDamage, null, targetImpactData.DefenseStat, + int damage = AIUtility.CalcDamageHP(AttackDamage, null, targetImpactData.DefenseStat, targetImpactData.DodgeStat); _targetableTarget.ApplyDamage(this, damage); } - - private void SetMove(bool value) - { - if (_movementComponent != null) - { - _movementComponent.SetMove(value); - } - } - - private Vector3 GetTargetDirection() - { - if (_target == null) - { - return Vector3.zero; - } - - return new Vector3( - _target.position.x - this.CachedTransform.position.x, - 0f, - _target.position.z - this.CachedTransform.position.z - ).normalized; - } } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs index 6b72e8a..c2f6a90 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs @@ -1,8 +1,7 @@ -using SepCore.Components; +using Cysharp.Threading.Tasks; using SepCore.AsyncTask; using SepCore.CustomUtility; using SepCore.Definition; -using Cysharp.Threading.Tasks; using UnityEngine; using UnityGameFramework.Runtime; @@ -10,83 +9,13 @@ namespace SepCore.Entity { public class RemoteEnemy : EnemyBase { - private const string EnemyProjectileGroupName = "Bullet"; - private const float EnemyProjectileGroupAutoReleaseInterval = 60f; - private const int EnemyProjectileGroupCapacity = 64; - private const float EnemyProjectileGroupExpireTime = 60f; - private const int EnemyProjectileGroupPriority = 0; - - private const string ProjectileSpeedParamKey = "ProjectileSpeed"; - private const string ProjectileLifeTimeParamKey = "ProjectileLifeTime"; - private const string ProjectileSpawnForwardOffsetParamKey = "ProjectileSpawnForwardOffset"; - private const string ProjectileSpawnHeightOffsetParamKey = "ProjectileSpawnHeightOffset"; - private const string ProjectileAssetNameParamKey = "ProjectileAssetName"; - - private MovementComponent _movementComponent; - private float _attackRange = 1f; private float _attackRangeSquared; - private float _attackCooldown = 1f; - private int _attackDamage = 1; - - [SerializeField] private float _projectileSpeed = 12f; - [SerializeField] private float _projectileLifeTime = 3f; - [SerializeField] private float _projectileSpawnForwardOffset = 0.7f; - [SerializeField] private float _projectileSpawnHeightOffset = 0.6f; - [SerializeField] private string _projectileAssetName = "BulletHandgun"; - - private EnemyData _remoteEnemyData - { - get => _enemyData; - set => _enemyData = value; - } - - protected override TargetableObjectData _targetableObjectData => _remoteEnemyData; - public override float AttackRange => _attackRange; - - public override ImpactData GetImpactData() - { - return new ImpactData(_remoteEnemyData.Camp, _attackDamage); - } - - protected override void OnInit(object userData) - { - base.OnInit(userData); - - _movementComponent = GetComponent(); - _healthComponent = GetComponent(); - } protected override void OnShow(object userData) { base.OnShow(userData); - if (userData is EnemyData enemyData) - { - _remoteEnemyData = enemyData; - _healthComponent.OnInit(enemyData.MaxHealthBase); - _movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform, null, true); - _movementComponent.SetMove(true); - - _attackRange = Mathf.Max(0.1f, _remoteEnemyData.AttackRange); - _attackRangeSquared = _attackRange * _attackRange; - _attackCooldown = Mathf.Max(0.01f, _remoteEnemyData.AttackCooldown); - _attackDamage = Mathf.Max(1, _remoteEnemyData.AttackDamage); - - _projectileSpeed = ReadPositiveParam(ProjectileSpeedParamKey, _projectileSpeed); - _projectileLifeTime = ReadPositiveParam(ProjectileLifeTimeParamKey, _projectileLifeTime); - _projectileSpawnForwardOffset = ReadPositiveParam(ProjectileSpawnForwardOffsetParamKey, - _projectileSpawnForwardOffset); - _projectileSpawnHeightOffset = ReadPositiveParam(ProjectileSpawnHeightOffsetParamKey, - _projectileSpawnHeightOffset); - _projectileAssetName = ReadStringParam(ProjectileAssetNameParamKey, _projectileAssetName); - - StartAttackCooldown(_attackCooldown); - this.CachedTransform.position = enemyData.Position; - } - else - { - Log.Error($"Invalid data type. Data type: {userData?.GetType()}"); - } + _attackRangeSquared = AttackRange * AttackRange; } protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) @@ -95,50 +24,45 @@ namespace SepCore.Entity if (_target == null) { - _movementComponent.SetMove(false); + Movement.SetMove(false); return; } - Vector3 toTarget = _target.position - this.CachedTransform.position; + Vector3 toTarget = _target.position - CachedTransform.position; toTarget.y = 0f; float distanceSquared = toTarget.sqrMagnitude; if (distanceSquared <= _attackRangeSquared) { - _movementComponent.SetMove(false); + Movement.SetMove(false); if (_canAttack) { TryFireProjectile(); - ResetAttackCooldown(_attackCooldown); + ResetAttackCooldown(AttackCooldown); } } else { - _movementComponent.SetMove(true); - _movementComponent.SetDirection(GetTargetDirection()); + Movement.SetMove(true); + Movement.SetDirection(GetTargetDirection()); } } - protected override void OnHide(bool isShutdown, object userData) - { - _movementComponent.OnReset(); - _healthComponent.OnReset(); - CancelAttackCooldown(); - - base.OnHide(isShutdown, userData); - } - private void TryFireProjectile() { - if (!EnsureEnemyProjectileGroup()) return; + string groupName = GameEntry.Luban.Global.ProjectileGroupName; + if (!GameEntry.Entity.HasEntityGroup(groupName)) + { + Log.Error("Entity group '{0}' does not exist.", groupName); + return; + } - Vector3 spawnPosition = this.CachedTransform.position + - this.CachedTransform.forward * _projectileSpawnForwardOffset + - Vector3.up * _projectileSpawnHeightOffset; + float spawnHeight = GameEntry.Luban.Global.ProjectileHeightOffset / 1000f; + Vector3 spawnPosition = CachedTransform.position + Vector3.up * spawnHeight; Vector3 direction = _target.position - spawnPosition; direction.y = 0f; if (direction.sqrMagnitude <= Mathf.Epsilon) { - direction = this.CachedTransform.forward; + direction = CachedTransform.forward; direction.y = 0f; } @@ -151,84 +75,7 @@ namespace SepCore.Entity direction.Normalize(); } - int projectileEntityId = GameEntry.Entity.NextId(); - var projectileData = new EnemyProjectileData(projectileEntityId, Id, _remoteEnemyData.Camp, - _attackDamage, _projectileSpeed, _projectileLifeTime, direction) - { - Position = spawnPosition, - Rotation = Quaternion.LookRotation(direction, Vector3.up) - }; - - GameEntry.Entity.ShowEntityAsync( - projectileEntityId, - AssetUtility.GetEntityAsset(_projectileAssetName), - EnemyProjectileGroupName, - Constant.AssetPriority.BulletAsset, - projectileData).Forget(); - } - - private static bool EnsureEnemyProjectileGroup() - { - var entityComponent = GameEntry.Entity; - if (entityComponent == null) return false; - - if (entityComponent.HasEntityGroup(EnemyProjectileGroupName)) - { - return true; - } - - bool addResult = entityComponent.AddEntityGroup( - EnemyProjectileGroupName, - EnemyProjectileGroupAutoReleaseInterval, - EnemyProjectileGroupCapacity, - EnemyProjectileGroupExpireTime, - EnemyProjectileGroupPriority); - - if (!addResult) - { - Log.Warning("Can not create entity group '{0}'.", EnemyProjectileGroupName); - return false; - } - - return true; - } - - private float ReadPositiveParam(string paramName, float defaultValue) - { - if (_remoteEnemyData != null && - _remoteEnemyData.TryGetParam(paramName, out string rawValue) && - float.TryParse(rawValue, out float parsedValue)) - { - return Mathf.Max(0.01f, parsedValue); - } - - return Mathf.Max(0.01f, defaultValue); - } - - private string ReadStringParam(string paramName, string defaultValue) - { - if (_remoteEnemyData != null && - _remoteEnemyData.TryGetParam(paramName, out string rawValue) && - !string.IsNullOrWhiteSpace(rawValue)) - { - return rawValue; - } - - return defaultValue; - } - - private Vector3 GetTargetDirection() - { - if (_target == null) - { - return Vector3.zero; - } - - return new Vector3( - _target.position.x - this.CachedTransform.position.x, - 0f, - _target.position.z - this.CachedTransform.position.z - ).normalized; + GameEntry.Entity.ShowProjectile(Id, EnemyData.Camp, AttackDamage, spawnPosition, direction).Forget(); } } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs index b662234..c376e6b 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs @@ -18,9 +18,7 @@ namespace SepCore.Entity [SerializeField] protected bool _isEnabled = false; public WeaponData WeaponData { get; protected set; } - - public float AttackCooldown => WeaponData.Cooldown; - + public bool IsAttacking => _isAttacking; protected ITargetSelector TargetSelector { get; private set; } @@ -29,7 +27,7 @@ namespace SepCore.Entity protected EntityBase _target; - protected float _sqrRange; + private float _sqrRange; public EntityBase Target => _target; @@ -40,8 +38,9 @@ namespace SepCore.Entity private StatComponent _attackStatComponent; private System.Action _attackStatCallback; - [SerializeField] protected float _rotateSpeed = 5f; - [SerializeField] protected LayerMask _hitMask = ~0; + private static float _rotateSpeed => GameEntry.Luban.Global.WeaponRotateSpeed; + + protected LayerMask _hitMask; protected Quaternion _cachedRotation; #region Lifecycle @@ -57,8 +56,12 @@ namespace SepCore.Entity { base.OnShow(userData); - WeaponData = RequireWeaponData(userData); - if (WeaponData == null) return; + WeaponData = (WeaponData)userData; + if (WeaponData == null) + { + Log.Error("WeaponData is Invalid!"); + return; + } StartAttackCooldown(WeaponData.Cooldown); _sqrRange = WeaponData.AttackRange * WeaponData.AttackRange; @@ -229,18 +232,7 @@ namespace SepCore.Entity protected virtual void OnEnabledChanged(bool enabled) { } - - protected T RequireWeaponData(object userData) where T : WeaponData - { - var data = userData as T; - if (data == null) - { - Log.Error("{0} data is invalid.", typeof(T).Name); - } - - return data; - } - + protected IEnumerable GetCandidates() { return GameEntry.EnemyManager.Enemies; diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs index e0c0b5b..fcf5bbe 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs index 999a18e..c9c8db1 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs index 6de10c1..d8955b3 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs index 8c51a07..aee7a0a 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs index f8080e4..98e3645 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs b/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs index 8ea18dc..7fdc539 100644 --- a/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs +++ b/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using SepCore.Definition; @@ -239,10 +239,11 @@ namespace SepCore.CustomUtility public static int CalcDamageHP(int attack, StatProperty attackStat, StatProperty defenseStat, StatProperty dodgeStat) { - // 1. 处理闪避(闪避率取值 (0, 0.9),不允许拉满闪避) + // 1. 处理闪避(从全局表读取闪避率上限) + float maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f; if (dodgeStat != null) { - if (Random.value < Mathf.Clamp(dodgeStat.Value, 0, 0.9f)) return 0; + if (Random.value < Mathf.Clamp(dodgeStat.Value, 0f, maxDodge)) return 0; } // 2. 处理攻击加成 最终伤害 = (基础伤害 + 伤害提升固定值) * 伤害提升率 @@ -258,9 +259,10 @@ namespace SepCore.CustomUtility damage = (damage - defenseStat.Value) / defenseStat.Percent; } - // 将伤害限制为正整数,避免堆防御导致无法造成伤害 - if (damage < 1) return 1; - else return Mathf.CeilToInt(damage); + // 4. 将伤害限制为保底伤害,避免堆防御导致无法造成伤害 + int minDamage = GameEntry.Luban.Global.MinDamage; + if (damage < minDamage) return minDamage; + return Mathf.CeilToInt(damage); } [StructLayout(LayoutKind.Auto)] diff --git a/Assets/Launcher.unity b/Assets/Launcher.unity index a4d3075..cf47dca 100644 --- a/Assets/Launcher.unity +++ b/Assets/Launcher.unity @@ -574,28 +574,28 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.size - value: 6 + value: 5 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[0].m_Name - value: Weapon + value: Projectile objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[1].m_Name - value: Bullet - objectReference: {fileID: 0} - - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[2].m_Name value: Effect objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[3].m_Name + propertyPath: m_EntityGroups.Array.data[2].m_Name value: Enemy objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[4].m_Name + propertyPath: m_EntityGroups.Array.data[3].m_Name value: Player objectReference: {fileID: 0} + - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} + propertyPath: m_EntityGroups.Array.data[4].m_Name + value: Drop + objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[5].m_Name value: Drop @@ -614,7 +614,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[0].m_InstanceCapacity - value: 10 + value: 16 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[1].m_InstanceCapacity @@ -622,15 +622,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[2].m_InstanceCapacity - value: 16 - objectReference: {fileID: 0} - - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[3].m_InstanceCapacity value: 10000 objectReference: {fileID: 0} + - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} + propertyPath: m_EntityGroups.Array.data[3].m_InstanceCapacity + value: 10 + objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[4].m_InstanceCapacity - value: 1 + value: 100 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[5].m_InstanceCapacity @@ -658,11 +658,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[2].m_InstanceExpireTime - value: 60 + value: 120 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[3].m_InstanceExpireTime - value: 120 + value: 60 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[4].m_InstanceExpireTime @@ -694,11 +694,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[2].m_InstanceAutoReleaseInterval - value: 60 + value: 30 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[3].m_InstanceAutoReleaseInterval - value: 30 + value: 60 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[4].m_InstanceAutoReleaseInterval diff --git a/数据表/Datas/EntityConfig_实体配置.xlsx b/数据表/Datas/EntityConfig_实体配置.xlsx index b6fb23dfcea43d8cff192b9281bf9d51c0004470..64a95e2c1f8d733db41aea6793fbcbd9192d61fc 100644 GIT binary patch delta 2814 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 Q0IQR-Co2Z+BLDyZ0F4wakN^Mx delta 2792 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 diff --git a/数据表/Datas/GlobalConfig_全局配置.xlsx b/数据表/Datas/GlobalConfig_全局配置.xlsx index 9fff49a9db86cdaa19855783a7bd339e25a63979..3c162060be192c09d59323065528f9883eb372f3 100644 GIT binary patch delta 7404 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 delta 6655 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 diff --git a/数据表/Datas/__enums__.xlsx b/数据表/Datas/__enums__.xlsx index c821c303334860032cc2947347315061c9055660..7d54eab0040af8f2417d25ac3920538e4bbcd330 100644 GIT binary patch delta 11641 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 delta 11487 zcmZ8{b9A3g*KTZEjcq$=Z08rFv5h8;?%1|%+eRC!VdFGSW1A<>^S$ew?>+O!>{)wX zd#=52u01n--y%KhQ^28V9XTs`lQO{>fZ{AuM0;3zZx_<)!UvqBu9}r%vWTzG_Z9|v zGzembgM9ZB9W<*~`9}ws)l~#J6q#sP0XQ+$H_Xz?f9P|IM!(fk)e$1|@dt`e>^{+B zhDLlJEV4(^{t^%cAuB+w5PP>t7}|j8t+GxvP!QB^U%e}=e~LoLy!nXYBsYr009@$1 z%v{3bWhI1r6=PA%`>b?A$^;a^K>rd@z>MtcA+B}-P~65t)HU9$2947w8R=wi_-myzX;0B;>QQ1!hIy&UbAdC-r;^x4cI8yon~l29v0 zc(>lS*2{wD%60dz?=RlB6N-Hv)@@~fPYd`K?_cw7OWu!%w%)(q&t<>2cQ3lH1;Ld~ z_`cWJZ}lOCrETc>0_%n_Q{KLT6f9GZ?e;r)f0uXuyy`pVbtT1y8S@h;hW$8wTUbW# z%{DdRWMO$W=&|~`6?ycYO2vG(?AQuOK>8+q@u>DS+*E0jANBD}jxl`-qF zem*@_^358yYoB*ve$QYN#c|A+*n?b!ROiR(W1Qo8pUAdHJJ3<~YP|J!;;@WRsTX~H zdCB(R*@yI%rf5)Jg`IH8{_S+0`^QM@#vRF7C-%dspijH^rZ~>x{QdOpM;^9Uf1J5b zhXP`+wtQcmFa6G1*2;jzFt6pM85M?C!?o_uODy(=Bq&Z-X6xq_m7X@4pBE~q;-!vU zHt3GX*9^Me0Ylp-6Bopk+rG2TBg^)KS%by*ReEjrl$*7Vd;LDUzE8ip`CfVfhpn?` zv{9pXJK;#L_ot=Jd-d`?1CM1K*#)kzQ{WJTTRp!Gv3>eH*53{cKfBdKO?NeaMpZeM zwGmH8T!nkm=lzv~Shv6K#-6Tdq8w)DURG6|A-{$49hfV#+;{VADN}}Nv%Yy0IblYv zyL&kh$jfxySRJOygpvLIVlsR-`l`lO*B?{L=zNk3!+g_9+Ky7rQl<8oO z*;3q9G(T_;aXBI%sOsq&d&iYsRKu0iNQ}YG`7^93!~SV&o4u;l`L?1{D$Q+KpmKoZ zY6_`Y|$>I??zv|U6%<%!2oP~+J!}bIlfX=sN_V)wuHjw+O`a{{$qJ!C8#$@PB z$?#3bwdJIw@EOL^Rle)8mnN%P!GLPJ*6_`fQw!F`sLC5fnd*1?i(rw+o*m$`1M*>^ z)4)7|2B=hHu00}cC!?_dY}h3tTFz~nzoAsp0J{TSQKKl^0DM(hOmV%tPco9ebfWR{ zIFMkxEgCwYxZX%p9r_m>fnirr#8{&nPDLA^g=wVr&p1RbZ)&`|e<1$hc=IgQA3o2x z?pAT4)?tlpHM!GTUfBe6~hZ?)_a z8Om|upwsq9itBh}HIlQo9_UOrV64>{)}BT92~LN^X{7uXoLG;qwP9j zIwfc7e_BxE{}KVrr+fBl`S#F1f+4-rX)(1AB&iXSAUw4J;j2eXWhE%iWP zZX74DjgApGXxyiuw5^Z$@bKa-)4}|255J}F)u3^-Fsf3Q4~le+e>7PC0feq2gab>nvwhkru)z^fC6mN&aAf9G_>7^iBE zpH&lMfUltfX@?ib#f6YY%s|VJcN&k1*`E=A$5gKaiAMKO#TRc>!^U5m zn~B~{Okgp|V^;n3!(`}ehXxakIHK>r!^(#zUyhl`9yM$LB4vDG$HRNwDvnI$+0UKq zsa5PUyPNj1y11eFokpv>ZW$YFb0bseozjt(8Fh@Z$6|{h+;*94X3|QZ$J!*e3ED-~ z1ketDXtDcFjj91qpA-4h{LVlE_c({0jWQ9WIh{gfZIG^5iO9{!*4ayKVfnKsZQVX* z|G%$guVE@9gO5?j%xVp!WJrY%#p;ob17{M02CNSnY$%c43BB3GoP&U#UlrqctrGc9s$jO zaRqvWDRq_kF^Vc*5PeomOdh_5vN=`7nCx>1W^?1&+XN}RZwZA)YO(q#M)yEby4d@) z41#!{7JOe>#pVIb%#B+4bcqUcK}c{ahOVp?Q^BAZWk@w>f;6JT+gz4E#15Mq@LVW~ z7vT(PSQ}Y5L4L(TpiQg(U+~-*x$;Idxeb|v1QAXeBWkuBKIMRNn^B%T_^iebDue4u zv@rMYi7vvI3i9NUD`?D&VyiYB5Hy*F39YxoPo)V;d+;w-9mB7`Ezk~l^a7y_w zuxUh3rFW{Ll5j$mKU8DQvs_RoJ+`>A!4xWWz&VddzM@?>u*m?NQ}q&Egfn!9bj=fn z!2oadc53qVSUpmLg?4sM;b7v+EyfVL1jf*O;!U9m$sBfv$(tX4)Y(ryBa7Zze9e09 zcT)I?F|4c%G+kVa#G_7=H&9xW9cK(-w@wj*(HoDadC`QVnmGJ(*8kWp-TPh~Sj*>1 zWc`#JgDu-dv@HJM36j<}WSe>)VgR`XvJ$EFNVqa%gK(UBTGe?L6Zow(QCzlsOO)hM zEz0J+^^%&6Dx3)^2F`xu^5`kZC4*y)4AhF^cu6S1?Ym`u;omG?l9MrPd$JP;m2h_J znYGoXX_RotH|mAIgvsn0?4li}w$)KRG5{07TB&Y}XY6q3i!_ntTEvJ~slbN?-Jly_ zI;`9ktjgyQeKPvP_7{UI{SEk2la|_Fa(I%EsqR)Jt6fl=0*fo5}TPPsTZ;T z)%s8*Nb*1Gqf3rUKBLd8J5h*2TBB6$QvLB+^4mAr4s$5-VfYnC%buWJf>kgvcE^!0 z+Q_zpEv5E7>XSkm!1PtY+@W*(6E#J&&CzT=JLo>ZB6tqoZJ4JU)cQ8(dk}0uVEtM;g;NqkFv->l5zrcVvp%#}A}It?8n1_VZaGz$BucNNz(!dd5E445|Gk)$uTMla#RYSO}RvYYXUBQNo7QoXkpr>&_^{tKQ65H{9UAz zI<3u6Us9i~5+`ztWuE@$`*MUvC#%!-k>$=}>fUnY#SxmUXA9L~HL$z*a}-{Owy+v@ zZi6iK!LBampZu(ahLsYmt%SpsXF_!fyA&B~km5daQ#T{XBsZPg5KTRBV3%Zt(ExA+ zBufh(S#dT#nMIsYt3$bCnm4V`6^0j1KZM~;utss|o9lRSU7nRU%GBk~WcPXXa#(zd z|0g&PF`^TQQ-S2`i1T0>`1k(b{KFW%rm|YTG!p+v{^PudX{1_At}v{GCV5SK za;g{g^p6L_INeDXr2&+Bg3kxOa)#D)$iTsjT+VbvS2J*FA3^*!Se8ojW1t3)9 zA)&Cqz`)?Z!jyJJ{O3|_QlP=WKsf&?a0rszF;D>?4>NDhORAMIxuV6%GmZg!!jLFj zne3NWkjL#OYLg^EDE(|<-^$KP0HBH$Bmi7MQ4wSLqyYYKvAI;$KpS%~i2~{ECX2l(tr6v3#4n;D{oWU>X=NygGcmf(~cd zUN=poz9n?kf+KI)?J%s{yjA<=Jgs2`jzA3vS7^;>7?>TG$FBd$uIay@|1ymVFOk69 zqn`MC@D*oW5Xuynn053se23<*9`WUu2HM<%pbg`lJ6e2MS%so=38!yl42h+P9-l7zJgp<&68jjX@I#KO3W2NJbM_MVjdo4wu{{(yxJb+imQ~9}lRZhxQGe=vE)`-(QvhaRwswK0pdAWQ@rTR**5NAq32lwl;4gYz1162y(Ls=ooa(C!8FG7W`HB2sm9ca1{oZaig6Wk$ zc1xAfT&G~#an-1^n$6j1BZ~pt$eaBz)H4+NoXbvT2Wl&%|{EG*>uN9e)v)(?kqpgMS z%ubvZ$;P}?b22kLBVvUIJs<_JxK|8s=|)uy)-|NKXzsGYz5wJQ!o)e^EAX`Ofhe?t zBm|R^7X-3daBMRxXoro^2C+6#q+Xcq#1;kN#=>vQp$*w^YpkibUEI+cozEX#_q{!y z{Ixit)F1zb!RAFjpAg4CPA%LNDe{ct48tPR;QI|VY_(Z%41OcL!YarIMZN-&X0B$EQ@_K1}1fMl|^{=;S*PNpd`wXT6!-2;wbuvzJkt)DXP-QU3NT`Jz$&>8euw+`)tS()~8J0i#AHZ@>b{Bu& z5DdYA;Ys zLYB^5xuy(?X?R(Kk1wz*yh3XN<+rn^9G0@L^moz?wK2FVwOr}r!)DN8W7R6C7(bO? zv8N2Qt|;R7;-hAo))27 z53UY!% zLnQ9b#yJ-a@=uw3@pN$}VTFs^S^8`^1-yo8Vp$MAj@b(}P za{7@4v0%Z#t{IbXiKsCAC*}1j*y8Es7@E)nAgsy6@RF2>hyWM_+31c}{F&NU7XCXI z=M$Y>JXD-SDvPiCKG!)Dli7T&-pYb%Q=}k}s#Hwslb(AjK{!#Pxy(YgCx-*@Bc@eE^wYyMY}EdnFC*p} zJqS=ri^ybmGQd)3XQzNBDq2Fxcd}hb8c;@EP4cP#)Tym zh!KmCz^P1oR)S~W#2Y+tRK}xte|aW5&V6P(ce=(Iy_JJ_A>wc+V-AbSr=;+LG8r61 ztau5Pn;p`4AIS?_+rH##IPQ|DxAOOe+4G7>0!Gb&eITAnr$SZVHzsvy=}XozZPBVl ziH6k5Lu~f&&mU_UBO?1w)I5X8SlGGlOIvSLYZg;e(a;#-35W3F4w{A;QOcEC45(c3b=Bu`?WFo3oUP+WQT)aCra-1aB8L;lMY)znczfsMEmP(P|D1*C&=^~(t6+A1rfd_4M8 zhbW7(7L!gtSC;NJ-8mL|GP*%Z4vWKdPbf}bnv26H6csQ(d&KmJeEdBR@MLST)kC1e zo>s!LRT4rQ{|5Dv={Nfd6_*8P!PZRm7!UaFl>&q}Q$=&Qu{u`2u;5g&XNhtfXV+Qy zz1M@?OTI8R^t(M`)q#)|PaPY#-NY@iZ<2i`22DIfpu9u7sT$PJ9$7LMa9VFS#2blB zj1Rzs_ngwiWIMo0+r~Vj_l)Yn+{E^N(+Ue=Q(-1B4FBvxkiM)KF6gim(9EyA6KUC& z8U*f^Tpsg~5-ufAO=){R8DDN`y3JIUd&>Ez%2*YtE37u2fW8Tcm<4y^al86Q)-IY= z9jWaFW?4y=ScHCw@3+M1i6gxj7p&1+)1^RUOSKY!NpCapNaK3%%rI$~h0kpkCpFF; zP>gyS>vH$Ks)ZHXU&MaW@ zxnxLrY*#ocoH-v1^_-ONwhm{N4K?wU{0v5EPAk5D51o+u``8!<4(iu~{>J%W{SpJr zaij!Y&y*v|p0{;M2{r0p0SoUkZdV&@(IT>A;T#^(%;@weE8q!HEYWj9?3t4iN_9q% zk<2_ZNryVa>)AX5v&dBbhilM|xbVQqp?BCm^L!Lc{0q;rDacJ6=oxp3(Uc4xrNgF- zqd&?qqnFUkD1AVCsvmS8lA~(9QQU87HqdMs(e+owHmjCV7CW9LhOK;qmb%lujL9T2;9C=fW*h&;rB z84exv3KN9R47-0an6#64@E7&B&0B*L4*Ub;W2T|YzILGFKC#+_5ba*{+&YIq0Th{I zpG@AgRnc{*p|;;DvJiVOIF_mq4!g87iP4z^BU5o&It$nxcqJSo#U>m(VID^oIL5>1 z{19I_H#+H=em2=7QPDsmG*CLF=%Kjj79V8ln?5F4Ro<`U)wUNSLbAD#cWUL0o``2Y zXH?wdDIKAIG$APW5;5$T+h*6uAt{BAda7+zhAqTQvV47{Eu*(-q`Gwz{5P{D?Rf9g zHT_0HPbKM-lePbDqv_A?K;a3_7ZiUY!DYodYib|D(p<&XD?e#=HK6C|foAHne)VG{ z@m#dGaRVBopp#1vMtqV#j=7YkK(KyO*-us9cP85r#lo;6+2e^B8K+>X`y=uq*Z09d zRj*;j_pxW%I#!yx6b*Lu6<<{qm(_$X7KzJVhF~tMZV^m%oWC2p?nN1xOc^(+>+_`t zGfLVX8-yIOY7!1`RRLq}Ja+UA(o3z1paz!PkPw`NC9TfJMg;HCIdFLDmJ|+TeT1>z zh(?&%ADq91eu`-G=6y0zFeSA$RA$yMHuq`koQ)F=4nQBlhJv(e;GwR2ZhX_i>9DjN)iU0HFcXAUr_(MnyIN z7^sLkD;LA<`8(4oK*qyB#tnn!^ruk!cya*HhGtWYsEmqsdBB9vz(AJo_)edc986yd zDuZuTyhbVub_TKYUSXl2OETu?!nM-<>7-2HBqJEtVw(Vs;M7J+z)pWMSoFxKgeCgv zlV`spRQxLe2KE9oOeSL8u1S}49x1U_d+^3U6^4DjAHS|6-YmsmrA@QSB=nHtC>a`1 zqeVaDSJ}DJj>BqHu+u4#>2Vmw=%kHdmq|59;G9V$r67}w6DzeoD1>1gi4}&*hwGSv z(M#>$QW|PTKl{|3C8sU2`YNG57Evlbz;)2+u?Jw&wTCr;{fNf*Z8J|4E7}{p>Aau{ z5ZAPkbF;HE-zA^XF?5Z0r618FuxbJH<2i$b((J_3?(_P#fTz3P6U!yioT?KP_G6~M z-^2_s_9Bf`3aXp4-)m*+vhJS(=W@K_S-ubmd>3!%GA@WUFyooga|#+?K^$Ld2$eU_ zvTu@c8CG?pal$SdK;)>qnt%w!X<&OW5BP=>6hRcGIqekPOH4vGa1Ys7kEaE|LnK0} zTknaBt~MxiCArS&`Bu2G1-?y6lEBp5_1n3f=>BYb(fxUp-ZHRVtz-MmugKlu+mPzS zyC<5kW%OG^-CFyKV#KDH^4_URb9BpJDb#d| ztuxe`H!N98l;DoraDz~%b%{%K5mN7xZ)h3rxL~fSCW6ap$zNlP04X|`cuvJ3+3vMf zw7Uqal}Gw#0lyoy+Dv9_c8wTQJ5@(t_pdM@%SF^B#zD_l<#*~0)8Tkqoj%=Gq9NH9 z02LAb67e(aQZf8A|(hK`DO2`-WHb8{?KD?`OMBy$uP~qmbw^- zP@pJAcE9heS@jo-T0c3py@PTYWYVfh@TN-RA1=b!HIAW9tx%lqFSg7`Wm@&#s0pkT zQycqpW#h?k%3ePcdw()AH+CJT^zQb4)6_Pzou-PG@5;VQq)(UBXU;2XSj8NI?2bI* z52?Wd;zRe$lv?J-xhDNHfREC^Qe^|knA+1-XZ3gD-yOPWCq;+v4M9|Q#rKPD@=(;G zNIQW~!|W4%geUhledeHa`NWR>mscIOo%eFq10~HVJ3)SWh7XQx_h>O)qouMo%(ex3 z7q5)d;XC+FPX-Mss`6<2aiU>kD(*;BUZ?e;vv2_!Q#XDUgvHYgM+H?P?KKKs(uP}S-Yv0i&0#CCKH z%a5c$;9!YGm#x`>r&IaMa7GEhk+HS3(9%<7Pt;J|VIkBJaKB26Vbs{Lr>lxh(}Gsj zuR}Wvz#C&5t?%n@t))dcMYN!AK~d8}o+z(bJh3z2;uiXS6cPnTnV4xzny`QbDeL{S zY9{7;2o#3w6l=VUO=y7r7iZfY^S!74rJ5{n30w?Bw4wn1afv9lz#EO`2dj5;0S}N+ z<3OTJ^ePpZ2PZ^A?@HYWOW_>T`;^GQD(HG{@0j`2A0V1xe{!0n{DUBgEb|X<{atg< zDo?YJZE?m$%bxl}UNA=Lwo!|H)%MAd4L&3LHhSK?O-nLPMVWkYISmCB^kP_LQX&}E z{jHMh4nXitm{1^cUS)m9R*n!|lAjP=^h1ug*qzXR%G|L2Ei>+oWo?fHr?!Tt!Z`F@ zGvtLSP(vbNT1p~*UP>ZiNlJ#h64@sZ?O}`RhQp-%KIV{BX2D27(#36rJW#i5A9YPl z{-y<2gWZmeoQW4*va_EoFgZXR%ep7ij6w$%aH2W-`74}uel;9U?L*?UG&m|6lSudv zj=BVoj(2dX3j9rkW6Y6HT3X_ZpiM2RtlyML<5&9&!y+9{&OGsVZ4z6VefbzoS$T#crslmGg>}X<`}&UQYw#UFwIerSdxq1RHejbRfwwxqMP3O`ZzFER?R!n)y>V zMy-YFBt&b*AImJ?H1MhLK^WCi!HBwm)(CG#)r_W!sc~7NV3!#$>EqWi5OvKem3l_A z4S9-j6fM9&8r25<@Q?_cXo5uEW&=Wx1FM(csgW#sy1`b`^3DN?<|a& z{jV0mldEkC<=;tSJ^mREujC;ihh*7-4OTszWeV#~hdIbvZRD_Kb!D@BLOPhkbbHob zcK{2wiPnu?&uMm9K0ic>_`~L^Q$+VAL+{sCo851BR<})I0BSF81X))EwsA(K8xz7B z4DH5Xya1gOk(@jeHhAk0sr20HJaSt9sjsCQp7))9$@TEgaZk}!v|Ig5VyuY@iz{j^ zpwz0vkKX?X!kKcTq2*HX73}V+y{m*gD^P`Ym)hyw5n}3QVKtkc1t(I0xj$qoQZ_Lp zT1@>COsv)eAQ;E%iw$YzY5H!rLyu4Yt&dFBdb&_?bvFegQ)k}|LNG*s@;vjBka6pB zmD{0XmLK|;tQKp9a849lctk#GL$~2zBlhSZ4Hqzw`NObUUG1P*r3Px~@lUCO;ZOK==ADaGv(>AzP!oY*L51w9uw*?;Q`Fl>#H~xk%8Z(^KRa^Dbp_~8M-E%X9X%`Jd?tY z4|@kuz)xvumM!0FN(E&LUp%2t%^~K*@5hd1sGC>NiST$*VDhDbo;iZA$0w(4vB3NG zLr>qco0%Nc+?=7l#QZa=(&vXDWzjTK^oZG48f9UqKe6Y+J0%HkEIf z9$Hj62yt*LPvqo$PZJYG(%|rM@ae(0@kD^<{liS0B`Uzv1)M*;&OquC<6;}< z>yJuX9S*`r;o7aEjlKR2`rVcHxI3yu9E^CMoW+V{8J7q9=TR4u+82u4B993A&o@pG z5NyTU^Bl#JCxk5H$p{M8PWOE~9Q%9*{@qfUB**V!bFA*OMLjeHZ&q-7d2$~ac8LVH zpr((z_&Kz!~1ZV1=F?%*c&2CeOMO7I}Ne`T<&CCP1#1Keh$we^{F? zPO?cy{|$9-DP%O}pO`4xm?h3^_r8&ETtISVGm(kh_W}LHFu{IkJ#6=OofiQP14H2h zzlUyu89L_dxKnhoy_B|T-v#qPA@YT$`*QgwC8}skXKtY&((1pmT+zlV-9A2$m;!RmI*&-Bh;9``g%yz|#qCz(dSGtZD<#LT{zpyKS z-?#oP;IBYF4WOhY%kiTJxoY05TAZnR)>dzWZ#@jY{qC{rB-6PjMI3XcJihg6VrEFm z6u6^hqu!R^BIIqfMA9O4qpCP(IO^Ob+JEi^uri6kZ~4exQZ8lf_>H%V<-4OP(haXc z?k#cRqg!TZFfEn@-m*IqM*HkE&L^&QMwBh40iqU0GxLV#NhhuWxaDM(FX}u35&9Sf zPa4i{ncYHBX);S9eQRVa83kp|P}gUy`$xx+_BQ-HK_qPmC(rJBVOl{T$J3ipQ9@Q= z_FC=>kAohHd5eb8*2a8FJ}1uMR0~Z#F=p;f$CqgiFXNA=zcq{QS-X?8#}v z$jrtdbg`XExhq89Nu28x^~O8+4a3Wo8th7X2H=Pz#C*#}H3%?~a|eXF*o%4K3kE4Q4~mw+~xm-(h6>n)vt{pTvA~#Le;pYtHs}C zQ?kO?mN}xUQHPu2RR!rT*R^;kUXIXBRrU(wpVa-y?zc#7&MC6Kc}a1*BYe;+p^Lq# z$)I&mHtzuwXn*BTKzsvr-oJvsOa7kgjB2d;G zU|>nc{5VNFyaW(297(UdiT^p?o6H9SKTA5~6M;2>`nP~g+7Td1Qs75{XyHuK=jVc0 z=S-^MB1)>`MS)4;(%tAqN_$y(^7VgWRw V|K5}S-@r8V4