Turret Entities (#38)
* Spotlight Turret. * Used new turret sprite, credit: @Lobstrex (Lob)#5692 * Moved AimShootLifeProcessor.cs to content. * Update Submodule Try 2.
This commit is contained in:
committed by
Pieter-Jan Briers
parent
98bd1552b9
commit
071ed3f1ed
170
Content.Server/AI/AimShootLifeProcessor.cs
Normal file
170
Content.Server/AI/AimShootLifeProcessor.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Server.AI;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects.Components;
|
||||
using SS14.Shared.Interfaces.Physics;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Server.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// The object stays stationary. The object will periodically scan for *any* life forms in its radius, and engage them.
|
||||
/// The object will rotate itself to point at the locked entity, and if it has a weapon will shoot at the entity.
|
||||
/// </summary>
|
||||
[AiLogicProcessor("AimShootLife")]
|
||||
class AimShootLifeProcessor : AiLogicProcessor
|
||||
{
|
||||
private readonly ICollisionManager _physMan;
|
||||
private readonly IServerEntityManager _entMan;
|
||||
private readonly IGameTiming _timeMan;
|
||||
|
||||
private readonly List<IEntity> _workList = new List<IEntity>();
|
||||
|
||||
private const float MaxAngSpeed = (float) (Math.PI / 2); // how fast our turret can rotate
|
||||
private const float ScanPeriod = 1.0f; // tweak this for performance and gameplay experience
|
||||
private float _lastScan;
|
||||
|
||||
private IEntity _curTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this LogicProcessor.
|
||||
/// </summary>
|
||||
public AimShootLifeProcessor()
|
||||
{
|
||||
_physMan = IoCManager.Resolve<ICollisionManager>();
|
||||
_entMan = IoCManager.Resolve<IServerEntityManager>();
|
||||
_timeMan = IoCManager.Resolve<IGameTiming>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (SelfEntity == null)
|
||||
return;
|
||||
|
||||
DoScanning();
|
||||
DoTracking(frameTime);
|
||||
}
|
||||
|
||||
private void DoScanning()
|
||||
{
|
||||
var curTime = _timeMan.CurTime.TotalSeconds;
|
||||
if (curTime - _lastScan > ScanPeriod)
|
||||
{
|
||||
_lastScan = (float) curTime;
|
||||
_curTarget = FindBestTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void DoTracking(float frameTime)
|
||||
{
|
||||
// not valid entity to target.
|
||||
if (_curTarget == null || !_curTarget.IsValid())
|
||||
{
|
||||
_curTarget = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// point me at the target
|
||||
var tarPos = _curTarget.GetComponent<ITransformComponent>().WorldPosition;
|
||||
var myPos = SelfEntity.GetComponent<ITransformComponent>().WorldPosition;
|
||||
|
||||
var curDir = SelfEntity.GetComponent<IServerTransformComponent>().LocalRotation.ToVec();
|
||||
var tarDir = (tarPos - myPos).Normalized;
|
||||
|
||||
var fwdAng = Vector2.Dot(curDir, tarDir);
|
||||
|
||||
Vector2 newDir;
|
||||
if (fwdAng < 0) // target behind turret, just rotate in a direction to get target in front
|
||||
{
|
||||
var curRight = new Vector2(-curDir.Y, curDir.X); // right handed coord system
|
||||
var rightAngle = Vector2.Dot(curDir, new Vector2(-tarDir.Y, tarDir.X)); // right handed coord system
|
||||
var rotateSign = -Math.Sign(rightAngle);
|
||||
newDir = curDir + curRight * rotateSign * MaxAngSpeed * frameTime;
|
||||
}
|
||||
else // target in front, adjust to aim at him
|
||||
{
|
||||
newDir = MoveTowards(curDir, tarDir, MaxAngSpeed, frameTime);
|
||||
}
|
||||
|
||||
SelfEntity.GetComponent<IServerTransformComponent>().LocalRotation = new Angle(newDir);
|
||||
|
||||
if (fwdAng > -0.9999)
|
||||
{
|
||||
// TODO: shoot gun, prob need aimbot because entity rotation lags behind moving target
|
||||
}
|
||||
}
|
||||
|
||||
private IEntity FindBestTarget()
|
||||
{
|
||||
// "best" target is the closest one with LOS
|
||||
|
||||
var ents = _entMan.GetEntitiesInRange(SelfEntity, VisionRadius);
|
||||
var myTransform = SelfEntity.GetComponent<IServerTransformComponent>();
|
||||
var maxRayLen = VisionRadius * 2.5f; // circle inscribed in square, square diagonal = 2*r*sqrt(2)
|
||||
|
||||
_workList.Clear();
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
// filter to "people" entities (entities with controllers)
|
||||
if (!entity.HasComponent<IMoverComponent>())
|
||||
continue;
|
||||
|
||||
// build the ray
|
||||
var dir = entity.GetComponent<TransformComponent>().WorldPosition - myTransform.WorldPosition;
|
||||
var ray = new Ray(myTransform.WorldPosition, dir.Normalized);
|
||||
|
||||
// cast the ray
|
||||
var result = _physMan.IntersectRay(ray, maxRayLen);
|
||||
|
||||
// add to visible list
|
||||
if (result.HitEntity == entity)
|
||||
_workList.Add(entity);
|
||||
}
|
||||
|
||||
// get closest entity in list
|
||||
var closestEnt = GetClosest(myTransform.WorldPosition, _workList);
|
||||
|
||||
// return closest
|
||||
return closestEnt;
|
||||
}
|
||||
|
||||
private static IEntity GetClosest(Vector2 origin, IEnumerable<IEntity> list)
|
||||
{
|
||||
IEntity closest = null;
|
||||
var minDistSqrd = float.PositiveInfinity;
|
||||
|
||||
foreach (var ent in list)
|
||||
{
|
||||
var pos = ent.GetComponent<ITransformComponent>().WorldPosition;
|
||||
var distSqrd = (pos - origin).LengthSquared;
|
||||
|
||||
if (distSqrd > minDistSqrd)
|
||||
continue;
|
||||
|
||||
closest = ent;
|
||||
minDistSqrd = distSqrd;
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
private static Vector2 MoveTowards(Vector2 current, Vector2 target, float speed, float delta)
|
||||
{
|
||||
var maxDeltaDist = speed * delta;
|
||||
var a = target - current;
|
||||
var magnitude = a.Length;
|
||||
if (magnitude <= maxDeltaDist)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
return current + a / magnitude * maxDeltaDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AI\AimShootLifeProcessor.cs" />
|
||||
<Compile Include="EntryPoint.cs" />
|
||||
<Compile Include="GameObjects\Components\Doors\ServerDoorComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\BaseTool.cs" />
|
||||
@@ -84,6 +85,7 @@
|
||||
<Compile Include="GameObjects\Components\Damage\ResistanceSet.cs" />
|
||||
<Compile Include="GameObjects\Components\Temperature\TemperatureComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\IOnDamageBehavior.cs" />
|
||||
<Compile Include="Placement\SpawnHelpers.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Temperature\ITemperatureComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Damage\IDamageableComponent.cs" />
|
||||
@@ -119,4 +121,5 @@
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
@@ -2,6 +2,7 @@
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.GameObjects.Components.Interactable.Tools;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Placement;
|
||||
using SS14.Server;
|
||||
using SS14.Server.Interfaces;
|
||||
using SS14.Server.Interfaces.Chat;
|
||||
@@ -19,6 +20,7 @@ using SS14.Shared.Log;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Timers;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Server
|
||||
{
|
||||
@@ -100,6 +102,9 @@ namespace Content.Server
|
||||
var newMap = mapMan.CreateMap(new MapId(2));
|
||||
|
||||
mapLoader.LoadBlueprint(newMap, new GridId(4), "Maps/Demo/DemoGrid.yaml");
|
||||
|
||||
var grid = newMap.GetGrid(new GridId(4));
|
||||
SpawnHelpers.SpawnLightTurret(grid, new Vector2(-15, 15));
|
||||
}
|
||||
var timeSpan = timing.RealTime - startTime;
|
||||
Logger.Info($"Loaded map in {timeSpan.TotalMilliseconds:N2}ms.");
|
||||
|
||||
30
Content.Server/Placement/SpawnHelpers.cs
Normal file
30
Content.Server/Placement/SpawnHelpers.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Placement
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper function for spawning more complex multi-entity structures
|
||||
/// </summary>
|
||||
public static class SpawnHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns a spotlight ground turret that will track any living entities in range.
|
||||
/// </summary>
|
||||
/// <param name="grid"></param>
|
||||
/// <param name="localPosition"></param>
|
||||
public static void SpawnLightTurret(IMapGrid grid, Vector2 localPosition)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IServerEntityManager>();
|
||||
var tBase = entMan.SpawnEntity("TurretBase");
|
||||
tBase.GetComponent<IServerTransformComponent>().LocalPosition = new LocalCoordinates(localPosition, grid);
|
||||
|
||||
var tTop = entMan.SpawnEntity("TurretTopLight");
|
||||
tTop.GetComponent<IServerTransformComponent>().LocalPosition = new LocalCoordinates(localPosition, grid);
|
||||
tTop.GetComponent<IServerTransformComponent>().AttachParent(tBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Resources/Prototypes/Entities/Turret.yml
Normal file
46
Resources/Prototypes/Entities/Turret.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
- type: entity
|
||||
id: TurretBase
|
||||
name: Turret Base
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- TurrBase
|
||||
|
||||
- type: entity
|
||||
id: TurretTopGun
|
||||
name: Turret (Gun)
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprites:
|
||||
- TurrTop
|
||||
- type: AiController
|
||||
logic: AimShootLife
|
||||
vision: 6.0
|
||||
|
||||
- type: entity
|
||||
id: TurretTopLight
|
||||
name: Turret (Light)
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprites:
|
||||
- TurrLamp
|
||||
- type: AiController
|
||||
logic: AimShootLife
|
||||
vision: 6.0
|
||||
- type: PointLight
|
||||
radius: 512
|
||||
mask: flashlight_mask
|
||||
autoRot: true
|
||||
|
||||
BIN
Resources/textures/Buildings/TurrBase.png
Normal file
BIN
Resources/textures/Buildings/TurrBase.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Resources/textures/Buildings/TurrLamp.png
Normal file
BIN
Resources/textures/Buildings/TurrLamp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Resources/textures/Buildings/TurrTop.png
Normal file
BIN
Resources/textures/Buildings/TurrTop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
2
engine
2
engine
Submodule engine updated: fc361882b8...93c7aab5d0
Reference in New Issue
Block a user