2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
21 class ConnectedTransducer
24 public float SignalStrength;
25 public float DisconnectTimer;
27 public ConnectedTransducer(
SonarTransducer transducer,
float signalStrength,
float disconnectTimer)
29 Transducer = transducer;
30 SignalStrength = signalStrength;
31 DisconnectTimer = disconnectTimer;
35 private const float DirectionalPingSector = 30.0f;
36 private static readonly
float DirectionalPingDotProduct;
40 DirectionalPingDotProduct = (float)Math.Cos(MathHelper.ToRadians(DirectionalPingSector) * 0.5f);
45 private const float PingFrequency = 0.5f;
47 private Mode currentMode =
Mode.Passive;
49 private class ActivePing
52 public bool IsDirectional;
54 public float PrevPingRadius;
57 private ActivePing[] activePings =
new ActivePing[8];
59 private int activePingsCount;
61 private int currentPingIndex = -1;
63 private const float MinZoom = 1.0f, MaxZoom = 4.0f;
64 private float zoom = 1.0f;
68 private bool useDirectionalPing =
false;
69 private Vector2 pingDirection =
new Vector2(1.0f, 0.0f);
71 private bool aiPingCheckPending;
74 private readonly List<ConnectedTransducer> connectedTransducers;
78 get {
return connectedTransducers.Select(t => t.Transducer); }
87 range = MathHelper.Clamp(value, 0.0f, 100000.0f);
109 [
Editable,
Serialize(
false,
IsPropertySaveable.No, description:
"Should the sonar view be centered on the transducers or the submarine's center of mass. Only has an effect if UseTransducers is enabled.")]
116 private bool hasMineralScanner;
121 get => hasMineralScanner;
125 if (controlContainer !=
null && !hasMineralScanner && value)
127 AddMineralScannerSwitchToGUI();
130 hasMineralScanner = value;
142 zoom = MathHelper.Clamp(value, MinZoom, MaxZoom);
144 zoomSlider.
BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
154 bool changed = currentMode != value;
157 if (changed) { prevPassivePingRadius =
float.MaxValue; }
164 : base(
item, element)
166 connectedTransducers =
new List<ConnectedTransducer>();
168 InitProjSpecific(element);
180 foreach (ConnectedTransducer transducer
in connectedTransducers)
182 transducer.DisconnectTimer -= deltaTime;
184 connectedTransducers.RemoveAll(t => t.DisconnectTimer <= 0.0f);
187 for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
189 activePings[pingIndex].State += deltaTime * PingFrequency;
192 if (currentMode ==
Mode.Active)
197 if (currentPingIndex != -1)
199 var activePing = activePings[currentPingIndex];
200 if (activePing.State > 1.0f)
202 aiPingCheckPending =
true;
203 currentPingIndex = -1;
206 if (currentPingIndex == -1 && activePingsCount < activePings.Length)
208 currentPingIndex = activePingsCount++;
209 if (activePings[currentPingIndex] ==
null)
211 activePings[currentPingIndex] =
new ActivePing();
213 activePings[currentPingIndex].IsDirectional = useDirectionalPing;
214 activePings[currentPingIndex].Direction = pingDirection;
215 activePings[currentPingIndex].State = 0.0f;
216 activePings[currentPingIndex].PrevPingRadius = 0.0f;
217 foreach (
AITarget aiTarget
in GetAITargets())
219 aiTarget.
SectorDegrees = useDirectionalPing ? DirectionalPingSector : 360.0f;
220 aiTarget.
SectorDir =
new Vector2(pingDirection.X, -pingDirection.Y);
227 aiPingCheckPending =
false;
231 for (var pingIndex = 0; pingIndex < activePingsCount;)
233 foreach (
AITarget aiTarget
in GetAITargets())
238 if (activePings[pingIndex].State > 1.0f)
240 var lastIndex = --activePingsCount;
241 var oldActivePing = activePings[pingIndex];
242 activePings[pingIndex] = activePings[lastIndex];
243 activePings[lastIndex] = oldActivePing;
244 if (currentPingIndex == lastIndex)
246 currentPingIndex = pingIndex;
256 private IEnumerable<AITarget> GetAITargets()
264 foreach (var transducer
in connectedTransducers)
266 if (transducer.Transducer.Item.AiTarget !=
null)
268 yield
return transducer.Transducer.Item.AiTarget;
289 return currentPingIndex != -1 && (character ==
null ||
characterUsable);
292 private static readonly Dictionary<string, List<Character>> targetGroups =
new Dictionary<string, List<Character>>();
296 if (currentMode ==
Mode.Passive || !aiPingCheckPending) {
return false; }
298 foreach (List<Character> targetGroup
in targetGroups.Values)
309 #warning This is not the best key for a dictionary.
311 if (!targetGroups.ContainsKey(directionName))
313 targetGroups.Add(directionName,
new List<Character>());
315 targetGroups[directionName].Add(c);
318 foreach (KeyValuePair<
string, List<Character>> targetGroup
in targetGroups)
320 if (!targetGroup.Value.Any()) {
continue; }
321 string dialogTag =
"DialogSonarTarget";
322 if (targetGroup.Value.Count > 1)
324 dialogTag =
"DialogSonarTargetMultiple";
326 else if (targetGroup.Value[0].Mass > 100.0f)
328 dialogTag =
"DialogSonarTargetLarge";
331 if (character.IsOnPlayerTeam)
333 character.Speak(TextManager.GetWithVariables(dialogTag,
335 (
"[count]", targetGroup.Value.Count.ToString(),
FormatCapitals.No)).Value,
336 null, 0, $
"sonartarget{targetGroup.Value[0].ID}".ToIdentifier(), 60);
340 for (
int i = 1; i < targetGroup.Value.Count; i++)
342 character.DisableLine(
"sonartarget" + targetGroup.Value[i].ID);
351 float angle = MathUtils.WrapAngleTwoPi((
float)-Math.Atan2(dir.Y, dir.X) + MathHelper.PiOver2);
353 int clockDir = (int)Math.Round((angle / MathHelper.TwoPi) * 12);
354 if (clockDir == 0) clockDir = 12;
356 return TextManager.GetWithVariable(
"roomname.subdiroclock",
"[dir]", clockDir.ToString());
361 base.ReceiveSignal(signal, connection);
363 if (connection.
Name ==
"transducer_in")
366 if (transducer ==
null) {
return; }
370 var connectedTransducer = connectedTransducers.Find(t => t.Transducer == transducer);
371 if (connectedTransducer ==
null)
373 connectedTransducers.Add(
new ConnectedTransducer(transducer, signal.
strength, 1.0f));
377 connectedTransducer.SignalStrength = signal.
strength;
378 connectedTransducer.DisconnectTimer = 1.0f;
386 bool directionalPing = useDirectionalPing;
387 float zoomT = zoom, pingDirectionT = 0.0f;
406 zoom = MathHelper.Lerp(MinZoom, MaxZoom, zoomT);
407 useDirectionalPing = directionalPing;
408 if (useDirectionalPing)
410 float pingAngle = MathHelper.Lerp(0.0f, MathHelper.TwoPi, pingDirectionT);
411 pingDirection =
new Vector2((
float)Math.Cos(pingAngle), (
float)Math.Sin(pingAngle));
416 directionalModeSwitch.
Selected = useDirectionalPing;
417 if (mineralScannerSwitch !=
null)
424 item.CreateServerEvent(
this);
431 if (currentMode ==
Mode.Active)
435 if (useDirectionalPing)
437 float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
438 msg.
WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
readonly CharacterParams Params
static readonly List< Character > CharacterList
readonly AnimController AnimController
virtual Vector2 WorldPosition
void Use(float deltaTime, Character user=null, Limb targetLimb=null, Entity useTarget=null, Character userForOnUsedEvent=null)
bool CanClientAccess(Client c)
float powerConsumption
The maximum amount of power the item can draw from connected items
void UpdateOnActiveEffects(float deltaTime)
const float PassivePowerConsumption
const float DefaultSonarRange
override void Update(float deltaTime, Camera cam)
Sonar(Item item, ContentXElement element)
void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData=null)
void ServerEventRead(IReadMessage msg, Client c)
override void ReceiveSignal(Signal signal, Connection connection)
IEnumerable< SonarTransducer > ConnectedTransducers
bool DetectSubmarineWalls
override bool Use(float deltaTime, Character character=null)
override float GetCurrentPowerConsumption(Connection connection=null)
Power consumption of the sonar. Only consume power when active and adjust the consumption based on th...
override bool CrewAIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
true if the operation was completed
Interface for entities that the clients can send events to the server
Single ReadRangedSingle(Single min, Single max, int bitCount)
Interface for entities that the server can send events to the clients
void WriteRangedSingle(Single val, Single min, Single max, int bitCount)
void WriteBoolean(bool val)