Skip to content

Commit

Permalink
Turret Entities (#38)
Browse files Browse the repository at this point in the history
* Spotlight Turret.

* Used new turret sprite, credit: @Lobstrex (Lob)#5692

* Moved AimShootLifeProcessor.cs to content.

* Update Submodule Try 2.
  • Loading branch information
Acruid authored and PJB3005 committed Mar 15, 2018
1 parent 98bd155 commit 071ed3f
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 1 deletion.
170 changes: 170 additions & 0 deletions Content.Server/AI/AimShootLifeProcessor.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
3 changes: 3 additions & 0 deletions Content.Server/Content.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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" />
Expand Down Expand Up @@ -119,4 +121,5 @@
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
</Project>
5 changes: 5 additions & 0 deletions Content.Server/EntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,6 +20,7 @@
using SS14.Shared.Map;
using SS14.Shared.Timers;
using SS14.Shared.Interfaces.Timing;
using SS14.Shared.Maths;

namespace Content.Server
{
Expand Down Expand Up @@ -100,6 +102,9 @@ private static void HandleRunLevelChanged(object sender, RunLevelChangedEventArg
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.");
Expand Down
30 changes: 30 additions & 0 deletions Content.Server/Placement/SpawnHelpers.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions Resources/Prototypes/Entities/Turret.yml
Original file line number Diff line number Diff line change
@@ -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

Binary file added Resources/textures/Buildings/TurrBase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Resources/textures/Buildings/TurrLamp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Resources/textures/Buildings/TurrTop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion engine

0 comments on commit 071ed3f

Please sign in to comment.