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)
196 if (currentPingIndex != -1)
198 var activePing = activePings[currentPingIndex];
199 if (activePing.State > 1.0f)
201 aiPingCheckPending =
true;
202 currentPingIndex = -1;
205 if (currentPingIndex == -1 && activePingsCount < activePings.Length)
207 currentPingIndex = activePingsCount++;
208 if (activePings[currentPingIndex] ==
null)
210 activePings[currentPingIndex] =
new ActivePing();
212 activePings[currentPingIndex].IsDirectional = useDirectionalPing;
213 activePings[currentPingIndex].Direction = pingDirection;
214 activePings[currentPingIndex].State = 0.0f;
215 activePings[currentPingIndex].PrevPingRadius = 0.0f;
216 foreach (
AITarget aiTarget
in GetAITargets())
218 aiTarget.
SectorDegrees = useDirectionalPing ? DirectionalPingSector : 360.0f;
219 aiTarget.
SectorDir =
new Vector2(pingDirection.X, -pingDirection.Y);
226 aiPingCheckPending =
false;
230 for (var pingIndex = 0; pingIndex < activePingsCount;)
232 foreach (
AITarget aiTarget
in GetAITargets())
237 if (activePings[pingIndex].State > 1.0f)
239 var lastIndex = --activePingsCount;
240 var oldActivePing = activePings[pingIndex];
241 activePings[pingIndex] = activePings[lastIndex];
242 activePings[lastIndex] = oldActivePing;
243 if (currentPingIndex == lastIndex)
245 currentPingIndex = pingIndex;
255 private IEnumerable<AITarget> GetAITargets()
263 foreach (var transducer
in connectedTransducers)
265 if (transducer.Transducer.Item.AiTarget !=
null)
267 yield
return transducer.Transducer.Item.AiTarget;
288 return currentPingIndex != -1 && (character ==
null ||
characterUsable);
291 private static readonly Dictionary<string, List<Character>> targetGroups =
new Dictionary<string, List<Character>>();
295 if (currentMode ==
Mode.Passive || !aiPingCheckPending) {
return false; }
297 foreach (List<Character> targetGroup
in targetGroups.Values)
308 #warning This is not the best key for a dictionary.
310 if (!targetGroups.ContainsKey(directionName))
312 targetGroups.Add(directionName,
new List<Character>());
314 targetGroups[directionName].Add(c);
317 foreach (KeyValuePair<
string, List<Character>> targetGroup
in targetGroups)
319 if (!targetGroup.Value.Any()) {
continue; }
320 string dialogTag =
"DialogSonarTarget";
321 if (targetGroup.Value.Count > 1)
323 dialogTag =
"DialogSonarTargetMultiple";
325 else if (targetGroup.Value[0].Mass > 100.0f)
327 dialogTag =
"DialogSonarTargetLarge";
330 if (character.IsOnPlayerTeam)
332 character.Speak(TextManager.GetWithVariables(dialogTag,
334 (
"[count]", targetGroup.Value.Count.ToString(),
FormatCapitals.No)).Value,
335 null, 0, $
"sonartarget{targetGroup.Value[0].ID}".ToIdentifier(), 60);
339 for (
int i = 1; i < targetGroup.Value.Count; i++)
341 character.DisableLine(
"sonartarget" + targetGroup.Value[i].ID);
350 float angle = MathUtils.WrapAngleTwoPi((
float)-Math.Atan2(dir.Y, dir.X) + MathHelper.PiOver2);
352 int clockDir = (int)Math.Round((angle / MathHelper.TwoPi) * 12);
353 if (clockDir == 0) clockDir = 12;
355 return TextManager.GetWithVariable(
"roomname.subdiroclock",
"[dir]", clockDir.ToString());
360 base.ReceiveSignal(signal, connection);
362 if (connection.
Name ==
"transducer_in")
365 if (transducer ==
null) {
return; }
369 var connectedTransducer = connectedTransducers.Find(t => t.Transducer == transducer);
370 if (connectedTransducer ==
null)
372 connectedTransducers.Add(
new ConnectedTransducer(transducer, signal.
strength, 1.0f));
376 connectedTransducer.SignalStrength = signal.
strength;
377 connectedTransducer.DisconnectTimer = 1.0f;
385 bool directionalPing = useDirectionalPing;
386 float zoomT = zoom, pingDirectionT = 0.0f;
405 zoom = MathHelper.Lerp(MinZoom, MaxZoom, zoomT);
406 useDirectionalPing = directionalPing;
407 if (useDirectionalPing)
409 float pingAngle = MathHelper.Lerp(0.0f, MathHelper.TwoPi, pingDirectionT);
410 pingDirection =
new Vector2((
float)Math.Cos(pingAngle), (
float)Math.Sin(pingAngle));
414 zoomSlider.BarScroll = zoomT;
415 directionalModeSwitch.Selected = useDirectionalPing;
416 if (mineralScannerSwitch !=
null)
423 item.CreateServerEvent(
this);
430 if (currentMode ==
Mode.Active)
434 if (useDirectionalPing)
436 float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
437 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)