From 399b933a384422d4f93444b034ba80224b2cd539 Mon Sep 17 00:00:00 2001 From: hyperion51 Date: Thu, 28 May 2020 04:50:40 +0100 Subject: [PATCH] Mining New Mining Controller Behavior and Mining Tool Behavior, tweaked a bunch of asteroid related stuff --- .../.idea/contentModel.xml | 3 + .../Scripts/ServerShared/Agents/AgentTask.cs | 2 + Assets/Scripts/ServerShared/Agents/Mining.cs | 24 ++ .../ServerShared/Agents/Mining.cs.meta | 11 + .../ServerShared/Behaviors/Behaviors.cs | 2 + .../ServerShared/Behaviors/Controller.cs | 55 +++-- .../ServerShared/Behaviors/FactoryBehavior.cs | 2 +- .../Scripts/ServerShared/Behaviors/Hitscan.cs | 2 + .../Behaviors/MiningController.cs | 130 +++++++++++ .../Behaviors/MiningController.cs.meta | 11 + .../ServerShared/Behaviors/MiningTool.cs | 71 ++++++ .../ServerShared/Behaviors/MiningTool.cs.meta | 11 + .../Behaviors/PatrolController.cs | 3 +- .../Scripts/ServerShared/Behaviors/Switch.cs | 2 +- .../Behaviors/TowingController.cs | 5 +- Assets/Scripts/ServerShared/Entity.cs | 24 ++ Assets/Scripts/ServerShared/GameContext.cs | 212 ++++++++++++++++-- Assets/Scripts/ServerShared/GlobalData.cs | 15 ++ Assets/Scripts/ServerShared/ZoneData.cs | 11 +- Assets/Scripts/ServerShared/ZoneGenerator.cs | 14 +- Assets/Scripts/StrategyGameManager.cs | 54 +++-- Economy.Shared.Unity.csproj | 3 + 22 files changed, 609 insertions(+), 58 deletions(-) create mode 100644 Assets/Scripts/ServerShared/Agents/Mining.cs create mode 100644 Assets/Scripts/ServerShared/Agents/Mining.cs.meta create mode 100644 Assets/Scripts/ServerShared/Behaviors/MiningController.cs create mode 100644 Assets/Scripts/ServerShared/Behaviors/MiningController.cs.meta create mode 100644 Assets/Scripts/ServerShared/Behaviors/MiningTool.cs create mode 100644 Assets/Scripts/ServerShared/Behaviors/MiningTool.cs.meta diff --git a/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml b/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml index 595b40391..a7599e1d4 100644 --- a/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml +++ b/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml @@ -1245,6 +1245,7 @@ + @@ -1260,6 +1261,8 @@ + + diff --git a/Assets/Scripts/ServerShared/Agents/AgentTask.cs b/Assets/Scripts/ServerShared/Agents/AgentTask.cs index e6830122a..87518d9d7 100644 --- a/Assets/Scripts/ServerShared/Agents/AgentTask.cs +++ b/Assets/Scripts/ServerShared/Agents/AgentTask.cs @@ -8,6 +8,7 @@ using UnityEngine; [MessagePackObject, Union(0, typeof(StationTowing)), + Union(1, typeof(Mining)), JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter))] public abstract class AgentTask : DatabaseEntry { @@ -25,6 +26,7 @@ public abstract class AgentTask : DatabaseEntry public enum TaskType { + None, Mine, Haul, Tow, diff --git a/Assets/Scripts/ServerShared/Agents/Mining.cs b/Assets/Scripts/ServerShared/Agents/Mining.cs new file mode 100644 index 000000000..c5280d352 --- /dev/null +++ b/Assets/Scripts/ServerShared/Agents/Mining.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using JsonKnownTypes; +using MessagePack; +using Newtonsoft.Json; + +[MessagePackObject, + JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter))] +public class Mining : AgentTask +{ + [IgnoreMember] public override TaskType Type => TaskType.Mine; + + [JsonProperty("asteroids"), Key(4)] + public Guid Asteroids; + + // public StationTowing(Guid zone, Guid station, Guid orbitParent, float orbitDistance) + // { + // Zone = zone; + // Station = station; + // OrbitParent = orbitParent; + // OrbitDistance = orbitDistance; + // } +} \ No newline at end of file diff --git a/Assets/Scripts/ServerShared/Agents/Mining.cs.meta b/Assets/Scripts/ServerShared/Agents/Mining.cs.meta new file mode 100644 index 000000000..1661dce42 --- /dev/null +++ b/Assets/Scripts/ServerShared/Agents/Mining.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e4409ff5e14e06468f205574cc67a3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ServerShared/Behaviors/Behaviors.cs b/Assets/Scripts/ServerShared/Behaviors/Behaviors.cs index d4c1bbcc3..db2967c21 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Behaviors.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Behaviors.cs @@ -89,6 +89,8 @@ public abstract class PersistentBehaviorData Union(22, typeof(VisibilityData)), Union(23, typeof(ThermotoggleData)), Union(24, typeof(EnergyDrawData)), + Union(25, typeof(MiningControllerData)), + Union(26, typeof(MiningToolData)), JsonConverter(typeof(JsonKnownTypesConverter)), JsonObject(MemberSerialization.OptIn)] public abstract class BehaviorData { diff --git a/Assets/Scripts/ServerShared/Behaviors/Controller.cs b/Assets/Scripts/ServerShared/Behaviors/Controller.cs index dd6e16d4a..e75b5e01f 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Controller.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Controller.cs @@ -41,12 +41,12 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh protected bool MatchVelocity; protected List Path; protected Guid Task; + protected bool Moving; private GameContext _context; private Entity _entity; private ControllerData _controllerData; private MovementPhase _movementPhase; - private bool _moving; private Action _onFinishMoving; public ControllerBase(GameContext context, ControllerData data, Entity entity) @@ -67,7 +67,7 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh public void AssignTask(Guid task) { Task = task; - _moving = false; + Moving = false; } public bool Update(float delta) @@ -79,18 +79,17 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh return false; // No task is assigned, but we're not home, go home! - var homeEntity = _context.Cache.Get(HomeEntity); - if (!_moving) + if (!Moving) { - if (Zone == homeEntity.Zone) MoveTo(homeEntity, true, () => - _context.SetParent(_entity, homeEntity)); - else MoveTo(homeEntity.Zone, () => MoveTo(homeEntity, true, () => - _context.SetParent(_entity, homeEntity))); + GoHome(); } } - if (_moving) + if (Moving) { + if(_entity.Parent!=Guid.Empty) + _context.RemoveParent(_entity); + var distance = length(TargetPosition() - _entity.Position); if (MatchVelocity) { @@ -108,8 +107,8 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh VelocityMatch.Clear(); VelocityMatch.OnMatch += () => { - _moving = false; - _onFinishMoving(); + Moving = false; + _onFinishMoving?.Invoke(); }; } } @@ -124,7 +123,7 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh Locomotion.Update(delta); if (distance < _controllerData.TargetDistance) { - _moving = false; + Moving = false; _onFinishMoving(); } } @@ -133,13 +132,39 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh return true; } + public void FinishTask() + { + if (Task == Guid.Empty) + return; + + var task = _context.Cache.Get(Task); + _context.Cache.Delete(task); + _context.Cache.Get(_entity.Corporation).Tasks.Remove(task.ID); + Task = Guid.Empty; + } + + public void GoHome(Action onFinish = null) + { + var homeEntity = _context.Cache.Get(HomeEntity); + if (Zone == homeEntity.Zone) MoveTo(homeEntity, true, () => + { + _context.SetParent(_entity, homeEntity); + onFinish?.Invoke(); + }); + else MoveTo(homeEntity.Zone, () => MoveTo(homeEntity, true, () => + { + _context.SetParent(_entity, homeEntity); + onFinish?.Invoke(); + })); + } + public void MoveTo(Entity entity, bool matchVelocity = true, Action onFinish = null) { TargetPosition = () => entity.Position; TargetVelocity = () => entity.Velocity; MatchVelocity = matchVelocity; _movementPhase = MovementPhase.Locomotion; - _moving = true; + Moving = true; _onFinishMoving = onFinish; } @@ -149,7 +174,7 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh TargetVelocity = () => float2.zero; MatchVelocity = matchVelocity; _movementPhase = MovementPhase.Locomotion; - _moving = true; + Moving = true; _onFinishMoving = onFinish; } @@ -159,7 +184,7 @@ public abstract class ControllerBase : IBehavior, IController, IInitializableBeh TargetVelocity = velocity; MatchVelocity = true; _movementPhase = MovementPhase.Locomotion; - _moving = true; + Moving = true; _onFinishMoving = onFinish; } diff --git a/Assets/Scripts/ServerShared/Behaviors/FactoryBehavior.cs b/Assets/Scripts/ServerShared/Behaviors/FactoryBehavior.cs index 6a5e06d89..8369525bc 100644 --- a/Assets/Scripts/ServerShared/Behaviors/FactoryBehavior.cs +++ b/Assets/Scripts/ServerShared/Behaviors/FactoryBehavior.cs @@ -109,7 +109,7 @@ public class Factory : IBehavior, IPersistentBehavior else { var simpleCommodityData = blueprintItemData as SimpleCommodityData; - var simpleCommodityInstance = Entity.Cargo.FirstOrDefault(i => i == simpleCommodityData.ID); + var simpleCommodityInstance = Entity.Cargo.FirstOrDefault(i => Context.Cache.Get(i).Data == simpleCommodityData.ID); if (simpleCommodityInstance == Guid.Empty) { var newSimpleCommodity = new SimpleCommodity diff --git a/Assets/Scripts/ServerShared/Behaviors/Hitscan.cs b/Assets/Scripts/ServerShared/Behaviors/Hitscan.cs index e81c4ffc1..3bc66fd25 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Hitscan.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Hitscan.cs @@ -24,6 +24,7 @@ public class Hitscan : IBehavior private float _firingVisibility; public BehaviorData Data => _data; + public float Range { get; private set; } public Hitscan(GameContext context, HitscanData c, Entity entity, Gear item) { @@ -36,6 +37,7 @@ public class Hitscan : IBehavior public bool Update(float delta) { // TODO: Implement hitscan weapons! + Range = Context.Evaluate(_data.Range, Item, Entity); return true; } diff --git a/Assets/Scripts/ServerShared/Behaviors/MiningController.cs b/Assets/Scripts/ServerShared/Behaviors/MiningController.cs new file mode 100644 index 000000000..273765f5a --- /dev/null +++ b/Assets/Scripts/ServerShared/Behaviors/MiningController.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using MessagePack; +using Newtonsoft.Json; +using Unity.Mathematics; +using static Unity.Mathematics.math; + +[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)] +public class MiningControllerData : ControllerData +{ + public override IBehavior CreateInstance(GameContext context, Entity entity, Gear item) + { + return new MiningController(context, this, entity, item); + } +} + +public class MiningController : ControllerBase, IBehavior, IPersistentBehavior, IInitializableBehavior +{ + public override TaskType TaskType => TaskType.Mine; + public BehaviorData Data => _data; + + private MiningControllerData _data; + private GameContext _context; + private Entity _entity; + private Gear _item; + private bool _taskStarted; + private int _asteroid = -1; + private MiningTool _miningTool; + private Switch _toolSwitch; + + public MiningController(GameContext context, MiningControllerData data, Entity entity, Gear item) : base(context, data, entity) + { + _context = context; + _data = data; + _entity = entity; + _item = item; + } + + public new void Initialize() + { + _miningTool = _entity.GetBehaviors().First(); + _toolSwitch = _entity.GetSwitch(_miningTool); + base.Initialize(); + } + + public new bool Update(float delta) + { + var miningTask = _context.Cache.Get(Task); + if (miningTask != null) + { + if (!_taskStarted) + { + NextAsteroid(); + _taskStarted = true; + } + else + { + if (!Moving) + { + if (_entity.OccupiedCapacity < _entity.Capacity - 1) + { + var asteroidTransform = _context.GetAsteroidTransform(miningTask.Asteroids, _asteroid); + if (length(_entity.Position - asteroidTransform.xy) - asteroidTransform.w > _miningTool.Range) + { + _context.Log($"Mining Controller {_entity.Name} is out of range, moving to target asteroid."); + MoveTo(() => _context.GetAsteroidTransform(miningTask.Asteroids, _asteroid).xy, + () => _context.GetAsteroidVelocity(miningTask.Asteroids, _asteroid)); + _toolSwitch.Activated = false; + } + else + { + if (_context.AsteroidExists(miningTask.Asteroids, _asteroid)) + { + _miningTool.AsteroidBelt = miningTask.Asteroids; + _miningTool.Asteroid = _asteroid; + _toolSwitch.Activated = true; + } + else NextAsteroid(); + } + } + else + { + _context.Log($"Mining Controller {_entity.Name} is out of cargo space. Returning home to offload cargo."); + GoHome(() => + { + var homeEntity = _context.Cache.Get(HomeEntity); + if (!_entity.Cargo.ToArray().All(ii => + _context.MoveCargo(_entity, homeEntity, _context.Cache.Get(ii)))) + { + _context.Log($"Mining Controller {_entity.Name} has run out of cargo space in parent. Closing Task."); + FinishTask(); + _taskStarted = false; + } + }); + } + } + } + } + return base.Update(delta); + } + + private void NextAsteroid() + { + var miningTask = _context.Cache.Get(Task); + _asteroid = _context.NearestAsteroid(miningTask.Asteroids, _entity.Position); + _context.Log($"Mining Controller {_entity.Name} selecting new asteroid."); + } + + public PersistentBehaviorData Store() + { + return new MiningControllerPersistence + { + Task = Task + }; + } + + public void Restore(PersistentBehaviorData data) + { + var miningControllerPersistence = data as MiningControllerPersistence; + Task = miningControllerPersistence.Task; + } +} + +public class MiningControllerPersistence : PersistentBehaviorData +{ + [JsonProperty("task"), Key(0)] + public Guid Task; +} \ No newline at end of file diff --git a/Assets/Scripts/ServerShared/Behaviors/MiningController.cs.meta b/Assets/Scripts/ServerShared/Behaviors/MiningController.cs.meta new file mode 100644 index 000000000..31d1d79bb --- /dev/null +++ b/Assets/Scripts/ServerShared/Behaviors/MiningController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b476455dca4333e4da76388aae46ac5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ServerShared/Behaviors/MiningTool.cs b/Assets/Scripts/ServerShared/Behaviors/MiningTool.cs new file mode 100644 index 000000000..cf3101d1e --- /dev/null +++ b/Assets/Scripts/ServerShared/Behaviors/MiningTool.cs @@ -0,0 +1,71 @@ +using System; +using System.Linq; +using MessagePack; +using Newtonsoft.Json; +using Unity.Mathematics; +using static Unity.Mathematics.math; + +[InspectableField, MessagePackObject, JsonObject(MemberSerialization.OptIn), Order(10)] +public class MiningToolData : BehaviorData +{ + [InspectableField, JsonProperty("dps"), Key(1)] + public PerformanceStat DamagePerSecond = new PerformanceStat(); + + [InspectableField, JsonProperty("efficiency"), Key(2)] + public PerformanceStat Efficiency = new PerformanceStat(); + + [InspectableField, JsonProperty("penetration"), Key(3)] + public PerformanceStat Penetration = new PerformanceStat(); + + [InspectableField, JsonProperty("range"), Key(4)] + public PerformanceStat Range = new PerformanceStat(); + + public override IBehavior CreateInstance(GameContext context, Entity entity, Gear item) + { + return new MiningTool(context, this, entity, item); + } +} + +public class MiningTool : IBehavior +{ + public Guid AsteroidBelt; + public int Asteroid; + + private MiningToolData _data; + + private Entity Entity { get; } + private Gear Item { get; } + private GameContext Context { get; } + + public BehaviorData Data => _data; + public float Range { get; private set; } + + public MiningTool(GameContext context, MiningToolData data, Entity entity, Gear item) + { + _data = data; + Entity = entity; + Item = item; + Context = context; + } + + public bool Update(float delta) + { + Range = Context.Evaluate(_data.Range, Item, Entity); + var asteroidTransform = Context.GetAsteroidTransform(AsteroidBelt, Asteroid); + if (AsteroidBelt != Guid.Empty && + Context.AsteroidExists(AsteroidBelt, Asteroid) && + length(Entity.Position - asteroidTransform.xy) - asteroidTransform.w < Range) + { + Context.MineAsteroid( + Entity, + AsteroidBelt, + Asteroid, + Context.Evaluate(_data.DamagePerSecond, Item, Entity) * delta, + Context.Evaluate(_data.Efficiency, Item, Entity), + Context.Evaluate(_data.Penetration, Item, Entity)); + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/Assets/Scripts/ServerShared/Behaviors/MiningTool.cs.meta b/Assets/Scripts/ServerShared/Behaviors/MiningTool.cs.meta new file mode 100644 index 000000000..9985f36a2 --- /dev/null +++ b/Assets/Scripts/ServerShared/Behaviors/MiningTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a7eebfaae57b2304580ef7e4a2740993 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ServerShared/Behaviors/PatrolController.cs b/Assets/Scripts/ServerShared/Behaviors/PatrolController.cs index 1d834feae..3c7121d18 100644 --- a/Assets/Scripts/ServerShared/Behaviors/PatrolController.cs +++ b/Assets/Scripts/ServerShared/Behaviors/PatrolController.cs @@ -17,7 +17,7 @@ public class PatrolControllerData : ControllerData public class PatrolController : IBehavior, IController, IInitializableBehavior { - public TaskType TaskType => TaskType.Tow; + public TaskType TaskType => TaskType.None; public bool Available => false; public Guid Zone => _entity.Zone; public BehaviorData Data => _data; @@ -26,7 +26,6 @@ public class PatrolController : IBehavior, IController, IInitializableBehavior private GameContext _context; private Entity _entity; private Gear _item; - private Guid _towingTask; private Locomotion _locomotion; private Guid _targetOrbit; diff --git a/Assets/Scripts/ServerShared/Behaviors/Switch.cs b/Assets/Scripts/ServerShared/Behaviors/Switch.cs index d52ed7502..88dee2a6a 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Switch.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Switch.cs @@ -22,7 +22,7 @@ public class Switch : IBehavior public BehaviorData Data => _data; - private bool Activated { get; set; } + public bool Activated { get; set; } public Switch(GameContext context, SwitchData data, Entity entity, Gear item) { diff --git a/Assets/Scripts/ServerShared/Behaviors/TowingController.cs b/Assets/Scripts/ServerShared/Behaviors/TowingController.cs index c7e5dfbcb..163ecf1c3 100644 --- a/Assets/Scripts/ServerShared/Behaviors/TowingController.cs +++ b/Assets/Scripts/ServerShared/Behaviors/TowingController.cs @@ -40,9 +40,6 @@ public class TowingController : ControllerBase, IBehavior, IPersistentBehavior var towingTask = _context.Cache.Get(Task); if (towingTask != null) { - if(_entity.Parent!=Guid.Empty) - _context.RemoveParent(_entity); - if (!_taskStarted) { MoveTo(towingTask.Zone, () => @@ -66,7 +63,7 @@ public class TowingController : ControllerBase, IBehavior, IPersistentBehavior var orbit = _context.CreateOrbit(towingTask.OrbitParent, _entity.Position); target.OrbitData = orbit.ID; - Task = Guid.Empty; + FinishTask(); _taskStarted = false; }); }); diff --git a/Assets/Scripts/ServerShared/Entity.cs b/Assets/Scripts/ServerShared/Entity.cs index cab53fb2d..55bc9bd8b 100644 --- a/Assets/Scripts/ServerShared/Entity.cs +++ b/Assets/Scripts/ServerShared/Entity.cs @@ -45,6 +45,15 @@ public abstract class Entity : DatabaseEntry, IMessagePackSerializationCallbackR [IgnoreMember] public float OccupiedCapacity { get; private set; } [IgnoreMember] public float SpecificHeat { get; private set; } [IgnoreMember] public float Visibility => VisibilitySources.Values.Sum(); + public float Capacity + { + get + { + var hull = Context.Cache.Get(Hull); + var hullData = Context.Cache.Get(hull.Data); + return Context.Evaluate(hullData.Capacity, hull, this); + } + } [IgnoreMember] public string EntryName { @@ -168,6 +177,21 @@ public abstract class Entity : DatabaseEntry, IMessagePackSerializationCallbackR return -1; } + public int GetSwitch() where T : class, IBehavior + { + var s = Behaviors + .FirstOrDefault(x => x.Value.Any(g => g is Switch) && x.Value.Any(g => g is T)) + .Value.First(g => g is Switch) as Switch; + return Switches.FirstOrDefault(kvp => kvp.Value == s).Key; + } + + public Switch GetSwitch(IBehavior behavior) + { + return Behaviors + .FirstOrDefault(x => x.Value.Any(g => g == behavior)) + .Value.First(g => g is Switch) as Switch; + } + public void AddHeat(float heat) { if (Parent != Guid.Empty) diff --git a/Assets/Scripts/ServerShared/GameContext.cs b/Assets/Scripts/ServerShared/GameContext.cs index 3c507e90e..9fba85edf 100644 --- a/Assets/Scripts/ServerShared/GameContext.cs +++ b/Assets/Scripts/ServerShared/GameContext.cs @@ -30,7 +30,10 @@ public class GameContext private Dictionary _orbitVelocities = new Dictionary(); private Dictionary _orbitPositions = new Dictionary(); private Dictionary _previousOrbitPositions = new Dictionary(); - private Dictionary _asteroidPositions = new Dictionary(); + private Dictionary _asteroidTransforms = new Dictionary(); // x, y, rotation, scale + private Dictionary _previousAsteroidTransforms = new Dictionary(); // x, y, rotation, scale + private Dictionary<(Guid, int), float> _asteroidRespawnTimers = new Dictionary<(Guid, int), float>(); + private Dictionary<(Guid, int), float> _asteroidDamage = new Dictionary<(Guid, int), float>(); private Dictionary _asteroidPositionsUpdated = new Dictionary(); private Guid _forceLoadZone; private Type[] _statObjects; @@ -104,7 +107,7 @@ public class GameContext : float2.zero; } - foreach (var asteroids in _asteroidPositions.Keys) + foreach (var asteroids in _asteroidTransforms.Keys) _asteroidPositionsUpdated[asteroids] = false; foreach (var corporation in Cache.GetAll()) @@ -232,26 +235,82 @@ public class GameContext return _orbitVelocities.ContainsKey(orbit) ? _orbitVelocities[orbit] : float2.zero; } - public float3[] GetAsteroidTransforms(Guid planetDataID) + public int NearestAsteroid(Guid planetDataID, float2 position) { var planetData = Cache.Get(planetDataID); - if (!_asteroidPositions.ContainsKey(planetDataID)) + + var asteroidPositions = GetAsteroidTransforms(planetDataID); + + int nearest = 0; + float nearestDistance = Single.MaxValue; + for (int i = 0; i < planetData.Asteroids.Length; i++) { - _asteroidPositions[planetDataID] = new float3[planetData.Asteroids.Length]; + var dist = lengthsq(asteroidPositions[i].xy - position); + if (AsteroidExists(planetDataID, i) && dist < nearestDistance) + { + nearest = i; + nearestDistance = dist; + } + } + + return nearest; + } + + public bool AsteroidExists(Guid planetDataID, int asteroid) => + !_asteroidRespawnTimers.ContainsKey((planetDataID, asteroid)); + + public float2 GetAsteroidVelocity(Guid planetDataID, int asteroid) + { + UpdateAsteroidTransforms(planetDataID); + + if (!_previousAsteroidTransforms.ContainsKey(planetDataID)) + return float2.zero; + + return (_asteroidTransforms[planetDataID][asteroid].xy - _previousAsteroidTransforms[planetDataID][asteroid].xy) / _deltaTime; + } + + public float4 GetAsteroidTransform(Guid planetDataID, int asteroid) + { + UpdateAsteroidTransforms(planetDataID); + return _asteroidTransforms[planetDataID][asteroid]; + } + + public float4[] GetAsteroidTransforms(Guid planetDataID) + { + UpdateAsteroidTransforms(planetDataID); + + return _asteroidTransforms[planetDataID]; + } + + private void UpdateAsteroidTransforms(Guid planetDataID) + { + var planetData = Cache.Get(planetDataID); + + if (!_asteroidTransforms.ContainsKey(planetDataID)) + { + _asteroidTransforms[planetDataID] = new float4[planetData.Asteroids.Length]; _asteroidPositionsUpdated[planetDataID] = false; } if (_asteroidPositionsUpdated[planetDataID]) - return _asteroidPositions[planetDataID]; + return; + + if (!_previousAsteroidTransforms.ContainsKey(planetDataID)) + _previousAsteroidTransforms[planetDataID] = new float4[planetData.Asteroids.Length]; + Array.Copy(_asteroidTransforms[planetDataID], _previousAsteroidTransforms[planetDataID], planetData.Asteroids.Length); + + var orbitData = Cache.Get(planetData.Orbit); + var orbitPosition = GetOrbitPosition(orbitData.Parent); for (var i = 0; i < planetData.Asteroids.Length; i++) { - _asteroidPositions[planetDataID][i] = float3( - OrbitData.Evaluate((float) frac(Time / OrbitalPeriod(planetData.Asteroids[i].x) * GlobalData.OrbitSpeedMultiplier + - planetData.Asteroids[i].y)) * planetData.Asteroids[i].x, - (float) (Time * planetData.Asteroids[i].w % 360.0)); + _asteroidTransforms[planetDataID][i] = float4( + OrbitData.Evaluate((float) frac(Time / OrbitalPeriod(planetData.Asteroids[i].Distance) * GlobalData.OrbitSpeedMultiplier + + planetData.Asteroids[i].Phase)) * planetData.Asteroids[i].Distance + orbitPosition, + (float) (Time * planetData.Asteroids[i].RotationSpeed % 360.0), + _asteroidRespawnTimers.ContainsKey((planetDataID, i)) ? 0 : planetData.Asteroids[i].Size); } - return _asteroidPositions[planetDataID]; + _asteroidPositionsUpdated[planetDataID] = true; } public OrbitData CreateOrbit(Guid parent, float2 position) @@ -274,11 +333,9 @@ public class GameContext Phase = storedPhase, Zone = parentOrbit.Zone }; - _lastCreatedOrbit = orbit; Cache.Add(orbit); return orbit; } - private OrbitData _lastCreatedOrbit; public float OrbitalPeriod(float distance) { @@ -812,4 +869,133 @@ public class GameContext parent.RecalculateMass(); child.Parent = Guid.Empty; } + + public void MineAsteroid(Entity miner, Guid asteroidBelt, int asteroid, float damage, float efficiency, float penetration) + { + var planetData = Cache.Get(asteroidBelt); + var asteroidTransform = GetAsteroidTransform(asteroidBelt, asteroid); + var hpLerp = unlerp(GlobalData.AsteroidSizeMin, GlobalData.AsteroidSizeMax, asteroidTransform.w); + var asteroidHitpoints = lerp(GlobalData.AsteroidHitpointsMin, GlobalData.AsteroidHitpointsMax, pow(hpLerp, GlobalData.AsteroidHitpointsPower)); + if (!_asteroidDamage.ContainsKey((asteroidBelt, asteroid))) + _asteroidDamage[(asteroidBelt, asteroid)] = 0; + _asteroidDamage[(asteroidBelt, asteroid)] = _asteroidDamage[(asteroidBelt, asteroid)] + damage; + if (_asteroidDamage[(asteroidBelt, asteroid)] > asteroidHitpoints) + _asteroidRespawnTimers[(asteroidBelt, asteroid)] = + lerp(GlobalData.AsteroidRespawnMin, GlobalData.AsteroidRespawnMax, hpLerp); + var resource = planetData.Resources.MaxBy(x => pow(x.Value, 1f / penetration) * Random.NextFloat()); + if (resource.Value * efficiency * Random.NextFloat() > 1 && miner.OccupiedCapacity < miner.Capacity - 1) + { + var simpleCommodityTarget = miner.Cargo + .Select(i => Cache.Get(i)) + .FirstOrDefault(i => i.Data == resource.Key) as SimpleCommodity; + if (simpleCommodityTarget == null) + { + var newSimpleCommodity = new SimpleCommodity + { + Context = this, + Data = resource.Key, + Quantity = 1 + }; + Cache.Add(newSimpleCommodity); + miner.Cargo.Add(newSimpleCommodity.ID); + miner.RecalculateMass(); + } + else + { + simpleCommodityTarget.Quantity++; + miner.RecalculateMass(); + } + } + } + + public bool MoveCargo(Entity source, Entity target, ItemInstance item, int quantity = int.MaxValue) + { + if (item is CraftedItemInstance craftedItemInstance) + { + var craftedItemData = Cache.Get(craftedItemInstance.Data); + if (target.Capacity - target.OccupiedCapacity > craftedItemData.Size) + { + // Target has the cargo capacity for the item, simply move the instance + source.Cargo.Remove(item.ID); + target.Cargo.Add(item.ID); + source.RecalculateMass(); + target.RecalculateMass(); + return true; + } + return false; + } + + if (item is SimpleCommodity simpleCommoditySource) + { + quantity = min(quantity, simpleCommoditySource.Quantity); + var simpleCommodityData = Cache.Get(simpleCommoditySource.Data); + var spareCapacity = (int) (target.Capacity - target.OccupiedCapacity); + var simpleCommodityTarget = target.Cargo + .Select(i => Cache.Get(i)) + .FirstOrDefault(i => i.Data == simpleCommodityData.ID) as SimpleCommodity; + var success = spareCapacity >= quantity; + quantity = min(quantity, spareCapacity); + if (simpleCommodityTarget == null) + { + if (quantity == simpleCommoditySource.Quantity) + { + // Target has the cargo capacity to hold the full quantity, + // and the target has no matching item instance; + // simply move the existing item instance to the target + source.Cargo.Remove(simpleCommoditySource.ID); + target.Cargo.Add(simpleCommoditySource.ID); + source.RecalculateMass(); + target.RecalculateMass(); + return success; + } + else + { + // Target has the cargo capacity to hold the desired quantity + // and the target has no matching item instance; + // Create a new item instance on the target and decrement the quantity of the source instance + var newSimpleCommodity = new SimpleCommodity + { + Context = this, + Data = simpleCommodityData.ID, + Quantity = simpleCommoditySource.Quantity + }; + Cache.Add(newSimpleCommodity); + target.Cargo.Add(newSimpleCommodity.ID); + simpleCommoditySource.Quantity -= quantity; + source.RecalculateMass(); + target.RecalculateMass(); + return success; + } + } + else + { + if (quantity == simpleCommoditySource.Quantity) + { + // Target has the cargo capacity to hold the full quantity, + // and there is already a matching item instance; + // delete the source entity's item instance and increment the target's quantity + source.Cargo.Remove(simpleCommoditySource.ID); + Cache.Delete(simpleCommoditySource); + simpleCommodityTarget.Quantity += quantity; + source.RecalculateMass(); + target.RecalculateMass(); + return success; + } + else + { + // Target has the cargo capacity to hold the desired quantity, + // and there is already a matching item instance; + // decrement the source's quantity and increment the target's quantity + simpleCommoditySource.Quantity -= quantity; + simpleCommodityTarget.Quantity += quantity; + source.RecalculateMass(); + target.RecalculateMass(); + return success; + } + } + + } + + return false; + } } \ No newline at end of file diff --git a/Assets/Scripts/ServerShared/GlobalData.cs b/Assets/Scripts/ServerShared/GlobalData.cs index 21589ec8e..257e31be9 100644 --- a/Assets/Scripts/ServerShared/GlobalData.cs +++ b/Assets/Scripts/ServerShared/GlobalData.cs @@ -139,6 +139,21 @@ public class GlobalData : DatabaseEntry [InspectableField, JsonProperty("startingGearQualityMax"), Key(43)] public float StartingGearQualityMax = .5f; + + [InspectableField, JsonProperty("asteroidHitpointsMin"), Key(44)] + public float AsteroidHitpointsMin = 10f; + + [InspectableField, JsonProperty("asteroidHitpointsMax"), Key(45)] + public float AsteroidHitpointsMax = 200f; + + [InspectableField, JsonProperty("asteroidHitpointsPower"), Key(46)] + public float AsteroidHitpointsPower = 2f; + + [InspectableField, JsonProperty("asteroidRespawnMin"), Key(47)] + public float AsteroidRespawnMin = 10f; + + [InspectableField, JsonProperty("asteroidRespawnMax"), Key(48)] + public float AsteroidRespawnMax = 100f; public float PlanetRadius(float mass) { diff --git a/Assets/Scripts/ServerShared/ZoneData.cs b/Assets/Scripts/ServerShared/ZoneData.cs index aade3105a..f80ab95dd 100644 --- a/Assets/Scripts/ServerShared/ZoneData.cs +++ b/Assets/Scripts/ServerShared/ZoneData.cs @@ -94,7 +94,7 @@ public class PlanetData : DatabaseEntry, INamedEntry public Dictionary Resources = new Dictionary(); [JsonProperty("asteroids"), Key(7)] - public float4[] Asteroids; // Distance, Phase, Size, RotationSpeed + public Asteroid[] Asteroids; // Distance, Phase, Size, RotationSpeed // [JsonProperty("radius")] [Key(5)] // public float GravityRadius; @@ -106,6 +106,15 @@ public class PlanetData : DatabaseEntry, INamedEntry } } +[MessagePackObject, JsonObject(MemberSerialization.OptIn)] +public class Asteroid +{ + public float Distance; + public float Phase; + public float Size; + public float RotationSpeed; +} + [RethinkTable("Galaxy"), MessagePackObject, JsonObject(MemberSerialization.OptIn)] public class OrbitData : DatabaseEntry { diff --git a/Assets/Scripts/ServerShared/ZoneGenerator.cs b/Assets/Scripts/ServerShared/ZoneGenerator.cs index 665fba7fe..225226c48 100644 --- a/Assets/Scripts/ServerShared/ZoneGenerator.cs +++ b/Assets/Scripts/ServerShared/ZoneGenerator.cs @@ -104,11 +104,15 @@ public class ZoneGenerator { planetData.Asteroids = Enumerable.Range(0, (int) (pow(planetData.Mass, context.GlobalData.BeltMassExponent) / context.GlobalData.BeltMassRatio * orbitMap[planet].Distance)) - .Select(_ => float4(context.Random.NextFloat(orbitMap[planet].Distance * .5f, orbitMap[planet].Distance * 1.5f), - context.Random.NextFloat(), - pow(context.Random.NextFloat(), context.GlobalData.AsteroidSizeExponent) * (context.GlobalData.AsteroidSizeMax - context.GlobalData.AsteroidSizeMin) + context.GlobalData.AsteroidSizeMin, - context.Random.NextFloat() * context.GlobalData.AsteroidRotationSpeed)) - .ToArray(); + .Select(_ => new Asteroid + { + Distance = context.Random.NextFloat(orbitMap[planet].Distance * .5f, orbitMap[planet].Distance * 1.5f), + Phase = context.Random.NextFloat(), + Size = pow(context.Random.NextFloat(), context.GlobalData.AsteroidSizeExponent) * + (context.GlobalData.AsteroidSizeMax - context.GlobalData.AsteroidSizeMin) + + context.GlobalData.AsteroidSizeMin, + RotationSpeed = context.Random.NextFloat() * context.GlobalData.AsteroidRotationSpeed + }).ToArray(); } return planetData; }).ToArray(); diff --git a/Assets/Scripts/StrategyGameManager.cs b/Assets/Scripts/StrategyGameManager.cs index a963ae0b7..f3af07be1 100644 --- a/Assets/Scripts/StrategyGameManager.cs +++ b/Assets/Scripts/StrategyGameManager.cs @@ -371,7 +371,7 @@ public class StrategyGameManager : MonoBehaviour foreach (var belt in _zoneBelts) { - belt.Value.Transform.position = float3(_context.GetOrbitPosition(belt.Key) * ZoneSizeScale, .15f); + //belt.Value.Transform.position = float3(_context.GetOrbitPosition(belt.Key) * ZoneSizeScale, .15f); belt.Value.Update(); } @@ -543,15 +543,31 @@ public class StrategyGameManager : MonoBehaviour ContextMenu.Clear(); if (targetObject is PlanetData planet) { - ContextMenu.AddOption("Test Option 1", () => Debug.Log("Test Option 1 Selected")); - ContextMenu.AddOption("Test Option 2", () => Debug.Log("Test Option 2 Selected")); - ContextMenu.AddDropdown("Test Dropdown", new [] + if (planet.Belt) { - ("Dropdown Option 1", (Action) (() => Debug.Log("Dropdown Option 1 Selected"))), - ("Dropdown Option 2", (Action) (() => Debug.Log("Dropdown Option 2 Selected"))), - ("Dropdown Option 3", (Action) (() => Debug.Log("Dropdown Option 3 Selected"))) - }); - ContextMenu.AddOption("Test Option 3", () => Debug.Log("Test Option 3 Selected")); + ContextMenu.AddOption("Create Mining Task", () => + { + var task = new Mining + { + Asteroids = clickTarget, + Context = _context, + Priority = 0, + Reserved = false, + Zone = _populatedZone + }; + _cache.Add(task); + _cache.Get(_playerCorporation).Tasks.Add(task.ID); + }); + } + // ContextMenu.AddOption("Test Option 1", () => Debug.Log("Test Option 1 Selected")); + // ContextMenu.AddOption("Test Option 2", () => Debug.Log("Test Option 2 Selected")); + // ContextMenu.AddDropdown("Test Dropdown", new [] + // { + // ("Dropdown Option 1", (Action) (() => Debug.Log("Dropdown Option 1 Selected"))), + // ("Dropdown Option 2", (Action) (() => Debug.Log("Dropdown Option 2 Selected"))), + // ("Dropdown Option 3", (Action) (() => Debug.Log("Dropdown Option 3 Selected"))) + // }); + // ContextMenu.AddOption("Test Option 3", () => Debug.Log("Test Option 3 Selected")); } else if (targetObject is Ship ship) { @@ -617,7 +633,7 @@ public class StrategyGameManager : MonoBehaviour hullData.HullType == HullType.Station ? "Colony" : "Weapons Platform"); PropertiesPanel.AddProperty("Hull", () => $"{hullData.Name}"); - PropertiesPanel.AddProperty("Capacity", () => $"{entity.OccupiedCapacity}/{_context.Evaluate(hullData.Capacity, hull, entity)}"); + PropertiesPanel.AddProperty("Capacity", () => $"{entity.OccupiedCapacity}/{entity.Capacity}"); PropertiesPanel.AddProperty("Mass", () => $"{entity.Mass}"); PropertiesPanel.AddProperty("Temperature", () => $"{entity.Temperature:0}°K"); PropertiesPanel.AddProperty("Energy", () => $"{entity.Energy:0}/{entity.GetBehaviors().First().Capacitance:0}"); @@ -986,9 +1002,11 @@ public class AsteroidBelt private Vector2[] _uvs; private int[] _indices; private Guid _data; + private Guid _orbit; private GameContext _context; private Mesh _mesh; private float _zoneScale; + private float _size; public AsteroidBelt(GameContext context, MeshFilter meshFilter, MeshCollider collider, Guid data, int spritesheetWidth, int spritesheetHeight, float zoneScale) { @@ -999,6 +1017,8 @@ public class AsteroidBelt _collider = collider; _zoneScale = zoneScale; var planetData = context.Cache.Get(data); + var orbitData = context.Cache.Get(planetData.Orbit); + _orbit = orbitData.Parent; _vertices = new Vector3[planetData.Asteroids.Length*4]; _normals = new Vector3[planetData.Asteroids.Length*4]; _uvs = new Vector2[planetData.Asteroids.Length*4]; @@ -1009,8 +1029,8 @@ public class AsteroidBelt // vertex order: bottom left, top left, top right, bottom right for (var i = 0; i < planetData.Asteroids.Length; i++) { - if (planetData.Asteroids[i].x > maxDist) - maxDist = planetData.Asteroids[i].x; + if (planetData.Asteroids[i].Distance > maxDist) + maxDist = planetData.Asteroids[i].Distance; var spriteX = Random.Range(0, spritesheetWidth); var spriteY = Random.Range(0, spritesheetHeight); @@ -1038,6 +1058,7 @@ public class AsteroidBelt _mesh.triangles = _indices; _mesh.normals = _normals; _mesh.bounds = new Bounds(Vector3.zero, Vector3.one * maxDist); + _size = maxDist; _filter.mesh = _mesh; //_collider.sharedMesh = _mesh; @@ -1051,12 +1072,13 @@ public class AsteroidBelt { var rotation = Quaternion.Euler(0, 0, asteroidTransforms[i].z); var position = (Vector3) (Vector2) asteroidTransforms[i].xy * _zoneScale; - _vertices[i * 4] = rotation * new Vector3(-planetData.Asteroids[i].z,-planetData.Asteroids[i].z,0) + position; - _vertices[i * 4 + 1] = rotation * new Vector3(-planetData.Asteroids[i].z,planetData.Asteroids[i].z,0) + position; - _vertices[i * 4 + 2] = rotation * new Vector3(planetData.Asteroids[i].z,planetData.Asteroids[i].z,0) + position; - _vertices[i * 4 + 3] = rotation * new Vector3(planetData.Asteroids[i].z,-planetData.Asteroids[i].z,0) + position; + _vertices[i * 4] = rotation * new Vector3(-asteroidTransforms[i].w * _zoneScale,-asteroidTransforms[i].w * _zoneScale,0) + position; + _vertices[i * 4 + 1] = rotation * new Vector3(-asteroidTransforms[i].w * _zoneScale,asteroidTransforms[i].w * _zoneScale,0) + position; + _vertices[i * 4 + 2] = rotation * new Vector3(asteroidTransforms[i].w * _zoneScale,asteroidTransforms[i].w * _zoneScale,0) + position; + _vertices[i * 4 + 3] = rotation * new Vector3(asteroidTransforms[i].w * _zoneScale,-asteroidTransforms[i].w * _zoneScale,0) + position; } + _mesh.bounds = new Bounds((Vector2) _context.GetOrbitPosition(_orbit) * _zoneScale, Vector3.one * (_size * _zoneScale * 2)); _mesh.vertices = _vertices; _collider.sharedMesh = _mesh; } diff --git a/Economy.Shared.Unity.csproj b/Economy.Shared.Unity.csproj index dcf068551..b5f806f24 100644 --- a/Economy.Shared.Unity.csproj +++ b/Economy.Shared.Unity.csproj @@ -64,6 +64,7 @@ + @@ -77,6 +78,8 @@ + +