EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Scan.cpp
Go to the documentation of this file.
1 
13 // w.i.p.
14 
15 /* SCAN__ERROR
16  * SCAN__WARNING
17  * SCAN__MESSAGE
18  * SCAN__DEBUG
19  * SCAN__INFO
20  * SCAN__TRACE
21  * SCAN__DUMP
22  * SCAN__RSPDUMP
23  */
24 
25 #include "eve-server.h"
26 
27 #include "Client.h"
28 #include "EntityList.h"
29 #include "StatisticMgr.h"
30 #include "exploration/Scan.h"
31 #include "Probes.h"
32 #include "system/SystemBubble.h"
33 #include "system/SystemManager.h"
35 
36 Scan::Scan(Client* pClient)
37 : m_client(pClient),
38  m_system(pClient->SystemMgr())
39 {
40  m_probeScan = false;
41  m_probeMap.clear();
42  m_activeProbeMap.clear();
43 }
44 
45 void Scan::AddProbe(ProbeSE* pProbe)
46 {
47  pProbe->SetScan(this);
48  m_probeMap.emplace(pProbe->GetID(), pProbe);
49 }
50 
52 {
53  m_probeMap.erase(pProbe->GetID());
54 }
55 
56 void Scan::ProcessScan(bool useProbe/*false*/)
57 {
58  if (!useProbe) {
60  return;
61  }
62  if (m_probeScan) {
64  m_probeScan = false;
65  m_activeProbeMap.clear();
66  return;
67  }
68 
69  bool idle = true;
70  uint16 ntime(0), duration = m_client->GetShip()->GetAttribute(AttrScanSpeed).get_uint32();
71  if (duration < 1000)
72  duration = 8000; // 8s default probe scan time.
73  for (auto cur : m_activeProbeMap) {
74  if (cur.second->IsMoving()) {
75  idle = false;
76  ntime = cur.second->GetMoveTime();
77  // this time isnt right (but very close). need to really think it thru for correctness
78  // currrently, it will allow probes to hit 'idle' as scanning starts, or a few ms after.
79  if (ntime > duration) // flipped conditional
80  duration = ntime;
81  } else {
82  cur.second->SendStateChange(Probe::State::Idle);
83  }
84  }
85  if (idle) {
86  m_probeScan = true;
87  for (auto cur : m_activeProbeMap) {
88  cur.second->SendStateChange(Probe::State::Scanning);
89  cur.second->StartStateTimer(duration);
90  }
91  SystemScanStarted(duration);
92  }
93  _log(SCAN__TRACE, "ProcessScan() - probes - active:%u, total:%u, duration: %u, idle: %s", \
94  m_activeProbeMap.size(), m_probeMap.size(), duration, idle?"true":"false");
95  m_client->SetScanTimer(duration, true);
96 }
97 
98 
99 PyRep* Scan::ConeScan(Call_ConeScan args) {
100  // WORKING CODE...DONT FUCK WITH THIS!! -allan 7Dec15
101 
102  // NOTE: max distance is 14.4AU or maxInt (2417482647 in km)
103  /* to find if a point is inside a cone. (-allan 10Aug20)
104  * U = unit vector along cone axis (sent from client, decoded as x,y,z)
105  * VR = vector from cone vertex(V) to test point(R).
106  * Uvr = unit vector of cone vertex to test point (normalized VR)
107  * DP = Uvr dot U (dot product of Uvr and U). this will give cosine of angle between VR and U
108  * acDP = arc cosine of DP to give angle
109  * test acDP < cone angle = point is inside cone.
110  */
111  bool test = false;
112  float dot(0), acDP(0), angle(args.ScanAngle/2);
113  std::vector<SystemEntity*> seVec;
114  const GPoint vertex(m_client->GetShipSE()->GetPosition());
115  const GPoint U(args.x, args.y, args.z);
116  m_client->SystemMgr()->DScan(args.range, vertex, seVec);
117  _log(SCAN__TRACE, "ConeScan() - query returned %u objects within range. angle is %.3f", seVec.size(), angle);
118  PyList* list = new PyList();
119  for (auto cur : seVec ) {
120  GVector VR(vertex, cur->GetPosition());
121  VR.normalize();
122  dot = U.dotProduct(VR);
123  acDP = acos(dot);
124  if (acDP < angle) {
125  test = true;
126  DirectionScanResult res;
127  res.id = cur->GetID();
128  res.typeID = cur->GetSelf()->typeID();
129  res.groupID = cur->GetSelf()->groupID();
130  list->AddItem(res.Encode());
131  }
132  _log(SCAN__TRACE, "ConeScan() - tested %s(%u). dot %.5f, acDP %.5f, result %s", cur->GetName(), cur->GetID(), dot, acDP, test?"true":"false");
133  test = false;
134  }
135 
136  return list;
137 }
138 
141  if ((dict == nullptr) or dict->empty()) {
142  _log(SCAN__MESSAGE, "Scan::RequestScans() called by %s in %s using ship scanner.", \
143  m_client->GetName(), m_client->GetSystemName().c_str());
144 
145  OnSystemScanStarted ossst;
146  ossst.timestamp = GetFileTimeNow();
147  ossst.duration = duration;
148  ossst.scanProbesDict = new PyDict();
149  PyTuple* ev = ossst.Encode();
150  m_client->SendNotification("OnSystemScanStarted", "charid", &ev);
151  m_client->SetScanTimer(duration);
152  return;
153  }
154 
155  _log(SCAN__MESSAGE, "Scan::RequestScans() called by %s in %s using %u probes.",\
156  m_client->GetName(), m_client->GetSystemName().c_str(), dict->size());
157 
158  uint32 probeID(0);
159  PyDict::const_iterator cItr = dict->begin();
160  for (; cItr != dict->end(); ++cItr) {
161  // find probe in map....
162  probeID = PyRep::IntegerValueU32(cItr->first); // key
163  std::map<uint32, ProbeSE*>::iterator pItr = m_probeMap.find(probeID);
164  if (pItr == m_probeMap.end()) {
165  _log(SCAN__ERROR, "Probe %u wasnt found in the probeMap for %s(%u)", \
166  probeID, m_client->GetName(), m_client->GetCharacterID());
167  continue; // make error here?
168  }
169  std::map<uint32, ProbeSE*>::iterator aItr = m_activeProbeMap.find(probeID);
170  if (aItr == m_activeProbeMap.end())
171  m_activeProbeMap[probeID] = pItr->second;
172 
173  Call_ProbeDataObj args;
174  if (!args.Decode(cItr->second)) { // value
175  _log(SERVICE__ERROR, "Scan::RequestScans::DecodeProbeData: Failed to decode arguments.");
176  // make error here
177  continue;
178  }
179 
180  ProbeData data = ProbeData();
181  data.state = args.state; // do we need this?
182  data.expiry = args.expiry;
183  data.rangeStep = args.rangeStep;
184  data.scanRange = args.scanRange;
185  // set probe target
186  PyObjectEx* obj = args.destination->AsObjectEx();
187  PyTuple* dest = obj->header()->AsTuple()->GetItem(1)->AsTuple();
188  data.dest.x = dest->GetItem(0)->AsFloat()->value();
189  data.dest.y = dest->GetItem(1)->AsFloat()->value();
190  data.dest.z = dest->GetItem(2)->AsFloat()->value();
191  pItr->second->UpdateProbe(data);
192  }
193 
194  // loop thru probe maps to see if any are disabled, then set status as 'inactive' to omit from tracking
195  for (auto cur : m_probeMap) {
196  std::map<uint32, ProbeSE*>::iterator itr = m_activeProbeMap.find(cur.first);
197  if (itr == m_activeProbeMap.end())
198  cur.second->SetState(Probe::State::Inactive);
199  }
200 
201  m_client->SetScanTimer(duration, true);
202 }
203 
205 {
206  _log(SCAN__TRACE, "Scan::SystemScanStarted() for %s in system %u", m_client->GetName(), m_client->GetSystemID());
207 
208  GPoint pos(NULL_ORIGIN);
209  PyDict* probeDict = new PyDict();
210  for (auto cur : m_activeProbeMap) {
211  // probe data here...
212  ScanProbesDict spd;
213  spd.expiry = cur.second->GetExpiryTime();
214  spd.maxDeviation = cur.second->GetDeviation();
215  spd.probeID = cur.first;
216  spd.state = cur.second->GetState();
217  spd.rangeStep = cur.second->GetRangeStep();
218  spd.scanRange = cur.second->GetScanRange();
219  spd.scanStrength = cur.second->GetScanStrength();
220  spd.typeID = cur.second->GetSelf()->typeID();
221  pos = cur.second->GetPosition();
222  ScanResultPos srp;
223  srp.x = pos.x;
224  srp.y = pos.y;
225  srp.z = pos.z;
226  PyToken* token = new PyToken("foo.Vector3");
227  PyTuple* oed_tuple = new PyTuple(2);
228  oed_tuple->SetItem(0, token);
229  oed_tuple->SetItem(1, srp.Encode());
230  spd.pos = new PyObjectEx(false, oed_tuple); // oed goes here
231  PyIncRef(spd.pos);
232  spd.destination = spd.pos;
233  probeDict->SetItem(new PyInt(cur.first), spd.Encode());
234  }
235 
236  OnSystemScanStarted ossst;
237  ossst.timestamp = GetFileTimeNow();
238  ossst.duration = duration;
239  ossst.scanProbesDict = probeDict;
240  PyTuple* ev = ossst.Encode();
241  ev->Dump(SCAN__RSPDUMP, "sss- ");
242  m_client->SendNotification("OnSystemScanStarted", "charid", &ev, false);
243 }
244 
246  // WORKING CODE...DONT FUCK WITH THIS!! -allan 11Dec15
248  // client scan data found in EVE_Scanning.h
249  std::vector<CosmicSignature> anom;
250  if (m_client->IsShowall()) {
251  m_system->GetAllEntities(anom);
252  // bubble centers only populate when bubble markers are enabled.
253  sBubbleMgr.GetBubbleCenterMarkers(m_system->GetID(), anom);
254  } else {
256  }
257 
258  PyList* resultList = new PyList();
259  // NOTE. cannot scan pos, wrecks, ships, mission sites, or escalations. they DO have sigIDs, and can get to type (25%), but no farther
260  for (auto anoms : anom) {
261  SystemScanResult ssr;
262  ssr.typeID = anoms.sigTypeID;
263  ssr.scanGroupID = anoms.scanGroupID;
264  ssr.groupID = anoms.sigGroupID;
265  ssr.strengthAttributeID = anoms.scanAttributeID;
266  ssr.dungeonName = anoms.sigName;
267  ssr.id = anoms.sigID;
268  ssr.deviation = 0; /* for scan probes */
269  ssr.degraded = false; /* dunno what this does. have only seen 'false' in packets */
270  ssr.probeID = new PyInt(m_client->GetShipID());
271  ssr.certainty = anoms.sigStrength;
272  ssr.pos = PyStatic.NewNone();
273  ScanResultPos ssr_oed;
274  ssr_oed.x = anoms.position.x;
275  ssr_oed.y = anoms.position.y;
276  ssr_oed.z = anoms.position.z;
277  PyTuple* oed_tuple = new PyTuple(2);
278  oed_tuple->SetItem(0, new PyToken("foo.Vector3"));
279  oed_tuple->SetItem(1, ssr_oed.Encode());
280  ssr.data = new PyObjectEx(false, oed_tuple); // oed goes here
281  resultList->AddItem(ssr.Encode());
282  }
283 
284  OnSystemScanStopped osss;
285  osss.systemScanResult = resultList;
286  // probeDict and absentTargets are both empty when using ship sensors to scan.
287  osss.scanProbesDict = new PyDict();
288  osss.absentTargets = new PyList();
289  PyTuple* ev = osss.Encode();
290  ev->Dump(SCAN__RSPDUMP, "ssr- ");
291  m_client->SendNotification("OnSystemScanStopped", "charid", &ev);
292 }
293 
295 {
296  // this will use outline of above code, but be MUCH more complicated...
297  _log(SCAN__TRACE, "Scan::ProbeScanResult() for %s in system %u", m_client->GetName(), m_client->GetSystemID());
298 
299  PyList* resultList = new PyList();
300  std::vector<CosmicSignature> sig, anom;
301 
302  // are anomalies shown in probe scan results? config option?
304  for (auto anoms : anom) {
305  SystemScanResult ssr;
306  ssr.typeID = anoms.sigTypeID;
307  ssr.scanGroupID = anoms.scanGroupID;
308  ssr.groupID = anoms.sigGroupID;
309  ssr.strengthAttributeID = anoms.scanAttributeID;
310  ssr.dungeonName = anoms.sigName;
311  ssr.id = anoms.sigID;
312  ssr.deviation = 0; /* 0 for anomalies */
313  ssr.degraded = false;
314  ssr.probeID = new PyInt(m_client->GetShipID());
315  ssr.certainty = anoms.sigStrength;
316  ssr.pos = new PyToken("foo.Vector3"); //PyStatic.NewNone();
317  /* this is ship position first, then anomaly position
318  * [PyString "data"]
319  * [PyObjectEx Type2]
320  * [PyTuple 2 items]
321  * [PyTuple 1 items]
322  * [PyObjectEx Type2]
323  * [PyTuple 2 items]
324  * [PyTuple 1 items]
325  * [PyToken foo.Vector3]
326  * [PyTuple 3 items]
327  * [PyFloat 924428329391.783]
328  * [PyFloat 8194006649.63061]
329  * [PyFloat 165918840569.438]
330  * [PyTuple 3 items]
331  * [PyFloat 1231197245226]
332  * [PyFloat -146772910080]
333  * [PyFloat 111531171840]
334  */
335  ScanResultPos ssr_oed;
336  ssr_oed.x = anoms.position.x;
337  ssr_oed.y = anoms.position.y;
338  ssr_oed.z = anoms.position.z;
339  PyToken* token = new PyToken("foo.Vector3");
340  PyTuple* oed_tuple = new PyTuple(2);
341  oed_tuple->SetItem(0, token);
342  oed_tuple->SetItem(1, ssr_oed.Encode());
343  ssr.data = new PyObjectEx(false, oed_tuple); // oed goes here
344  resultList->AddItem(ssr.Encode());
345  }
346 
348  for (auto sigs : sig) {
349  SignalData data = SignalData();
350  data.sig = sigs;
351  data.probes = nullptr;
352  data.probePos = nullptr;
353  if (GetProbeDataForSig(data)) {
354  SystemScanResult ssr;
355  ssr.id = sigs.sigID;
356  ssr.dungeonName = sigs.sigName;
357  ssr.typeID = sigs.sigTypeID;
358  ssr.groupID = sigs.sigGroupID;
359  ssr.scanGroupID = sigs.scanGroupID;
360  ssr.strengthAttributeID = sigs.scanAttributeID;
361  ssr.degraded = false;
362  ssr.deviation = data.deviation; //deviation is the distance between the scan result shown on the map and the actual location of your target.
363  ssr.certainty = data.certainty; // this is listed as "signal strength" in scan window
364  ssr.probeID = data.probes;
365  ssr.pos = data.probePos;
366  ScanResultPos ssr_oed;
367  ssr_oed.x = data.sig.position.x;
368  ssr_oed.y = data.sig.position.y;
369  ssr_oed.z = data.sig.position.z;
370  PyToken* token = new PyToken("foo.Vector3");
371  PyTuple* oed_tuple = new PyTuple(2);
372  oed_tuple->SetItem(0, token);
373  oed_tuple->SetItem(1, ssr_oed.Encode());
374  ssr.data = new PyObjectEx(false, oed_tuple); // oed goes here
375  resultList->AddItem(ssr.Encode());
376  }
377  }
378 
379  GPoint pos(NULL_ORIGIN);
380  PyDict* probeDict = new PyDict();
381  for (auto cur : m_activeProbeMap) {
382  // probe data here...
383  ScanProbesDict spd;
384  spd.expiry = cur.second->GetExpiryTime();
385  spd.maxDeviation = cur.second->GetDeviation();
386  pos = cur.second->GetPosition();
387  ScanResultPos ssr_oed;
388  ssr_oed.x = pos.x;
389  ssr_oed.y = pos.y;
390  ssr_oed.z = pos.z;
391  PyToken* token = new PyToken("foo.Vector3");
392  PyTuple* oed_tuple = new PyTuple(2);
393  oed_tuple->SetItem(0, token);
394  oed_tuple->SetItem(1, ssr_oed.Encode());
395  spd.pos = new PyObjectEx(false, oed_tuple); // oed goes here
396  spd.destination = spd.pos;
397  spd.probeID = cur.first;
398  spd.state = cur.second->GetState();
399  spd.rangeStep = cur.second->GetRangeStep();
400  spd.scanRange = cur.second->GetScanRange();
401  spd.scanStrength = cur.second->GetScanStrength();
402  spd.typeID = cur.second->GetSelf()->typeID();
403  probeDict->SetItem(new PyInt(cur.first), spd.Encode());
404  }
405 
406  // this will be sigs that are no longer present in current scan range
407  // will have to keep previous list and compare with current list to populate this
408  // may not be used.....testing
409  PyList* absentList = new PyList();
410 
411  OnSystemScanStopped osssp;
412  osssp.scanProbesDict = probeDict;
413  osssp.systemScanResult = resultList;
414  osssp.absentTargets = absentList;
415  PyTuple* ev = osssp.Encode();
416  ev->Dump(SCAN__RSPDUMP, "psr- ");
417  m_client->SendNotification("OnSystemScanStopped", "charid", &ev);
418 }
419 
420 
421 /* probeID is for those probes that pick up this signal.
422  * pos is probe position
423  * will have to call a method to determine sig type, probe data, and signal specifics for each signal
424  */
426 {
427  /* this will determine sig position vs probe position, range, strength
428  * to decide if this sig is picked up by a probe, and return probe data
429  * based on that.
430  *
431 struct CosmicSignature {
432  uint8 dungeonType;
433  uint16 sigTypeID;
434  uint16 sigGroupID;
435  uint16 scanGroupID;
436  uint16 scanAttributeID;
437  uint32 ownerID;
438  uint32 systemID;
439  uint32 sigItemID; // itemID of this entry
440  GPoint position;
441  std::string sigID; // this is unique xxx-nnn id displayed in scanner
442  std::string sigName;
443 };
444  */
445 
446  bool hit(false);
447  float dist(0);
448  std::vector<ProbeSE*> probeVec;
449  // check probe scan ranges for signals; verify probe can scan signal
450  for (auto cur : m_activeProbeMap) {
451  // reset ring/sphere checks
452  cur.second->SetRing(false);
453  cur.second->SetSphere(false);
454  switch (data.sig.scanGroupID) {
458  if (cur.second->CanScanShips()) {
459  dist = cur.second->GetPosition().distance(data.sig.position);
460  if (cur.second->GetScanRange() > dist) {
461  hit = true;
462  probeVec.push_back(cur.second);
463  }
464  _log(SCAN__DEBUG, "Scan::GetProbeDataForSig() scan range for probe %u: %.2fAU, distance to signal '%s' -> %.2fAU - %s",\
465  cur.first, cur.second->GetScanRange() /ONE_AU_IN_METERS, data.sig.sigName.c_str(), \
466  dist /ONE_AU_IN_METERS, hit?"hit":"miss");
467  } else {
468  _log(SCAN__TRACE, "Scan::GetProbeDataForSig() probe %u cannot scan signal %s", cur.first, data.sig.sigName.c_str());
469  }
470  } break;
472  switch (data.sig.dungeonType) {
473  case Dungeon::Type::Mission: // npc mission
474  case Dungeon::Type::Escalation:// new dungeon from previous site. very limited access
475  case Dungeon::Type::Rated: { // DED rated dungeon
476  _log(SCAN__TRACE, "Scan::GetProbeDataForSig() probe %u cannot scan signal %s", cur.first, data.sig.sigName.c_str());
477  continue;
478  }
479  /* nothing to check for on these...
480  case Dungeon::Type::Gravimetric:// roids
481  case Dungeon::Type::Magnetometric:// salvage and archeology
482  case Dungeon::Type::Radar:// hacking
483  case Dungeon::Type::Ladar: // gas mining
484  case Dungeon::Type::Wormhole:
485  case Dungeon::Type::Anomaly:// non-rated dungeon that isnt required to scan with probes - will not hit here.
486  case Dungeon::Type::Unrated:// non-rated dungeon no waves, possible escalation to complex
487  break;
488  */
489  }
490  } // this probe can scan this signal; fall thru to add probe to signal's map
491  default: {
492  dist = cur.second->GetPosition().distance(data.sig.position);
493  if (cur.second->GetScanRange() > dist) {
494  hit = true;
495  probeVec.push_back(cur.second);
496  }
497  _log(SCAN__DEBUG, "Scan::GetProbeDataForSig() scan range for probe %u: %.2fAU, distance to signal '%s' -> %.2fAU - %s",\
498  cur.first, cur.second->GetScanRange() /ONE_AU_IN_METERS, data.sig.sigName.c_str(), \
499  dist /ONE_AU_IN_METERS, hit?"hit":"miss");
500  } break;
501  }
502  hit = false;
503  }
504 
505  _log(SCAN__TRACE, "Scan::GetProbeDataForSig() probeVec size: %u for signal %s (%s)", \
506  probeVec.size(), data.sig.sigName.c_str(), m_system->GetAnomMgr()->GetScanGroupName(data.sig.scanGroupID));
507 
508  if (probeVec.empty())
509  return false;
510 
511  // at this point, we have at least one probe scanning at least one signal
512  GetSignalData(data, probeVec);
513  if (probeVec.size() > 1) {
514  bool isRing(false);
515  uint8 count(0);
516  GPoint pos(NULL_ORIGIN);
517  PyList* list = new PyList();
518  PyList* ring = new PyList();
519  PyTuple* tuple = new PyTuple(probeVec.size());
520  for (auto cur : probeVec) {
521  tuple->SetItem(count++, new PyInt(cur->GetID()));
522  pos = cur->GetPosition();
523  ScanResultPos ssr_oed;
524  ssr_oed.x = pos.x;
525  ssr_oed.y = pos.y;
526  ssr_oed.z = pos.z;
527  PyToken* token = new PyToken("foo.Vector3");
528  PyTuple* oed_tuple = new PyTuple(2);
529  oed_tuple->SetItem(0, token);
530  oed_tuple->SetItem(1, ssr_oed.Encode());
531  list->AddItem(new PyObjectEx(false, oed_tuple));
532  if (cur->IsRing()) {
533  isRing = true;
534  ring->AddItem(new PyObjectEx(false, oed_tuple));
535  }
536  }
537  if (isRing)
538  list->AddItem(ring);
539  data.probes = tuple;
540  data.probePos = list;
541  // there is *something* here where one of the positions is given as a nested list of objects
542  /*
543  [PyString "pos"]
544  [PyList 6 items]
545  [PyObjectEx Type2]
546  [PyTuple 2 items]
547  [PyTuple 1 items]
548  [PyToken foo.Vector3]
549  [PyTuple 3 items]
550  [PyFloat 506169425920]
551  [PyFloat 948782891008]
552  [PyFloat 154119258112]
553  [PyList 3 items]
554  [PyObjectEx Type2]
555  [PyTuple 2 items]
556  [PyTuple 1 items]
557  [PyToken foo.Vector3]
558  [PyTuple 3 items]
559  [PyFloat 506169425920]
560  [PyFloat -555071897600]
561  [PyFloat 1636387389440]
562  */
563  } else {
564  ScanResultPos ssr_oed;
565  ssr_oed.x = probeVec.at(0)->GetPosition().x;
566  ssr_oed.y = probeVec.at(0)->GetPosition().y;
567  ssr_oed.z = probeVec.at(0)->GetPosition().z;
568  PyToken* token = new PyToken("foo.Vector3");
569  PyTuple* oed_tuple = new PyTuple(2);
570  oed_tuple->SetItem(0, token);
571  oed_tuple->SetItem(1, ssr_oed.Encode());
572  data.probes = new PyInt(probeVec.at(0)->GetID());
573  if (probeVec.at(0)->IsSphere())
574  ; // placeholder. no clue how to do this yet
575  data.probePos = new PyObjectEx(false, oed_tuple);
576  }
577  return true;
578 }
579 
580 void Scan::GetSignalData(SignalData& data, std::vector<ProbeSE*>& probeVec)
581 {
582  // probeVec has only probes than are in range of a signal it can scan
583  uint8 probeCount = probeVec.size();
584  float probeMultiplier(0.0f);
585  switch(probeCount) {
586  // new style...already calculated (in python) for 1 to 8 probes
587  case 1: probeMultiplier = 0.25774312594204907; break;
588  case 2: probeMultiplier = 0.5130245854773758; break;
589  case 3: probeMultiplier = 0.7234132613571191; break;
590  case 4: probeMultiplier = 0.8824741410676007; break;
591  case 5: probeMultiplier = 0.9963325352118082; break;
592  case 6: probeMultiplier = 1.0754155621393995; break;
593  case 7: probeMultiplier = 1.1296251734489133; break;
594  case 8: probeMultiplier = 1.1666968137637062; break;
595  }
596 
597  GPoint point(data.sig.position);
598  data.deviation = 0;
599  float scanStr1(0), rangeMod1(0), dist1(0), scanStr2(0), rangeMod2(0), dist2(0), angleMod(0);
600  if (probeCount == 1) {
601  dist1 = probeVec.at(0)->GetPosition().distance(point);
602  rangeMod1 = probeVec.at(0)->GetRangeModifier(dist1);
603  scanStr1 = probeVec.at(0)->GetScanStrength();
604  data.deviation = probeVec.at(0)->GetDeviation() *1.3; // fudge a bit for single probe
605  data.certainty = data.sig.sigStrength * (scanStr1 / rangeMod1) / 2;
606  _log(SCAN__TRACE, "Scan::GetSignalData(1) dist: %.3fAU, rangeMod: %.5f, scanStr: %.5f", \
607  dist1 / ONE_AU_IN_METERS, rangeMod1, scanStr1);
608  } else {
609  /* loop thru probes and get range mods and sigStrength for each.
610  * combine all probe's data to get good sum based on probe range and strength
611  */
612  int8 count(0), max(2);
613  if (probeVec.at(0)->HasMaxSkill())
614  max = 3;
615  std::map<float, std::pair<ProbeSE*, ProbeSE*>> angleMap; // angle, <probeSE1,probeSE2>
616  CalcProbeAngles(point, probeVec, angleMap); //determine probe angles to target
617  float probeSig1(0), probeSig2(0);
618  // reverse-iterate to use highest values first
619  std::map<float, std::pair<ProbeSE*, ProbeSE*>>::reverse_iterator itr = angleMap.rbegin(), end = angleMap.rend();
620  for (; itr != end; ++itr) {
621  // we are using top 3 angle pairs of 2 probes/angle, which gives 6 total scans for str calc.
622  // if player has signal acquisition and sensor linking both at l5, this will allow another pair of scan results
623  if (count > max)
624  break;
625  angleMod = sin(itr->first / 2); // get value between 0 and 1 (fuzzy logic as angle modifier)
626  // get sigStr from first probe
627  dist1 = itr->second.first->GetPosition().distance(point);
628  rangeMod1 = itr->second.first->GetRangeModifier(dist1);
629  scanStr1 = itr->second.first->GetScanStrength();
630  probeSig1 = data.sig.sigStrength * angleMod * probeMultiplier * (scanStr1 / rangeMod1);
631  data.certainty += probeSig1;
632  // get sigStr from second probe
633  dist2 = itr->second.second->GetPosition().distance(point);
634  rangeMod2 = itr->second.second->GetRangeModifier(dist2);
635  scanStr2 = itr->second.second->GetScanStrength();
636  probeSig2 = data.sig.sigStrength * angleMod * probeMultiplier * (scanStr2 / rangeMod2);
637  data.certainty += probeSig2;
638  // get deviation from both probes
639  data.deviation += itr->second.first->GetDeviation();
640  data.deviation += itr->second.second->GetDeviation();
641  _log(SCAN__TRACE, "Scan::GetSignalData(%u) #%u - angle %.3f (P1 %u, P2 %u) - dist: %.3fAU, %.3fAU, rangeMod: %.5f, %.5f, scanStr: %.5f, %.5f, angleMod: %.4f, multiplier: %.5f, probeSig: %.5f, %.5f", \
642  probeCount, ++count, EvE::Trig::Rad2Deg(itr->first), itr->second.first->GetID(), itr->second.second->GetID(), dist1 /ONE_AU_IN_METERS, dist2 /ONE_AU_IN_METERS,\
643  rangeMod1, rangeMod2, scanStr1, scanStr2, angleMod, probeMultiplier, probeSig1, probeSig2);
644  }
645  // get average deviation from all probes
646  data.deviation /= count *2;
647  }
648 /*Results, and What They Mean
649 
650  Sphere: The result is somewhere in the sphere. One probe only has a hit and approximate range. You can get multiple probes with independent hits, it can be messy. This is the least accurate hit.
651  Circle: Two probes have approximate distances. More accurate, and likely to yield better hits with more probes. Target anomaly is located near the circle.
652  Two Dots: Three probes have distances, which narrows it down to between two possible locations. Likely to be more or less accurate, but beware of deviation. Target anomaly has been narrowed down to being near one of the dots.
653  One Dot: Four or more probes can see the result. Once scan strength reaches 100%, the result will be warpable.
654 */
655  if ((data.certainty < 0.05) and (probeCount == 1)) { // set sphere
656  probeVec.at(0)->SetSphere(true);
657  } else if ((data.certainty < 0.08) and (probeCount == 2) ) {
658  // set ring
659  probeVec.at(0)->SetRing(true);
660  probeVec.at(1)->SetRing(true);
661  } else if (data.certainty > 0.99) {
662  sStatMgr.Increment(Stat::sitesScanned);
663  }
664 
665  // adjust deviation based on signal strength
666  /*
667  if (data.certainty < 0.8f)
668  data.deviation *= (1 + (1 - data.certainty));
669  else
670  data.deviation *= (1 - data.certainty);
671  */
672 
674  //point.MakeRandomPointOnSphereLayer(data.deviation /2, data.deviation);
675  data.sig.position = point;
676 
677  _log(SCAN__TRACE, "Scan::GetSignalData() - certainty for signal %s(%s) is %.5f (sigStrength:%.5f) \n Deviation: %.0fm (%.3f AU)", \
678  data.sig.sigName.c_str(), data.sig.sigID.c_str(), data.certainty, data.sig.sigStrength, data.deviation, (data.deviation / ONE_AU_IN_METERS));
679 }
680 
681 void Scan::CalcProbeAngles(GPoint& sigPos, std::vector<ProbeSE*>& probeVec, std::map<float, std::pair<ProbeSE*, ProbeSE*>>& angleMap ) {
682  bool run(true);
683  uint8 count(probeVec.size()), x(0), y(1);
684  float dot(0.0f), angle(0.0f);
685  ProbeSE* p1(nullptr);
686  while (x < count) {
687  p1 = probeVec.at(x++);
688  if (p1 == nullptr)
689  continue;
690  GVector v1(p1->GetPosition(), sigPos);
691  v1.normalize();
692  for (auto cur : probeVec) {
693  if ((cur == nullptr) or (p1 == cur))
694  continue;
695  GVector v2(cur->GetPosition(), sigPos);
696  v2.normalize();
697  dot = v1.dotProduct(v2); // cosine of angle between v1,v2
698  angle = acos(dot); // angle in rad
699  // if this probe set is already inserted, skip and move along.
700  if (angleMap.find(angle) != angleMap.end())
701  continue;
702  // add to map
703  angleMap[angle] = std::make_pair(p1, cur);
704  _log(SCAN__DEBUG, "Scan::CalcProbeAngles() - adding angle %.3f, p1 %u, p2 %u to map",\
705  /*EvE::Trig::Rad2Deg*/(angle), p1->GetID(), cur->GetID());
706  }
707  }
708 }
709 
710 // ********* apoc or before.....
711 /* probe formulas....
712  *
713  * single...
714  * sp = sb*(1+0.05(a+ar))*(1+l)*(1+h)*pi(1+p*Fi))
715  * sb is probe base str
716  * a/ar is astrometrics and rangefinding
717  * l is launcher bonus
718  * h is hull bonus
719  * fi is fit bonuses
720  * p is stack penalty
721  *
722  * s3 is effective sig str. ease with which a given target can be scanned
723  * ship/struct sig str is ratio of sig radius to effective sig str of probe
724  *
725  * nr is probe scan radius. distance from probe to target with radius of probe.
726  * usually given in AU, but could be steps (nr) above min radius (rb = 0.25 for core, 0.5 for combat) where
727  * r = rb*2^nr-1
728  *
729  *
730  * multiple....
731  * str = (probe average)*2*(a1+a2+a3+a4)/360;
732  * avg is from best 4 probes (within range)
733  * a1,a2,a3,a4 = angles of best 4 probes (others disregarded)
734  */
735 
736 // post from 2013
737 /* probe formula...incomplete and some functions i cant do, so we're using apoc
738  *
739  * formula steps
740  * 1) eval sig str of each probe
741  * 2) eval angles of probe pairs to target
742  * 3) eval total sig str
743  *
744  * 1 - eval sig str for probe
745  * base sig str = size * probe str * distancMod /4
746  * sig str = f(base sig str) + g(f(base sig str), position)
747  *
748  * size = targ sig radius
749  * probe str = probe base str * bonus mods / range mods
750  * dist mod = e^-((targ range/max range)^2)
751  * pos = 3d coords of probe relative to target
752  * f() = asymptotic function where f(x)=x below 25%, then diminishing returns to f(x)=75% for x=infinity
753  * g(x,y) = returns percentage of x, between 1.5 and 3.5 (or 1 and 4)
754  *
755  * 2 - eval angles of probe pairs
756  * compute angle between targ and each pair of probes
757  * angleMod(a,b) = beta(angle(a,b),x,y)
758  * a = probe 1 (arbitrary)
759  * b = probe 2 (arbitrary)
760  * beta = regularized beta function (found in boost as `ibeta(a,b,x)`)
761  * angle = actual angle between a and b that intersects the signal
762  * x, y = ratio between str of probe a, probe b and strongest probe
763  *
764  * 3 - eval total sig str
765  * final = sigstrP1 + sigstrP2*(angleMod(p2,p1) + sigstrP3*(anglemod(P3,P1)*anglemod(p3,p2)) ... [to max of 8 probes?]
766  */
767 
768 
769 // unscannable ship is (sigRad/sensorStr < 1.08)
770 
771 /* center of tetrahedron formula
772  * (((x1+x2+x3+x4) /4), ((y1+y2+y3+y4) /4), ((z1+z2+z3+z4) /4))
773  */
774 /*
775  One probe will only tell you if something is in range of the probe.
776 This will generate a red sphere inside your probe’s bubble that is centered on your probe.
777 The sphere gives you a general idea of how far the target is from your probe.
778  Two probes will tell you that something exists on an imaginary ring which shows on what plane and the general area where your target is.
779  Three probes will produce two possible locations for your target.
780 These are still not warpable and will usually show up as just one small circle on your map screen but there will be two entries under your scan results screen with the same ID#.
781  Four or more probes will give you a single location shown by a red (less than 50% strength), yellow (50-99%), or green (100% hit).
782 By maneuvering your probes and decreasing their range they will eventually give a warpable result when the signal strength reaches 100% and turns green.
783 */
784 
785 
786 // these are for moon scanning
788 {
789 
790 }
791 
793  /*
794  *
795  * [PyString "OnSpecialFX"]
796  * [PyTuple 14 items]
797  * [PyIntegerVar 1002331681462]
798  * [PyIntegerVar 1002332233248]
799  * [PyInt 444]
800  * [PyNone]
801  * [PyNone]
802  * [PyList 0 items]
803  * [PyString "effects.SurveyScan"]
804  * [PyBool False]
805  * [PyInt 1]
806  * [PyInt 1]
807  * [PyFloat 4250]
808  * [PyInt 0]
809  * [PyIntegerVar 129509430135552798]
810  * [PyNone]
811  *
812  */
813 }
uint32 GetShipID() const
Definition: Client.h:150
Base Python wire object.
Definition: PyRep.h:66
PyTuple * AsTuple()
Definition: PyRep.h:138
unsigned __int8 uint8
Definition: eve-compat.h:46
#define sStatMgr
Definition: StatisticMgr.h:68
void SendNotification(const PyAddress &dest, EVENotificationStream &noti, bool seq=true)
Definition: Client.cpp:2245
void GetSignatureList(std::vector< CosmicSignature > &sig)
Definition: AnomalyMgr.cpp:192
double value() const
Definition: PyRep.h:309
bool empty() const
Definition: PyRep.h:770
void AddProbe(ProbeSE *pProbe)
Definition: Scan.cpp:45
uint32 GetSystemID() const
Definition: Client.h:152
itemID[count] Create count or of the specified() x() y(z)-Jump to the specified position in space.Stopped." ) COMMAND( translocate
#define _log(type, fmt,...)
Definition: logsys.h:124
PyRep * GetItem(size_t index) const
Returns Python object.
Definition: PyRep.h:602
Python's dictionary.
Definition: PyRep.h:719
Scan(Client *pClient)
Definition: Scan.cpp:36
bool GetProbeDataForSig(SignalData &data)
Definition: Scan.cpp:425
std::string sigID
double Rad2Deg(double rad)
Definition: Trig.h:26
PyRep * probes
Definition: Scan.h:22
void GetSignalData(SignalData &data, std::vector< ProbeSE * > &probeVec)
Definition: Scan.cpp:580
const char * GetScanGroupName(uint8 groupID=0)
Definition: AnomalyMgr.cpp:539
int32 GetCharacterID() const
Definition: Client.h:113
void SystemScanStarted(uint16 duration)
Definition: Scan.cpp:204
void RemoveProbe(ProbeSE *pProbe)
Definition: Scan.cpp:51
uint32 GetID() const
Definition: SystemManager.h:80
static uint32 IntegerValueU32(PyRep *pRep)
Definition: PyRep.cpp:134
void CalcProbeAngles(GPoint &sigPos, std::vector< ProbeSE * > &probeVec, std::map< float, std::pair< ProbeSE *, ProbeSE * >> &angleMap)
Definition: Scan.cpp:681
std::string sigName
Python tuple.
Definition: PyRep.h:567
GaFloat x
Definition: GaTypes.h:207
void Dump(FILE *into, const char *pfx) const
Dumps object to file.
Definition: PyRep.cpp:84
signed __int8 int8
Definition: eve-compat.h:45
const GPoint & GetPosition() const
Definition: SystemEntity.h:211
void AddItem(PyRep *i)
Definition: PyRep.h:701
GaExpInl GaFloat normalize()
Definition: GaTypes.h:163
* args
Definition: gpoint.h:33
Python extended object.
Definition: PyRep.h:861
float deviation
Definition: Scan.h:20
AnomalyMgr * GetAnomMgr()
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
SystemManager * SystemMgr() const
Definition: Client.h:92
size_t size() const
Definition: PyRep.h:769
Definition: Probes.h:71
static const GPoint NULL_ORIGIN(0, 0, 0)
uint32 get_uint32()
Definition: EvilNumber.cpp:173
void RequestScans(PyDict *dict)
Definition: Scan.cpp:139
SystemManager * m_system
Definition: Scan.h:65
Python integer.
Definition: PyRep.h:231
float certainty
Definition: Scan.h:19
bool IsShowall()
Definition: Client.h:271
void SetScan(Scan *pScan)
Definition: Probes.h:104
uint32 GetID()
Definition: SystemEntity.h:207
bool m_probeScan
Definition: Scan.h:69
std::map< uint32, ProbeSE * > m_probeMap
Definition: Scan.h:71
#define PyStatic
Definition: PyRep.h:1209
PyRep * ConeScan(Call_ConeScan args)
Definition: Scan.cpp:99
void SetScanTimer(uint16 time, bool useProbe=false)
Definition: Client.h:306
ShipItemRef GetShip() const
Definition: Client.h:167
const char * GetName() const
Definition: Client.h:94
void GetAllEntities(std::vector< CosmicSignature > &vector)
std::map< uint32, ProbeSE * > m_activeProbeMap
Definition: Scan.h:72
void ProbeScanResult()
Definition: Scan.cpp:294
CosmicSignature sig
Definition: Scan.h:21
Definition: Client.h:66
unsigned __int32 uint32
Definition: eve-compat.h:50
if(sConfig.world.saveOnMove)
#define PyIncRef(op)
Definition: PyRep.h:56
void ShipScanResult()
Definition: Scan.cpp:245
void GetAnomalyList(std::vector< CosmicSignature > &sig)
Definition: AnomalyMgr.cpp:200
uint8 rangeStep
const_iterator begin() const
Definition: PyRep.h:766
Python token (eg. class name).
Definition: PyRep.h:522
GaFloat y
Definition: GaTypes.h:207
double GetFileTimeNow()
Definition: utils_time.cpp:84
ShipSE * GetShipSE()
Definition: Client.h:168
int64 max(int64 x, int64 y=0)
Definition: misc.h:103
storage_type::const_iterator const_iterator
Definition: PyRep.h:750
PyRep * header() const
Definition: PyRep.h:886
EvilNumber GetAttribute(const uint16 attrID) const
void DScan(int64 range, const GPoint &pos, std::vector< SystemEntity * > &vector)
void ProcessScan(bool useProbe=false)
Definition: Scan.cpp:56
const_iterator end() const
Definition: PyRep.h:767
PyRep * probePos
Definition: Scan.h:23
void ScanStart()
Definition: Scan.cpp:787
Definition: gpoint.h:70
PyFloat * AsFloat()
Definition: PyRep.h:126
std::string GetSystemName() const
Definition: Client.h:155
#define sBubbleMgr
unsigned __int16 uint16
Definition: eve-compat.h:48
float scanRange
void SetItem(PyRep *key, PyRep *value)
SetItem adds or sets a database entry.
Definition: PyRep.cpp:713
Client * m_client
Definition: Scan.h:64
Python list.
Definition: PyRep.h:639
GaFloat z
Definition: GaTypes.h:207
static const int64 ONE_AU_IN_METERS
Definition: EVE_Consts.h:40
void SurveyScan()
Definition: Scan.cpp:792