EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ActiveModule.cpp
Go to the documentation of this file.
1 
9 #include "eve-server.h"
10 
11 #include "StatisticMgr.h"
12 #include "exploration/Probes.h"
13 #include "exploration/Scan.h"
14 #include "ship/Missile.h"
18 #include "system/Container.h"
20 
21 
23 : GenericModule(mRef, sRef),
24 m_timer(0, true),
25 m_reloadTimer(0),
26 m_bubble(nullptr),
27 m_sysMgr(nullptr),
28 m_targMgr(nullptr),
29 m_targetSE(nullptr),
30 m_destinyMgr(nullptr),
31 m_usesCharge(false),
32 m_needsCharge(false),
33 m_needsTarget(false),
34 m_targetID(0),
35 m_effectID(0),
36 m_Stop(true)
37 {
38  m_repeat = 1000; //based on client data
39 
40  // civilian turrets dont use charges. this is checked/hacked in TurretModule() to fix error when firing.
42  if (m_needsCharge) {
43  switch (mRef->groupID()) {
44  // these neither require nor consume charges...may be wrong. some of these use scripts. to verify
54  // t2 mining laser can be used without charge by using default extraction rate
56  m_usesCharge = true;
57  m_needsCharge = false;
58  } break;
59  }
60  }
61 
62  // this is an internal variable only.
64  /* our defaults:
65  * 4s for turrets and scrips
66  * 5s for snowball and probe launchers
67  * 6s for miners and remote scripts
68  * 7s for missile launchers
69  * 10s for others.
70  */
71  if (m_needsCharge or m_usesCharge) {
72  if (m_reloadTime < 1) {
73  switch (mRef->groupID()) {
82  m_reloadTime = 4000;
83  } break;
86  m_reloadTime = 5000;
87  } break;
91  m_reloadTime = 6000;
92  } break;
103  m_reloadTime = 7000;
104  } break;
105  default: {
106  m_reloadTime = 10000;
107  } break;
108  }
109  }
110  }
111 
112  //Clear();
113  //GM_Modules = 353,
114 
115  if (m_reloadTime or m_usesCharge) {
116  _log(MODULE__TRACE, "Reload time for %s(%u) set to %ums. (uses charge: %s", \
117  mRef->name(), mRef->itemID(), m_reloadTime, m_usesCharge?"true":"false");
118  } else {
119  _log(MODULE__TRACE, "%s(%u) does not use reload time.", mRef->name(), mRef->itemID());
120  }
121 
122  if (!m_shipRef->HasPilot())
123  return;
124 
125  // these groups receive a 3% increase in scan range
126  switch (mRef->groupID()) {
128  float range = GetAttribute(AttrShipScanRange).get_float();
129  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
131  } break;
133  float range = GetAttribute(AttrCargoScanRange).get_float();
134  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
136  } break;
138  float range = GetAttribute(AttrSurveyScanRange).get_float();
139  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
141  } break;
144  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
146  } break;
150  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
152  } break;
155  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
157  } break;
165  float range = GetAttribute(AttrMaxRange).get_float();
166  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
167  SetAttribute(AttrMaxRange, range);
168  } break;
169  /* these are 50AU. we dont need to increase it...
170  case EVEDB::invGroups::System_Scanner: {
171  float range = GetAttribute(AttrScanRange).get_float(); // range in AU
172  range *= (1 + (0.03 * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
173  SetAttribute(AttrScanRange, range);
174  } break;
175  */
176  }
177 }
178 
180 {
181  if (!m_shipRef->HasPilot())
182  return;
183 
184  // these groups receive a 3% increase in scan range
185  switch (m_modRef->groupID()) {
188  float range = GetAttribute(AttrShipScanRange).get_float();
189  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
191  } break;
194  float range = GetAttribute(AttrCargoScanRange).get_float();
195  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
197  } break;
200  float range = GetAttribute(AttrSurveyScanRange).get_float();
201  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
203  } break;
207  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
209  } break;
214  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
216  } break;
220  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
222  } break;
231  float range = GetAttribute(AttrMaxRange).get_float();
232  range *= (1 + (0.03f * (m_shipRef->GetPilot()->GetChar()->GetSkillLevel(EvESkill::LongRangeTargeting, true))));
233  SetAttribute(AttrMaxRange, range);
234  } break;
235  }
236 }
237 
239 {
240  _log(MODULE__TRACE, "%s(%u) calling Clear()", m_modRef->name(), m_modRef->itemID());
241  if (m_targetSE != nullptr)
242  if (m_targetSE->TargetMgr() != nullptr)
244 
245  m_targetSE = nullptr;
246 
247  m_Stop = true;
248  m_repeat = 1;
249  m_targetID = 0;
250  m_effectID = 0;
251  m_bubble = nullptr;
252  m_sysMgr = nullptr;
253  m_targMgr = nullptr;
254  m_targetSE = nullptr;
255  m_destinyMgr = nullptr;
256  m_needsTarget = false;
257 
258  m_timer.Disable();
259 
261 
263 }
264 
266 {
267  // the order of Reload/Unload is significant.
268  if (m_reloadTimer.Enabled()) {
269  if (m_reloadTimer.Check(false)) {
270  // charge loading complete
272  // apply charge effects here after loading is complete, but only for empty modules (no previous charge fx)
273  if (!m_chargeLoaded)
274  sFxProc.ApplyEffects(m_chargeRef.get(), m_shipRef->GetPilot()->GetChar().get(), m_shipRef.get(), true);
275 
277  m_chargeLoaded = true;
278  }
279  }
280 
282  return;
283 
284  // if chargestate is loading or reloading, deny further processing and let m_reloadTimer handle it.
286  _log(MODULE__TRACE, "ActiveModule::Process - %s on %s is loading.", m_modRef->name(), m_shipRef->name());
287  return;
288  }
289 
290  // decrement repeat and check for single activation.
291  --m_repeat;
292  if (m_repeat < 1)
293  m_Stop = true;
294 
295  // check for module cycle timer. if module is stopped, this will process the deactivation and set stop
296  if (m_timer.Check()) {
297  if (m_needsTarget) {
298  if (m_targetSE == nullptr) {
299  DeactivateCycle(true);
300  return;
301  }
302  if (m_targetSE->GetID() != m_targetID) {
303  DeactivateCycle(true);
304  return;
305  }
306  }
307 
309  }
310 }
311 
313  if (m_targetSE == pSE) {
314  _log(MODULE__TRACE, "ActiveModule::RemoveTarget called on %s on %s to remove %s", m_modRef->name(), m_shipRef->name(), pSE->GetName());
315  Deactivate();
316  }
317 }
318 
319 void ActiveModule::Activate(uint16 effectID, uint32 targetID/*0*/, int16 repeat/*0*/)
320 {
321  if (effectID == 16) {
322  // catchall for elusive online/offline error, but should be caught in Ship::Activate(), backup in MM::Activate()
323  sLog.Error("AM::Activate()", "effectID 16 got here.");
324  Online();
325  return;
326  }
327  if (m_needsCharge and (!m_chargeLoaded or (m_chargeRef.get() == nullptr))) {
328  _log(MODULE__TRACE, "ActiveModule::Activate - %s: needsCharge: %s, chargeLoaded: %s, chargeRef: %s", \
329  m_modRef->name(), m_needsCharge?"True":"False", m_chargeLoaded?"True":"False", \
330  m_chargeRef.get() == nullptr ? "(none)": m_chargeRef->name());
331  Clear();
332  throw CustomError ("Your %s doesn't seem to be loaded.", m_modRef->name());
333  }
334  if (IsValidTarget(targetID)) {
335  // this is just a guess. may have to use groupID test to verify if this doesnt work right.
336  // also need to make check for modules acting on OUR ship....in which case this will be wrong.
337  m_needsTarget = true;
338  m_targetID = targetID;
339  m_targetSE = m_shipRef->GetPilot()->SystemMgr()->GetSE(targetID);
340  if (m_targetSE == nullptr) {
341  Clear();
342  throw UserError ("DeniedActivateTargetNotPresent");
343  }
344  }
345 
346  if (m_targetSE != nullptr) {
347  /*
348  * AttrdisallowAgainstEwImmuneTarget
349  * AttrDisallowOffensiveModifiers
350  * AttrDisallowOffensiveModifierBonus
351  */
352 
353  // if target is non-combatant deny attack
354  if (sFxDataMgr.isOffensive(effectID))
356  // or (m_targetSE->IsLogin())) // this is incomplete, so always returns false
357  {
358  throw CustomError ("You cannot attack the %s.", m_targetSE->GetName());
359  }
360 
361  if (sFxDataMgr.isAssistance(effectID)) {
363  Clear();
364  throw UserError ("DeniedActivateTargetAssistDisallowed");
365  }
371  }
372  if (m_targetSE->IsCOSE()) {
373  Clear();
374  throw CustomError ("Attacking Customs Offices isn't implemented at this time.");
375  }
376  if (m_targetSE->TargetMgr() != nullptr)
378  }
379 
380  m_repeat = repeat;
381  m_effectID = effectID;
382 
383  if (!CanActivate()) {
384  Clear();
385  return;
386  }
387 
388  m_Stop = false;
389  m_isWarpSafe = sFxDataMgr.isWarpSafe(m_effectID);
390 
391  ShipSE* pShip = m_shipRef->GetPilot()->GetShipSE();
392  m_bubble = pShip->SysBubble();
393  m_sysMgr = pShip->SystemMgr();
394  m_targMgr = pShip->TargetMgr();
395  m_destinyMgr = pShip->DestinyMgr();
396 
397  // Do initial cycle immediately while we start timer
398  SetTimer(DoCycle());
399 
400  if (!m_timer.Enabled()) {
401  // if the timer wasnt set (for whatever reason), kill activation and return
402  Clear();
403  return;
404  }
405 
407  if (IsValidTarget(targetID))
409 
410  std::vector<GenericModule*> modules;
411  if (m_linkMaster) {
413  for (auto cur : modules) {
414  cur->GetActiveModule()->SetSlaveData(pShip);
415  cur->GetActiveModule()->ShowEffect(true, false);
416  }
417  } else {
418  ShowEffect(true, false);
419  }
420 
422 
423  switch (groupID()) {
427  } break;
429  if (m_targetSE != nullptr)
431  } break;
432  }
433  /*def OnSpecialFX
434  * if start and guid == 'effects.WarpScramble*':
435  * if settings.user.ui.Get('notifyMessagesEnabled', 1) or eve.session.shipid in (shipID, targetID):
436  * jammerName = sm.GetService('bracket').GetBracketName2(shipID)
437  * targetName = sm.GetService('bracket').GetBracketName2(targetID)
438  * if jammerName and targetName:
439  * if eve.session.shipid == targetID:
440  * eve.Message('WarpScrambledBy', {'scrambler': jammerName})
441  * elif eve.session.shipid == shipID:
442  * eve.Message('WarpScrambledSuccess', {'scrambled': targetName})
443  * else:
444  * eve.Message('WarpScrambledOtherBy', {'scrambler': jammerName,
445  * 'scrambled': targetName})
446  */
447 
448  --m_repeat;
449  if (m_repeat < 1)
450  m_Stop = true;
451 
452  // check for one-hit kills and stop module after cycle completes
453  if (m_needsTarget)
454  if (m_targetSE->IsDead())
455  m_Stop = true;
456 }
457 
458 void ActiveModule::Deactivate(std::string effect/*""*/)
459 {
461  _log(MODULE__TRACE, "ActiveModule::Deactivate - %s called Deactivate but is currently %s.", m_modRef->name(), GetModuleStateName(m_ModuleState));
462  return;
463  }
464 
465  _log(MODULE__TRACE, "ActiveModule::Deactivate(%s) - module %s(%u) remaining time %ums.", \
466  effect.c_str(), m_modRef->name(), m_modRef->itemID(), GetRemainingCycleTimeMS());
467 
469 
471  DeactivateCycle(true);
472  return;
473  }
474 
475  if (effect.compare("TargetDestroyed") == 0) {
476  m_targetSE = nullptr;
477  if (IsProspectModule())
479  }
480 
481  // let module complete current cycle then shut it down.
482  m_Stop = true;
483 }
484 
486  m_bubble = pShip->SysBubble();
487  m_sysMgr = pShip->SystemMgr();
488  m_targMgr = pShip->TargetMgr();
489  m_destinyMgr = pShip->DestinyMgr();
490 }
491 
493 {
494  m_overLoaded = true;
496 }
497 
499 {
501  m_overLoaded = false;
502 }
503 
505 {
506  if (m_destinyMgr == nullptr) {
507  // make error for no destiny/bubble
508  AbortCycle();
509  return 0;
510  }
511 
512  // Check for (13) modules which consume items per-cycle (they only consume single type)
514  uint16 typeID(m_modRef->GetAttribute(AttrConsumptionType).get_uint32()); // cast uint32 to uint16
516  // verify character has require amount of consumption type available
518  m_shipRef->GetPilot()->SendNotifyMsg("This module requires you to have %u units of %s in your cargo hold.", \
519  qtyNeed, sItemFactory.GetType(typeID)->name().c_str());
520  AbortCycle(); // CHECK THIS for initial activate
521  return 0;
522  } else {
525  if (iRef.get() != nullptr) {
526  if (iRef->quantity() > qtyNeed) {
527  //If we have all the quantity we need in the current stack, decrement the amount we need and break
528  iRef->AlterQuantity(-qtyNeed, true);
529  } else {
530  // Delete item
531  iRef->SetQuantity(0, true, true);
532  }
533  } else {
534  AbortCycle(); // CHECK THIS for initial activate
535  return 0;
536  }
537  }
538  }
539 
540  // not sure if this is entirely accurate...wip
541  switch (m_modRef->groupID()) {
545  // turret weapons use specific code.
546  ApplyDamage();
547  } break;
549  case EVEDB::invGroups::Missile_Launcher_Bomb: // not sure here
557  LaunchMissile();
558  } break;
560  ConsumeCharge();
562  m_repeat = 1;
563  } break;
564  // i *think* these first 2 go here....need testing
568  if (m_targetSE != nullptr)
570  } break;
572  if (m_targetSE != nullptr)
574  } break;
577  } break;
579  if (m_targetSE != nullptr)
581  } break;
584  } break;
586  if (m_targetSE != nullptr)
588  } break;
591  } break;
593  LaunchSnowBall();
594  } break;
596  LaunchProbe();
597  } break;
598  // these neither require nor consume charges
599  case EVEDB::invGroups::Salvager: //working
600  case EVEDB::invGroups::Target_Painter: // working
601  case EVEDB::invGroups::Signal_Amplifier: //working
602  case EVEDB::invGroups::Sensor_Booster: //working
610  } break;
612  } break;
613  // these passive modules will need specific code
617  } break;
618  // these active modules will need specific code
622  } break;
623  // these im not sure about yet
630  case EVEDB::invGroups::Interdiction_Sphere_Launcher: // launch a sphere (like missile and probe)
636  } break;
637  }
638 
639  // do heat damage if overloaded...this will be handled in shipItem class
640  if (m_overLoaded)
641  m_shipRef->HeatDamageCheck(this);
642 
643  EvilNumber cycleTime = 10000; // default to 10s
644  if (m_modRef->HasAttribute(AttrSpeed, cycleTime)) {
645  ; //return cycleTime.get_int();
646  } else if (m_modRef->HasAttribute(AttrDuration, cycleTime)) {
647  ; //return cycleTime.get_int();
648  }
649  return cycleTime.get_int();
650 }
651 
653 {
654  _log(MODULE__TRACE, "%s(%u) calling AbortCycle() - state: %s, m_stop:%s", \
656 
658  return;
659 
660  // if stop is already set, let module finish cycle.
661  if (m_Stop)
662  return;
663  // Immediately stop active cycle for things such as insufficient cap, remove module, init warp, target destroyed, target left bubble, or miner deactivated by player:
665  DeactivateCycle(true);
666  m_timer.Disable();
667  m_Stop = true;
668 }
669 
670 void ActiveModule::DeactivateCycle(bool abort/*false*/)
671 {
673  return;
674 
675  _log(MODULE__TRACE, "%s(%u) calling DeactivateCycle(abort: %s)", m_modRef->name(), m_modRef->itemID(), abort?"true":"false");
676  if ((m_ModuleState == Module::State::Activated) and (!abort)) {
677  _log(MODULE__ERROR, "ActiveModule::DeactivateCycle() - Called on %s(%u) with current state %s and !abort.", \
679  EvE::traceStack();
680  }
681 
682  std::vector<GenericModule*> modules;
683  if (m_linkMaster) {
685  for (auto cur : modules)
686  cur->GetActiveModule()->ShowEffect(false, abort);
687  } else {
688  ShowEffect(false, abort);
689  }
690 
691  // Remove modifier added by module
694  and (m_targetSE != nullptr))
696  /*
697  else if (m_needsTarget) {
698  _log(MODULE__INFO, "%s - DeactivateCycle() - need target = true and targetID: %u, targSE: %x", \
699  m_modRef->name(), m_targetID, m_targetSE);
700  } */
701 
702  switch (groupID()) {
704  if (m_targetSE != nullptr)
706  } break;
709  m_destinyMgr->SpeedBoost(true);
710  } break;
712  if (m_targetSE != nullptr)
714  } break;
716  if (abort) {
717  Clear();
718  return;
719  }
720  // this is the complete belt scanner rsp code here.
721  PyTuple* result = new PyTuple(2);
722  result->SetItem(0, new PyString("OnSurveyScanComplete"));
723  PyList* list = new PyList();
724  std::vector<AsteroidSE*> vList;
725  m_sysMgr->GetBeltMgr()->GetList(sBubbleMgr.GetBeltID(m_bubble->GetID()), vList);
726  // when roids are spawned, BeltMgr sets this bubble "IsBelt = true", even in anomalies
727  if (m_bubble->IsBelt()) {
728  float m_range = GetAttribute(AttrSurveyScanRange).get_float();
729  float distance = 0;
730  for (auto pASE : vList) {
731  distance = m_shipRef->position().distance(pASE->GetPosition());
732  distance -= pASE->GetRadius();
733  distance -= m_shipRef->radius(); // do we need this one here?
734  if (distance < m_range) {
735  PyTuple* tuple2 = new PyTuple(3);
736  tuple2->SetItem(0, new PyInt(pASE->GetID()));
737  tuple2->SetItem(1, new PyInt(pASE->GetTypeID()));
738  tuple2->SetItem(2, pASE->GetSelf()->GetAttribute(AttrQuantity).GetPyObject());
739  list->AddItem(tuple2);
740  }
741  }
742  } else if (m_bubble->IsIce()) {
743  // allow ice scanning without a radius check....may change later.
744  for (auto pASE : vList) {
745  PyTuple* tuple2 = new PyTuple(3);
746  tuple2->SetItem(0, new PyInt(pASE->GetID()));
747  tuple2->SetItem(1, new PyInt(pASE->GetTypeID()));
748  tuple2->SetItem(2, pASE->GetSelf()->GetAttribute(AttrQuantity).GetPyObject());
749  list->AddItem(tuple2);
750  }
751  }
752 
753  result->SetItem(1, list);
754  // Send results.
755  m_shipRef->GetPilot()->QueueDestinyEvent(&result);
756  } break;
757  //case EVEDB::invGroups::Data_Miner:
758  // some data containers will pop after successful access. currently incomplete
760  if (m_targetSE == nullptr)
761  break;
762  if (IsSuccess()) {
763  _log(MODULE__DEBUG, "%s - DeactivateCycle() - Salvage successful. deleting target %s.", m_modRef->name(), m_targetSE->GetName());
765  //m_targMgr->OnTarget(m_targetSE, TargMgr::Mode::Lost, TargMgr::Msg::Destroyed);
766  // just in case other modules are targeting this object, let them know it was destroyed.
767  if (m_targetSE->TargetMgr() != nullptr) {
770  }
771  m_targetSE->Delete();
773  }
774  } break;
775  /*
776  case EVEDB::invGroups::Warp_Scrambler: {
777  if (m_targetSE != nullptr)
778  m_targetSE->GetSelf()->SetAttribute(AttrWarpScrambleStatus);
779  } break;
780  case EVEDB::invGroups::Warp_Core_Stabilizer: {
781  if (m_targetSE != nullptr)
782  m_targetSE->GetSelf()->SetAttribute(AttrWarpScrambleStatus);
783  } break;
784  */
787  if (m_targetSE != nullptr)
788  ; // not sure if we need this here.....do these work like belt scanner?
789  } break;
790  // not sure just how this works yet
791  // case EVEDB::invGroups::System_Scanner:
792  }
793 
794  Clear();
795 }
796 
798  if (m_Stop)
800 
802  DeactivateCycle();
803  return;
804  }
805 
807  if (newCap >= 0 ) {
809  SetTimer(DoCycle());
810  } else {
811  AbortCycle();
812  }
813 }
814 
816  if (time < 100) {
817  m_Stop = true;
818  return;
819  }
820  // timer must be restarted for fleet boosts to activate
821  if (m_timer.Enabled())
822  return;
823  // updated timer will reset cycle time if changed, but i DO NOT have client display coded to reset...this will fuck up timer time in client. (worse than it already is)
824  _log(MODULE__TRACE, "ActiveModule::SetTimer() - %s with %u ms for %s on %s.", \
825  (m_timer.Enabled()? "Updated" : "Started"), time, m_modRef->name(), m_shipRef->name());
826  m_timer.Start(time);
827 }
828 
830 {
831  _log(MODULE__TRACE, "%s(%u) calling AM::LoadCharge()", m_modRef->name(), m_modRef->itemID());
832 
833  if (chargeRef.get() == nullptr) {
834  _log(MODULE__WARNING, "ActiveModule::LoadCharge() for %s - Cannot find charge to load into this module", m_modRef->name());
835  return;
836  }
837 
838  Client* pClient = m_shipRef->GetPilot();
839  if (pClient == nullptr) {
840  _log(MODULE__WARNING, "ActiveModule::LoadCharge() for %s - Pilot is null. Cannot load charge.", m_modRef->name());
841  // these are just in case...
842  m_chargeRef = InventoryItemRef(nullptr);
843  m_chargeLoaded = false;
845  return;
846  }
847 
848  uint8 loadQty = chargeRef->quantity();
849 
852  if (m_chargeRef.get() != nullptr)
853  loadQty += m_chargeRef->quantity();
854  // dont change chargeRef if we're just reloading. new chargeRef is deleted on merge
855  } else {
857  m_chargeRef = chargeRef;
858  }
859 
860  if (!pClient->IsLogin()) {
861  // process new charge's effects (load timer will determine if fx are applied based on existing charge)
862  // GM::Online proc fx when client logs in...this is to avoid dupe calls
863  for (auto it : chargeRef->type().m_stateFxMap) {
864  fxData data = fxData();
866  data.srcRef = chargeRef;
867  sFxProc.ParseExpression(m_modRef.get(), sFxDataMgr.GetExpression(it.second.preExpression), data, this);
868  }
869  if (pClient->IsInSpace()) {
870  /* **** this sets "reload blink" status on weapon button
871  * def OnChargeBeingLoadedToModule(self, moduleIDs, chargeTypeID, reloadTime):
872  * {returns}
873  * [PyTuple 3 items]
874  * [PyTuple 1 items]
875  * [PyIntegerVar 1005885547063] << moduleID (can be multiple, but not coded for multiples yet)
876  * [PyInt 203] << chargeTypeID
877  * [PyFloat 10000] << reloadTime (ms)
878  */
879  PyTuple* module = new PyTuple(1);
880  module->SetItem(0, new PyInt(m_modRef->itemID()));
881  PyTuple* tmp = new PyTuple(3);
882  tmp->SetItem(0, module);
883  tmp->SetItem(1, new PyInt(chargeRef->typeID()));
884  tmp->SetItem(2, new PyInt(m_reloadTime));
885  pClient->SendNotification("OnChargeBeingLoadedToModule", "shipid", &tmp);
887  }
888  }
889 
890  if (!m_reloadTimer.Enabled()) {
891  // apply charge effects only for empty modules (no previous charge fx)
892  if (!m_chargeLoaded)
893  sFxProc.ApplyEffects(m_chargeRef.get(), m_shipRef->GetPilot()->GetChar().get(), m_shipRef.get(), true);
894 
895  // set immediately on login or when docked
896  m_chargeLoaded = true;
898  }
899 }
900 
901 //{'FullPath': u'UI/Messages', 'messageID': 259200, 'label': u'NoChargesBody'}(u'{launcher} has run out of charges', None, {u'{launcher}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'launcher'}})
902 //{'FullPath': u'UI/Messages', 'messageID': 259232, 'label': u'NotEnoughChargesBody'}(u'{launcher} has {[numeric]got} charges, but needs {[numeric]need} to fire.', None, {u'{[numeric]got}': {'conditionalValues': [], 'variableType': 9, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'got'}, u'{launcher}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'launcher'}, u'{[numeric]need}': {'conditionalValues': [], 'variableType': 9, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'need'}})
903 //{'FullPath': u'UI/Messages', 'messageID': 258889, 'label': u'TooManyChargesForLauncherBody'}(u'The launcher is currently holding {[numeric]excess} too many excess units.', None, {u'{[numeric]excess}': {'conditionalValues': [], 'variableType': 9, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'excess'}})
904 
905 //{'FullPath': u'UI/Messages', 'messageID': 259152, 'label': u'NoSpaceForReplacedItemBody'}(u'There is not enough space in the cargo hold for the charges currently in the {item} to be moved into.', None, {u'{item}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'item'}})
906 
908 {
909  _log(MODULE__TRACE, "%s(%u) calling AM::UnloadCharge()", m_modRef->name(), m_modRef->itemID());
910 
911  // make sure module isnt currently active
912  Deactivate();
913 
914  if (m_chargeRef.get() != nullptr) {
919  }
920 
922  for (auto it : m_chargeRef->type().m_stateFxMap) {
923  fxData data = fxData();
925  data.srcRef = m_chargeRef;
926  sFxProc.ParseExpression(m_modRef.get(), sFxDataMgr.GetExpression(it.second.postExpression), data, this);
927  }
928 
929  // apply to containing module to properly remove effects
931  }
932 
933  m_chargeRef = InventoryItemRef(nullptr); // Ensure ref is NULL
935  m_chargeLoaded = false;
936 }
937 
939  if (m_linkMaster) {
940  // remove charges from linked modules as applicable
941  std::vector<GenericModule*> modules;
943  for (auto cur : modules)
944  if (cur->isOnline() and cur->IsLoaded())
945  cur->GetLoadedChargeRef()->AlterQuantity(-1, cur->IsLoaded());
946  } else {
947  m_chargeRef->AlterQuantity(-1, false); // only used in space. dont send ixStacksize update
948  }
949 }
950 
951 void ActiveModule::ApplyEffect(int8 state, bool active/*false*/)
952 {
953  // process and apply module's active effects
954  ProcessEffects(state, active);
955  sFxProc.ApplyEffects(m_modRef.get(), m_shipRef->GetPilot()->GetChar().get(), m_shipRef.get(), true);
956 }
957 
958 void ActiveModule::UpdateCharge(uint16 attrID, uint16 testAttrID, uint16 srcAttrID, InventoryItemRef iRef)
959 {
960  // Apply boost amount:
961  EvilNumber newValue = iRef->GetAttribute(attrID);
962  newValue += GetAttribute(srcAttrID);
963  if (newValue > iRef->GetAttribute(testAttrID)) {
964  newValue = iRef->GetAttribute(testAttrID);
965  if (m_shipRef->GetPilot()->AutoStop())
966  Deactivate();
967  }
968  iRef->SetAttribute(attrID, newValue);
969 
970  if (attrID == AttrShieldCharge)
972 }
973 
975 {
976  EvilNumber newValue = iRef->GetAttribute(attrID);
977  newValue -= GetAttribute(srcAttrID);
978  if (newValue < EvilZero) {
979  newValue = EvilZero;
980  if (m_shipRef->GetPilot()->AutoStop())
981  Deactivate();
982  }
983  iRef->SetAttribute(attrID, newValue);
985 }
986 
987 // not used
989 {
990  if (m_chargeRef.get() == nullptr)
991  return;
992  /* may not need to reset this...
993  m_chargeRef->ClearModifiers();
994  for (auto it : m_chargeRef->type().m_stateFxMap) {
995  fxData data = fxData();
996  data.action = FX::Action::dgmActInvalid;
997  data.srcRef = m_chargeRef;
998  sFxProc.ParseExpression(m_chargeRef.get(), sFxDataMgr.GetExpression(it.second.preExpression), data, this);
999  } */
1002 }
1003 
1005 {
1006  // there is still more to be done here. wip
1007  // modules that require specific tests are coded in their module class, which will call this if their specific checks pass
1008 
1009  // Check for (13) modules which consume items.
1011  uint16 typeID(m_modRef->GetAttribute(AttrConsumptionType).get_uint32()); // cast uint32 to uint16
1013  // verify character has require amount of consumption type available
1015  m_shipRef->GetPilot()->SendNotifyMsg("This module requires you to have %u units of %s in your inventory.", \
1016  qtyNeed, sItemFactory.GetType(typeID)->name().c_str());
1017  return false;
1018  }
1019  }
1020 
1021  // check distance for targetable actions
1022  if (m_targetSE != nullptr) {
1023  // weapons use ships AttrMaxTargetRange which is checked in TargetMgr
1024  if (m_turret or m_launcher)
1025  return true;
1026 
1027  // test for specific targets and distance here
1028  float range(0.0f);
1029  using namespace EVEDB::invGroups;
1030  switch (groupID()) {
1031  case Tractor_Beam: {
1033  bool allowed(false);
1034  if ( m_targetSE->IsWreckSE()) {
1035  allowed = true;
1036  } else if (m_targetSE->IsContainerSE()) {
1037  if (m_targetSE->GetContSE()->IsAnchored()) {
1038  m_shipRef->GetPilot()->SendNotifyMsg("The %s is anchored. It cannot be tractored.", m_targetSE->GetName());
1039  return false;
1040  }
1041  allowed = true;
1042  }
1043 
1044  if (allowed) {
1045  range = GetAttribute(AttrMaxRange).get_float();
1046  } else {
1047  m_shipRef->GetPilot()->SendNotifyMsg("You cannot tractor the %s.", m_targetSE->GetName());
1048  return false;
1049  }
1050 
1051  // test for ownership here...wip
1052  // once crim shit is implemented, allow for tractoring no matter owner.
1053  bool owner(false), fleet(false), corp(false), ally(false), war(false);
1054  if (m_targetSE->GetOwnerID() == m_shipRef->ownerID())
1055  owner = true;
1057  corp = true;
1059  ally = true;
1061  war = true;
1062  if (m_shipRef->GetPilot()->InFleet())
1064  fleet = true;
1065 
1066  if (owner or fleet or corp or ally or war) {
1068  } else {
1069  int id = m_targetSE->GetID ();
1070 
1071  Clear ();
1072 
1073  throw UserError ("InvalidTargetCanOwner")
1074  .AddFormatValue ("module", new PyInt (id));
1075  }
1076  } break;
1077  case Shield_Transporter: {
1079  } break;
1080  case Energy_Vampire:
1081  case Energy_Transfer_Array: {
1083  } break;
1084  case Energy_Destabilizer: {
1086  } break;
1087  case Warp_Scrambler: {
1089  } break;
1090  case Cargo_Scanner: {
1092  } break;
1093  case Ship_Scanner: {
1095  } break;
1096  case Shield_Disruptor: // no modules in this group
1097  case Stasis_Web:
1098  case Salvager:
1099  case Strip_Miner:
1100  case Mining_Laser:
1101  case Target_Painter:
1102  case Tracking_Disruptor:
1103  case Gas_Cloud_Harvester:
1104  case Remote_Sensor_Damper:
1105  case Remote_Sensor_Booster:
1107  case Frequency_Mining_Laser: {
1108  range = GetAttribute(AttrMaxRange).get_float();
1109  } break;
1110 
1111  // these scanners *may* not hit here, as they dont need a target, and we really dont want them to test for target
1112  // the pilot *may* have a target locked, in which case we really dont want these to hit
1113  /*
1114  case System_Scanner: {
1115  range = GetAttribute(AttrScanRange).get_float(); // this is in AU
1116  range *= oneauinmeters;
1117  } break;
1118  case Survey_Scanner: {
1119  range = GetAttribute(AttrSurveyScanRange).get_float();
1120  } break;
1121  'OnShipScanCompleted',
1122  'OnJamStart',
1123  'OnJamEnd',
1124  'OnCargoScanComplete',
1125  */
1126  default: {
1127  // make error here with group
1128  range = GetAttribute(AttrMaxRange).get_float();
1129  _log(SHIP__WARNING, "Activate::RangeTest - Default hit for %s(%u) group: %u. Using %.1f", \
1130  m_modRef->name(), m_modRef->itemID(), m_modRef->groupID(), range);
1131  } break;
1132  }
1133 
1134  float distance = m_shipRef->position().distance(m_targetSE->GetPosition());
1135  distance -= m_targetSE->GetRadius();
1136 
1137  _log(MODULE__MESSAGE, "Activate::RangeTest - distance between %s and target %s: %.1f. range of %s is %.1f", \
1138  m_shipRef->name(), m_targetSE->GetName(), distance, m_modRef->name(), range);
1139 
1140  if (distance > range) {
1141  m_shipRef->GetPilot()->SendNotifyMsg("The %s is %.0f meters from you, outside the effective range of your %s, which is %.0f meters.", \
1142  m_targetSE->GetName(), distance, m_modRef->name(), range);
1143  return false;
1144  }
1145  }
1146 
1147  //AttrDeadspaceUnsafe
1148  //AttrMaxGroupActive
1149  return true;
1150 }
1151 
1152 
1153 void ActiveModule::ShowEffect(bool active/*false*/, bool abort/*false*/)
1154 {
1155  if (m_effectID < 1)
1156  _log(EFFECTS__ERROR, "fxID = 0 for %s.", m_modRef->name());
1157 
1158  int64 abortTime(GetFileTimeNow());
1159  if (abort) {
1160  active = false;
1163  abortTime += (5 * EvE::Time::Second); // delay abort for 5s to simulate module "completing" its' cycle and dumping ore to cargo
1164  } else {
1165  abortTime += (3 * EvE::Time::Second); // delay abort for 3s to simulate module "completing" its' cycle
1166  }
1167  }
1168 
1169  uint16 effectID(m_effectID);
1170  // there may be others here like this...this is ONLY for OnSpecialFX data
1171  if ((m_effectID == EVEEffectID::useMissiles) and (m_chargeRef.get() != nullptr)) //operation defined by charge (use charge's default effectID)
1172  effectID = m_chargeRef->type().GetDefaultEffect();
1173  std::string guidStr = sFxDataMgr.GetEffectGuid(effectID);
1174  if (guidStr.empty())
1175  _log(EFFECTS__ERROR, "guid empty for %s using effectID %u", m_modRef->name(), effectID);
1176 
1177  uint16 chgTypeID(((m_chargeRef.get() != nullptr) ? m_chargeRef->typeID() : 0));
1178  uint32 timeLeft(GetRemainingCycleTimeMS());
1179 
1180  if (m_destinyMgr != nullptr)
1182  m_shipRef->itemID(),
1183  m_modRef->itemID(),
1184  m_modRef->typeID(),
1186  chgTypeID,
1187  guidStr,
1188  sFxDataMgr.isOffensive(m_effectID),
1189  active, // start - if (start = 0) THEN remove effect
1190  active, // active - if (start and active) THEN starting ONE-SHOT event of (duration) (dunno what 'ONE-SHOT event' is)
1191  timeLeft, // duration in ms
1192  m_repeat); // repeat - if (repeat > 0) THEN starting REPEAT event ELSE (repeat == 0) THEN starting TOGGLE event
1193 
1194 
1195  // Create Destiny Updates and GFx
1196  GodmaEnvironment ge;
1197  ge.selfID = m_modRef->itemID(); //ENV_IDX_SELF = 0
1198  ge.charID = m_shipRef->ownerID(); //ENV_IDX_CHAR = 1
1199  ge.shipID = m_shipRef->itemID(); //ENV_IDX_SHIP = 2
1200  ge.target = IsValidTarget(m_targetID) ? new PyInt(m_targetID) : PyStatic.NewNone(); //ENV_IDX_TARGET = 3
1201  ge.area = new PyList(); //ENV_IDX_AREA = 5 still dont know what this is.
1202  ge.effectID = m_effectID; //ENV_IDX_EFFECT = 6
1203 
1204  if (chgTypeID > 0) {
1205  GodmaSubLoc gsl; // subLocation is for charges loaded into modules on ship
1206  gsl.shipID = ge.shipID;
1207  gsl.slotID = m_modRef->flag();
1208  gsl.typeID = chgTypeID;
1209  ge.subLoc = gsl.Encode(); //ENV_IDX_OTHER = 4
1210  } else {
1211  ge.subLoc = PyStatic.NewNone(); //ENV_IDX_OTHER = 4
1212  }
1213 
1214  timeLeft /= 1000;
1215  //def OnGodmaShipEffect(self, itemID, effectID, t, start, active, environment, startTime, duration, repeat, randomSeed, error, actualStopTime = None, stall = True):
1216  Notify_OnGodmaShipEffect shipEff;
1217  shipEff.itemID = ge.selfID;
1218  shipEff.effectID = ge.effectID;
1219  shipEff.timeNow = GetFileTimeNow();
1220  shipEff.start = (active ? 1 : 0);
1221  shipEff.active = (active ? 1 : 0);
1222  shipEff.environment = ge.Encode();
1223  shipEff.startTime = (abort ? (abortTime / EvE::Time::Second) : shipEff.timeNow - (timeLeft * EvE::Time::Second));
1224  shipEff.duration = (abort ? 2000 : timeLeft); // duration in seconds
1225  shipEff.repeat = m_repeat;
1226  // will need to check and update for data miners here (any other cases?)
1227  if ((groupID() == EVEDB::invGroups::Salvager) and IsSuccess()) {
1228  // Create Destiny Updates:
1229  PyTuple* type = new PyTuple(2);
1230  type->SetItem(0, new PyInt(4));
1231  type->SetItem(1, new PyInt(m_targetSE->GetTypeID()));
1232  PyDict* dict = new PyDict;
1233  dict->SetItemString("type", type);
1234  PyTuple* tuple = new PyTuple(2);
1235  tuple->SetItem(0, new PyString("SalvagingSuccess"));
1236  tuple->SetItem(1, dict);
1237  shipEff.error = tuple;
1238  } else if (m_needsTarget and (m_targetSE == nullptr)) {
1239  /* these both give client warning - [no messageID: 258855]
1240  if (IsValidTarget(m_targetID)) {
1241  PyDict* dict = new PyDict();
1242  dict->SetItemString("moduleID", new PyInt(m_modRef->itemID()));
1243  dict->SetItemString("targetID", new PyInt(m_targetID));
1244  PyTuple* tuple = new PyTuple(2);
1245  tuple->SetItem(0, new PyString("TargetNoLongerPresent"));
1246  tuple->SetItem(1, dict);
1247  shipEff.error = tuple;
1248  } else {
1249  PyDict* dict = new PyDict();
1250  dict->SetItemString("moduleID", new PyInt(m_modRef->itemID()));
1251  PyTuple* tuple = new PyTuple(2);
1252  tuple->SetItem(0, new PyString("TargetNoLongerPresentGeneric"));
1253  tuple->SetItem(1, dict);
1254  shipEff.error = tuple;
1255  }
1256  // this one doesnt work, either.
1257  PyDict* dict = new PyDict();
1258  dict->SetItemString("moduleID", new PyInt(m_modRef->itemID()));
1259  PyTuple* tuple = new PyTuple(2);
1260  tuple->SetItem(0, new PyString("TargetNoLongerPresentGeneric"));
1261  tuple->SetItem(1, dict);
1262  shipEff.error = PyStatic.NewNone();
1263  m_shipRef->GetPilot()->SendNotification("TargetNoLongerPresentGeneric", "charid", &tuple);
1264  */
1265  m_targetID = 0;
1266  // this is wrong....need to find error msg and insert here, but client throws error on above msgs
1267  shipEff.error = PyStatic.NewNone();
1268  /*
1269  * {'messageKey': 'TargetNoLongerPresent', 'dataID': 17881666, 'suppressable': False, 'bodyID': 258855, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 1626}
1270  * u'TargetNoLongerPresentBody'}(u'{[item]moduleID.name} deactivates as the {[item]targetID.name} it was targeted at is no longer present.', None, {u'{[item]moduleID.name}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'name', 'args': 0, 'kwargs': {}, 'variableName': 'moduleID'}, u'{[item]targetID.name}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'name', 'args': 0, 'kwargs': {}, 'variableName': 'targetID'}})
1271  * {'messageKey': 'TargetNoLongerPresentGeneric', 'dataID': 17875297, 'suppressable': False, 'bodyID': 256459, 'messageType': 'notify', 'urlAudio': '', 'urlIcon': '', 'titleID': None, 'messageID': 3742}
1272  * u'TargetNoLongerPresentGenericBody'}(u'{[item]moduleID.name} deactivates as the item it was targeted at is no longer present.', None, {u'{[item]moduleID.name}': {'conditionalValues': [], 'variableType': 2, 'propertyName': 'name', 'args': 0, 'kwargs': {}, 'variableName': 'moduleID'}})
1273  *
1274  */
1275  } else {
1276  shipEff.error = PyStatic.NewNone();
1277  }
1278 
1279  PyTuple* tuple = shipEff.Encode();
1280  if (is_log_enabled(EFFECTS__DUMP))
1281  tuple->Dump(EFFECTS__DUMP, "");
1282  if ((m_destinyMgr == nullptr)
1283  or (m_bubble == nullptr)
1284  or m_destinyMgr->IsWarping()) {
1285  m_shipRef->GetPilot()->QueueDestinyEvent(&tuple);
1286  } else {
1287  m_bubble->BubblecastDestinyEvent(&tuple, "destiny");
1288  }
1289 }
1290 /*
1291  [PyTuple 12 items]
1292  [PyString "OnGodmaShipEffect"]
1293  [PyIntegerVar 1005902575207]
1294  [PyInt 27]
1295  [PyIntegerVar 129756563776224944]
1296  [PyInt 0]
1297  [PyInt 0]
1298  [PyList 7 items]
1299  [PyIntegerVar 1005902575207]
1300  [PyIntegerVar 649670823]
1301  [PyIntegerVar 1005885567714]
1302  [PyNone]
1303  [PyNone]
1304  [PyList 0 items]
1305  [PyInt 27]
1306  [PyIntegerVar 129756563680133184]
1307  [PyFloat 9600]
1308  [PyBool True]
1309  [PyNone]
1310  [PyTuple 2 items]
1311  [PyString "NotEnoughEnergy"]
1312  [PyDict 3 kvp]
1313  [PyString "need"]
1314  [PyFloat 160]
1315  [PyString "got"]
1316  [PyFloat 99.9814376484626]
1317  [PyString "effectname"]
1318  [PyTuple 2 items]
1319  [PyInt 4]
1320  [PyInt 3530]
1321  */
1322 
1324 {
1325  // must not throw here...
1326  //throw UserError ("TargetingMissileToSelf");
1327  //throw UserError ("NoCharges");
1328 
1329  //{'FullPath': u'UI/Messages', 'messageID': 259200, 'label': u'NoChargesBody'}(u'{launcher} has run out of charges', None, {u'{launcher}': {'conditionalValues': [], 'variableType': 10, 'propertyName': None, 'args': 0, 'kwargs': {}, 'variableName': 'launcher'}})
1330 
1331  // AttrAimedLaunch ? what is this used for?
1332 
1333  // Launch a missile, creating a new Destiny object for it
1334  Client* pClient = m_shipRef->GetPilot();
1335  if (pClient == nullptr)
1336  return;
1338  InventoryItemRef missileRef = sItemFactory.SpawnItem(idata);
1339  if (missileRef.get() == nullptr) {
1340  _log(ITEM__ERROR ,"Unable to spawn item #%u:'%s' of type %u.", m_chargeRef->itemID(), m_chargeRef->name(), m_chargeRef->typeID());
1341  pClient->SendErrorMsg("Your %s in %s experienced a loading error and was disabled.", m_chargeRef->name(), m_modRef->name());
1342  AbortCycle();
1343  return;
1344  }
1345 
1346  SystemManager* pSystem = pClient->SystemMgr();
1347  Missile* pMissile = new Missile(missileRef, *(pSystem->GetServiceMgr()), pSystem, m_modRef, m_targetSE, m_shipRef->GetPilot()->GetShipSE(), this);
1348  if (pMissile == nullptr) {
1349  _log(ITEM__ERROR ,"Unable to create SE #%u:'%s' of type %u.", m_chargeRef->itemID(), m_chargeRef->name(), m_chargeRef->typeID());
1350  pClient->SendErrorMsg("Your %s in %s experienced a launching error and was disabled.", m_chargeRef->name(), m_modRef->name());
1351  AbortCycle();
1352  return;
1353  }
1354 
1355  float distance = pMissile->GetSelf()->position().distance(m_targetSE->GetPosition());
1356  float missileSpeed = pMissile->GetSelf()->GetAttribute(AttrMaxVelocity).get_float();
1357  float travelTime = (distance/missileSpeed);
1358  if (travelTime < 1)
1359  travelTime = 1;
1360  pMissile->SetSpeed(missileSpeed);
1361  pMissile->SetHitTimer(travelTime *1000);
1362  pMissile->DestinyMgr()->MakeMissile(pMissile);
1363 
1364  // Reduce ammo charge by 1 unit:
1365  ConsumeCharge();
1366 
1367  // tell target a missile has been launched at them.. (defender missile trigger for ship, tower, pos, npc, others?)
1368  m_targetSE->MissileLaunched(pMissile);
1369 
1370  // add data to StatisticMgr
1371  sStatMgr.Increment(Stat::pcMissiles);
1372 }
1373 
1375 {
1376  Client* pClient = m_shipRef->GetPilot();
1377  if (pClient == nullptr)
1378  return;
1379  if (pClient->scan() == nullptr)
1380  pClient->SetScan(new Scan(pClient));
1381 
1382  uint8 pcount = pClient->scan()->GetProbeCount();
1383  if (pcount > (pClient->GetChar()->GetSkillLevel(EvESkill::Astrometrics) + 3)) {
1384  pClient->SendErrorMsg("You can only control %u probes based on your current skills.", pcount);
1385  return;
1386  }
1387 
1388  GPoint pos(m_shipRef->position());
1390 
1391  //ItemData( uint32 _typeID, uint32 _ownerID, uint32 _locationID, EVEItemFlags _flag, uint32 _quantity);
1392  // we are not changing singleton status of probes
1393  ItemData idata(m_chargeRef->typeID(), pClient->GetCharacterID(), pClient->GetLocationID(), flagNone, 1);
1394  ProbeItemRef probeRef = sItemFactory.SpawnProbe(idata);
1395  if (probeRef.get() == nullptr)
1396  throw CustomError ("Unable to spawn item #%u:'%s' of type %u.", \
1398 
1399  probeRef->SetPosition(pos);
1400  SystemManager* pSystem = pClient->SystemMgr();
1401  ProbeSE* pProbe = new ProbeSE(probeRef, *(pSystem->GetServiceMgr()), pSystem, m_modRef, m_shipRef);
1402  if (pProbe == nullptr)
1403  return; // make error here
1404 
1405  pProbe->SendNewProbe();
1406  pSystem->AddEntity(pProbe, false);
1407  pClient->scan()->AddProbe(pProbe);
1408 
1409  // Reduce ammo charge by 1 unit:
1410  ConsumeCharge();
1411 
1412  // add data to StatisticMgr
1413  sStatMgr.Increment(Stat::probesLaunched);
1414 }
1415 
1417 {
1418 
1419 }
bool IsLogin()
Definition: Client.h:235
void ResetAttribute(uint32 attrID)
Definition: GenericModule.h:52
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
SystemEntity * GetSE(uint32 entityID) const
double radius() const
uint32 GetLocationID() const
Definition: Client.h:151
bool IsBelt()
Definition: SystemBubble.h:64
void AddEntity(SystemEntity *pSE, bool addSignal=true)
virtual bool IsSuccess()
void HeatDamageCheck(GenericModule *pMod)
Definition: Ship.cpp:1272
void AddProbe(ProbeSE *pProbe)
Definition: Scan.cpp:45
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
#define _log(type, fmt,...)
Definition: logsys.h:124
bool IsIce()
Definition: SystemBubble.h:63
void Disable()
Definition: timer.h:39
Python string.
Definition: PyRep.h:430
double GetRadius()
Definition: SystemEntity.h:208
virtual Prospector * GetProspectModule()
Definition: GenericModule.h:63
void ProcessEffects(int8 state, bool active=false)
void ConsumeCharge()
Python's dictionary.
Definition: PyRep.h:719
uint16 m_effectID
Definition: ActiveModule.h:100
bool HasAttribute(const uint16 attrID) const
bool m_needsCharge
Definition: ActiveModule.h:106
ActiveModule(ModuleItemRef mRef, ShipItemRef sRef)
uint32 groupID()
Definition: GenericModule.h:99
uint32 ownerID() const
Definition: InventoryItem.h:99
const GPoint & position() const
void MakeMissile(Missile *missile)
SystemBubble * SysBubble()
Definition: SystemEntity.h:195
virtual void DeOverload()
virtual void RemoveTarget(SystemEntity *pSE)
double MakeRandomFloat(double low, double high)
Generates random real from interval [low; high].
Definition: misc.cpp:114
uint32 GetOwnerID()
Definition: SystemEntity.h:219
void QueueDestinyEvent(PyTuple **multiEvent)
Definition: Client.cpp:2124
void SetHitTimer(uint32 setTime)
Definition: Missile.h:60
virtual bool HasPilot()
Definition: Ship.h:71
int32 GetCharacterID() const
Definition: Client.h:113
void GetList(uint32 beltID, std::vector< AsteroidSE * > &list)
Definition: BeltMgr.cpp:221
int32 GetWarFactionID() const
Definition: Client.h:126
int32 GetCorporationID() const
Definition: Client.h:123
uint16 GetDefaultEffect() const
Definition: ItemType.h:95
SystemManager * m_sysMgr
Definition: ActiveModule.h:80
uint32 GetRemainingCycleTimeMS()
Definition: ActiveModule.h:94
UserError & AddFormatValue(const char *name, PyRep *value)
Fluent version of the protected AddKeyword, allows for adding a keyword to the exception.
this is a class that kinda mimics how python polymorph's numbers.
Definition: EvilNumber.h:59
const char * name()
bool IsInSpace()
Definition: Client.h:228
InventoryItemRef srcRef
Definition: EffectsData.h:78
void SendNotifyMsg(const char *fmt,...)
Definition: Client.cpp:2776
TargetManager * TargetMgr()
Definition: SystemEntity.h:197
CharacterRef GetChar() const
Definition: Client.h:164
Timer m_reloadTimer
Definition: ActiveModule.h:111
Python tuple.
Definition: PyRep.h:567
void SetAttribute(uint32 attrID, EvilNumber val, bool update=true)
Definition: GenericModule.h:51
Definition: Ship.h:301
Advanced version of UserError that allows to send a full custom message.
Definition: PyExceptions.h:453
void Dump(FILE *into, const char *pfx) const
Dumps object to file.
Definition: PyRep.cpp:84
std::unordered_multimap< int8, Effect > m_stateFxMap
Definition: ItemType.h:154
signed __int8 int8
Definition: eve-compat.h:45
Scan * scan()
Definition: Client.h:302
int32 GetAllianceID() const
Definition: Client.h:125
const GPoint & GetPosition() const
Definition: SystemEntity.h:211
ModuleItemRef m_modRef
void UpdateDamage(uint16 attrID, uint16 srcAttrID, InventoryItemRef iRef)
EvilNumber EvilZero
Definition: EvilNumber.cpp:32
void AddItem(PyRep *i)
Definition: PyRep.h:701
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
virtual void Delete()
uint16 groupID() const
const ItemType & type() const
void SendNewProbe()
Definition: Probes.cpp:348
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
ShipItemRef m_shipRef
void SendSpecialEffect(uint32 entityID, uint32 moduleID, uint32 moduleTypeID, uint32 targetID, uint32 chargeTypeID, std::string guid, bool isOffensive, bool start, bool isActive, int32 duration, uint32 repeat, int32 graphicInfo=0) const
virtual bool IsProspectModule() const
Definition: GenericModule.h:74
TargetManager * m_targMgr
Definition: ActiveModule.h:81
Definition: gpoint.h:33
void LaunchMissile()
virtual bool IsCOSE()
Definition: SystemEntity.h:164
SystemEntity * m_targetSE
Definition: ActiveModule.h:78
DestinyManager * DestinyMgr()
Definition: SystemEntity.h:198
SystemManager * SystemMgr()
Definition: SystemEntity.h:196
bool IsAnchored()
Definition: Container.h:164
void RemoveTarget(SystemEntity *tSE)
uint16 m_reloadTime
Definition: ActiveModule.h:99
bool InFleet()
Definition: Client.h:142
InventoryItemRef GetSelf()
Definition: SystemEntity.h:202
void BubblecastDestinyEvent(std::vector< PyTuple * > &events, const char *desc) const
bool Enabled() const
Definition: timer.h:41
int64 get_int()
Definition: EvilNumber.cpp:166
DestinyManager * m_destinyMgr
Definition: ActiveModule.h:79
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
virtual void UnloadCharge()
SystemManager * SystemMgr() const
Definition: Client.h:92
InventoryItemRef m_chargeRef
virtual bool IsItemEntity()
Definition: SystemEntity.h:153
virtual bool IsContainerSE()
Definition: SystemEntity.h:157
uint16 GetID()
Definition: SystemBubble.h:91
Definition: Probes.h:71
uint32 get_uint32()
Definition: EvilNumber.cpp:173
void RemoveTargetModule(ActiveModule *pMod)
#define sFxDataMgr
#define IsValidTarget(itemID)
Definition: EVE_Defines.h:327
virtual void Overload()
Python integer.
Definition: PyRep.h:231
bool AlterQuantity(int32 qty, bool notify=false)
BeltMgr * GetBeltMgr()
void ShowEffect(bool active=false, bool abort=false)
virtual void Process()
int32 GetAllianceID()
Definition: SystemEntity.h:216
void SetAttribute(uint16 attrID, int num, bool notify=true)
uint32 GetID()
Definition: SystemEntity.h:207
virtual void ApplyDamage()
Definition: ActiveModule.h:55
bool Check(bool reset=true)
Definition: timer.cpp:62
#define PyStatic
Definition: PyRep.h:1209
uint32 GetFleetID() const
Definition: Client.h:146
X * get() const
Definition: RefPtr.h:213
void LaunchSnowBall()
virtual void AbortCycle()
void SetSpeed(double speed)
Definition: Missile.h:61
const char * GetName() const
Definition: SystemEntity.h:210
const char * GetModuleStateName(int8 state)
void GetLinkedWeaponMods(EVEItemFlags flag, std::vector< GenericModule * > &modules)
Definition: Ship.cpp:1342
uint8 action
Definition: EffectsData.h:73
Python object "ccp_exceptions.UserError".
Definition: PyExceptions.h:121
Definition: Client.h:66
virtual void Overload()
void SetTimer(uint32 time)
virtual bool CanActivate()
unsigned __int32 uint32
Definition: eve-compat.h:50
EVEItemFlags flag() const
PyServiceMgr * GetServiceMgr()
Definition: SystemManager.h:88
virtual bool IsWreckSE()
Definition: SystemEntity.h:188
void ApplyEffect(int8 state, bool active=false)
#define sFxProc
RefPtr< InventoryItem > InventoryItemRef
Definition: ItemRef.h:52
virtual void Update()
double GetFileTimeNow()
Definition: utils_time.cpp:84
void TargetDestroyed()
Definition: Prospector.h:36
ShipSE * GetShipSE()
Definition: Client.h:168
void UpdateCharge(uint16 attrID, uint16 testAttrID, uint16 srcAttrID, InventoryItemRef iRef)
signed __int64 int64
Definition: eve-compat.h:51
uint32 GetCorporationID()
Definition: SystemEntity.h:218
void ProcessActiveCycle()
virtual Client * GetPilot()
Definition: Ship.h:72
void SendDamageStateChanged()
void SetScan(Scan *pScan)
Definition: Client.h:303
uint32 m_targetID
Definition: ActiveModule.h:101
EvilNumber GetAttribute(const uint16 attrID) const
void ClearTargetRef()
Definition: Ship.h:153
void TractorBeamStart(SystemEntity *pShipSE, EvilNumber speed)
signed __int16 int16
Definition: eve-compat.h:47
uint8 GetProbeCount()
Definition: Scan.h:41
int8 GetSkillLevel(uint16 skillTypeID, bool zeroForNotInjected=true) const
Definition: Character.cpp:575
void SetSlaveData(ShipSE *pShip)
virtual void LoadCharge(InventoryItemRef charge)
void SpeedBoost(bool deactivate=false)
void traceStack(void)
Definition: misc.cpp:169
virtual void DeOverload()
const bool IsDead()
Definition: SystemEntity.h:239
bool m_needsTarget
Definition: ActiveModule.h:107
SystemBubble * m_bubble
Definition: ActiveModule.h:77
virtual ContainerSE * GetContSE()
Definition: SystemEntity.h:107
#define sItemFactory
Definition: ItemFactory.h:165
virtual uint32 DoCycle()
float get_float()
Definition: EvilNumber.cpp:184
GaExpInl GaFloat distance(const GaVec3 &oth) const
Definition: GaTypes.h:158
void AddTargetModule(ActiveModule *pMod)
virtual void Deactivate(std::string effect="")
virtual void MissileLaunched(Missile *pMissile)
Definition: SystemEntity.h:249
void MakeRandomPointOnSphere(double radius)
Definition: gpoint.h:46
uint16 GetTypeID()
Definition: SystemEntity.h:203
virtual bool IsStaticEntity()
Definition: SystemEntity.h:146
bool ContainsTypeQtyByFlag(uint16 typeID, EVEItemFlags flag=flagNone, uint32 qty=0) const
Definition: Inventory.cpp:542
#define sBubbleMgr
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
unsigned __int16 uint16
Definition: eve-compat.h:48
entityID heal the character with the entityID note giving you detailed ship status information gives a list of all dynamic entities and players and their destinyState in this bubble shows some current destiny variables save all kick all and halt server immediate command list all items in current location s gives list of cargo contents and volumes in all holds list current session values show current ship DNA show current objects in their destiny state
EvilNumber GetAttribute(uint32 attrID)
Definition: GenericModule.h:53
virtual void DeactivateCycle(bool abort=false)
uint32 GetFleetID()
Definition: SystemEntity.h:220
InventoryItemRef GetByTypeFlag(uint32 typeID, EVEItemFlags flag) const
Definition: Inventory.cpp:444
virtual void ReprocessCharge()
bool AutoStop()
Definition: Client.h:274
uint16 typeID() const
void SetModuleState(int8 state)
Definition: Scan.h:32
Python list.
Definition: PyRep.h:639
virtual void Activate(uint16 effectID, uint32 targetID=0, int16 repeat=0)
void WebbedMe(InventoryItemRef modRef, bool apply=false)
uint32 typeID()
Definition: GenericModule.h:98
uint32 itemID() const
Definition: InventoryItem.h:98
void SetItemString(const char *key, PyRep *value)
SetItemString adds or sets a database entry.
Definition: PyRep.h:812
int32 GetWarFactionID()
Definition: SystemEntity.h:217
int32 quantity() const
Definition: InventoryItem.h:97
void Start(uint32 setTimerTime=0, bool changeResetTimer=true)
Definition: timer.cpp:81