EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
DestinyManager.cpp
Go to the documentation of this file.
1 /*
2  ------------------------------------------------------------------------------------
3  LICENSE:
4  ------------------------------------------------------------------------------------
5  This file is part of EVEmu: EVE Online Server Emulator
6  Copyright 2006 - 2021 The EVEmu Team
7  For the latest information visit https://evemu.dev
8  ------------------------------------------------------------------------------------
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License along with
19  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  Place - Suite 330, Boston, MA 02111-1307, USA, or go to
21  http://www.gnu.org/copyleft/lesser.txt.
22  ------------------------------------------------------------------------------------
23  Author: Zhur
24  Rewrite: Allan
25 */
26 
27 // this class is for objects that move
28 
29 #include "EVEServerConfig.h"
30 
31 #include "Client.h"
32 #include "EntityList.h"
33 #include "PyServiceMgr.h"
34 #include "StaticDataMgr.h"
35 #include "npc/NPC.h"
36 #include "npc/NPCAI.h"
37 #include "packets/Missile.h"
38 #include "ship/Missile.h"
39 #include "ship/Ship.h"
40 #include "station/Station.h"
41 #include "station/StationDataMgr.h"
42 #include "system/BubbleManager.h"
43 #include "system/Container.h"
44 #include "system/DestinyManager.h"
45 #include "system/SystemBubble.h"
46 #include "system/SystemManager.h"
47 
48 
50 : mySE(self),
51 m_maxSpeed(1.0f),
52 m_shipMaxAccelTime(0.0f),
53 m_ballMode(Destiny::Ball::Mode::STOP),
54 m_warpTimer(0),
55 m_moveTime(0.0),
56 m_targetDistance(0),
57 m_followDistance(0),
58 m_stopDistance(0),
59 m_mass(1.0f),
60 m_turnTic(1),
61 m_massMKg(1.0f),
62 m_alignTime(1.0f),
63 m_timeToEnterWarp(10.0f),
64 m_shipWarpSpeed(1.0f),
65 m_maxShipSpeed(100.0f),
66 m_shipAgility(1.0f),
67 m_shipInertia(1.0f),
68 m_warpAccelTime(1),
69 m_warpDecelTime(1),
70 m_warpState(nullptr),
71 m_targBubble(nullptr),
72 m_warpCapacitorNeed(0.00001f),
73 m_frozen(false)
74 {
75  m_bump = false;
76  m_stop = false;
77  m_accel = false;
78  m_decel = false;
79  m_cloaked = false;
80  m_turning = false;
81  m_inBubble = true;
82  m_orbiting = 0;
83  m_tractored = false;
84  m_changeDelay = false;
85  m_tractorPause = false;
86  m_hasSentShipUpdates = false;
87 
88  m_capNeeded = 0.0f;
89  m_prevSpeed = 0.0f;
90  m_degPerTic = 0.0f;
91  m_orbitTime = 0.0f;
92  m_orbitRadTic = 0.0f;
93  m_prevSpeedFraction = 0.0f;
94  m_userSpeedFraction = 0.0f;
95  m_activeSpeedFraction = 0.0f;
98 
99  m_targetEntity.first = 0;
100  m_targetEntity.second = nullptr;
105 
106  m_radius = mySE->GetRadius();
108 
109  m_turnTic = 0;
110  m_turning = false;
111  m_turnFraction = 0;
112 
113  m_inclination = 0;
114  m_longAscNode = 0;
115 
116  m_stateStamp = 0;
117 }
118 
122 }
123 
124 // this is called once per tic by SystemEntity::Process()
126  if (mySE->IsFrozen()) {
127  Halt();
128  return;
129  }
130 
131  double profileStartTime = GetTimeUSeconds();
132  //check for and process Destiny::Ball::State changes.
133  ProcessState();
134 
135  if (sConfig.debug.UseProfiling)
136  sProfiler.AddTime(Profile::destiny, GetTimeUSeconds() - profileStartTime);
137 }
138 
140  using namespace Destiny;
141  switch(m_ballMode) {
142  case Ball::Mode::STOP: {
143  if (IsMoving()) {
144  MoveObject();
145  return;
146  }
147  Stop();
148  } break;
149  case Ball::Mode::GOTO: {
150  MoveObject();
151  } break;
152  case Ball::Mode::MISSILE: {
153  // if target was removed, continue movement and wait for Missile::EndOfLife() call to do cleanup
154  //set current direction based on position and targetPoint. this will keep missile aligned properly
155  GVector moveVector(m_position, m_targetPoint);
156  moveVector.normalize();
157  //set position and direction for this round of movement
158  m_shipHeading = moveVector;
159  m_velocity = (moveVector * m_maxSpeed);
161  } break;
162  case Ball::Mode::ORBIT: {
163  if (IsTargetInvalid())
164  return;
165  Orbit();
166  } break;
167  case Ball::Mode::FOLLOW: {
168  if (IsTargetInvalid())
169  return;
170  Follow();
171  } break;
172  case Ball::Mode::WARP: {
173  /*
174  * There are three stages of warp, which are functions of time, speed and distance:
175  *
176  * 1) Acceleration.
177  * this is a fixed attribute, which is roughly 9s to full warp speed for all ships
178  * 2) Cruising.
179  * traveling at maximum warp speed
180  * 3) Deceleration.
181  * this also is a fixed attribute, which is roughly 22s from full warp speed for all ships
182  *
183  * Acceleration and Deceleration are logarithmic with finite caps (instead of infinity) at the ends.
184  * see also: my notes in InitWarp()
185  */
186  if (m_warpState != nullptr) {
187  //warp is in progress
188  uint16 sec_into_warp = (sEntityList.GetStamp() - m_stateStamp);
189  // speed and distance formulas based on current warp distance
190  if (m_warpState->accel) {
191  WarpAccel(sec_into_warp);
192  } else if (m_warpState->cruise) {
193  WarpCruise(sec_into_warp);
194  } else if (m_warpState->decel) {
195  WarpDecel(sec_into_warp);
196  } else {// houston, we have a problem...
197  if (mySE->HasPilot()) {
198  _log(DESTINY__ERROR, "Destiny::ProcessState() Error! Ship %s(%u) for Player %s(%u) Has WarpState but checks are false.", \
200  mySE->GetPilot()->SendErrorMsg("Internal Server Error. Ref: ServerError 35928. Please Dock or Relog to reset your ship.");
201  } else {
202  _log(DESTINY__ERROR, "Destiny::ProcessState() Error! NPC %s(%u) Has WarpState but checks are false.", \
203  mySE->GetName(), mySE->GetID());
204  }
205  }
206  return;
207  }
208 
209  // Updated warp alignment and speed check. -allan 17nov15
211  toVec.normalize();
212  float dot = toVec.dotProduct(m_shipHeading);
213  float degrees = EvE::Trig::Rad2Deg(std::acos(dot));
214 
215  if ((degrees < WARP_ALIGNMENT) and (m_currentSpeedFraction > 0.749)) {
216  m_shipHeading = toVec;
217  InitWarp();
218  return;
219  } else if (m_currentSpeedFraction < 0.749) {
220  if (m_userSpeedFraction < 0.7499)
221  SetSpeedFraction(1.0f, true);
222  } else if ((sEntityList.GetStamp() - m_stateStamp) > m_timeToEnterWarp + 0.3) {
223  // catchall for turn checks messed up, and m_moveTime > ship align time
224  if (mySE->HasPilot()) {
225  _log(DESTINY__ERROR, "Destiny::ProcessState() Error! Ship %s(%u) for Player %s(%u) - warp align/speed is incorrect, but time > shipTimeToWarp.", \
227  } else {
228  _log(DESTINY__ERROR, "Destiny::ProcessState() Error! NPC %s(%u) - warp align/speed is incorrect, but time > shipTimeToWarp.", \
229  mySE->GetName(), mySE->GetID());
230  }
231  m_shipHeading = toVec;
232  InitWarp();
233  return;
234  }
235 
236  MoveObject();
237  } break;
238  case Ball::Mode::MUSHROOM: // aoe?
239  case Ball::Mode::BOID: // this will turn RIGID after a set time
240  case Ball::Mode::TROLL: // seen for wrecks
241  case Ball::Mode::MINIBALL: // used for sentrys
242  case Ball::Mode::FIELD: // dunno
243  case Ball::Mode::FORMATION: // dunno
244  case Ball::Mode::RIGID: // item that never moves
245  //no default on purpose
246  break;
247  }
248 }
249 /* acceleration forumula
250  * V(t) = Vmax*(1-e^-(t/a))
251  * V(t) = velocity at time t
252  * a = agility
253  */
254  //Velocity setting methods
255 void DestinyManager::SetSpeedFraction(float fraction/*1.0*/, bool startMovement/*false*/) {
256  // this sets current speed fraction for object.
257 
258  // if orbiting, call Orbit() and let code reset the variables
259  if (m_orbiting != 0)
261 
262  if ((fraction == m_userSpeedFraction) and (!startMovement))
263  return;
264  if (is_log_enabled(DESTINY__MOVE_TRACE))
265  _log(DESTINY__MOVE_TRACE, "Destiny::SetSpeedFraction() - %s(%u): fraction: %.2f, start: %i, stop: %i, prevSpeed:%.2f, accel: %s, decel: %s",
266  mySE->GetName(), mySE->GetID(), fraction, startMovement, m_stop, m_prevSpeed, m_accel ? "true" : "false", m_decel ? "true": "false");
267 
268  // this is to start movement when setting fractional speeds from speedo in client.
269  // also a hack to circumvent above check when called again by goto, warp, align, follow for changing direction.
270  if (startMovement) {
273  m_stop = false;
274  }
275 
276  // prevent multiple client calls to Stop() from resetting ship speed.
277  if (m_stop)
278  return;
279 
280  /* movement is set according to time vs speed fraction. -allan 8Oct14 -major update 20Nov15 -added prop mod code 29Mar17
281  * all *SpeedFraction variables use fuzzy logic
282  * m_userSpeedFraction (USF) is user-set speed control (fractional from speedo or full from goto, warp, align, follow, and stop).
283  * -> sets m_maxSpeed
284  * m_prevSpeedFraction (PSF) is previously-user-set speed fraction used for decel checks, as USF = 0 OR deactivated prop mod.
285  * this allows for proper decel speeds when USF was previously < 1.
286  * -> caps decel when USF = 0, or sets 'decel to' when m_prevSpeed > 0
287  * m_activeSpeedFraction (ASF) holds ship's current speed setting. set in MoveObject()
288  * this is actually a ratio of CSF to USF, capped by USF and/or OSF
289  * -> sets accel/decel when changing speeds while moving
290  * m_currentSpeedFraction (CSF) holds current euler value for time. set in MoveObject()
291  * this is reset on initial turn based on turn angle, then follows normal progression.
292  * this is the only SpeedFraction value that runs the full range of 0 - 1 no matter value of other SpeedFractions.
293  * -> sets m_velocity
294  * m_maxOrbitSpeedFraction (OSF) is ship's max speed based on orbit data
295  * -> modifies m_velocity
296  * m_maxSpeed is ship maximum speed based on user input. set in UpdateVelocity()
297  * -> sets m_velocity
298  * m_targetPoint holds current target coords. set by goto, warp, align, follow, orbit
299  * -> sets m_shipHeading
300  * m_shipHeading holds current direction and is set in _Turn()
301  * -> sets m_velocity
302  * m_velocity is current ship velocity. set in MoveObject()
303  * m_velocity = m_shipHeading * (m_currentSpeedFraction * m_maxSpeed)
304  */
305  if ((m_shipMaxAccelTime < 1.0) and (mySE->IsDynamicEntity()))
306  if (!mySE->HasPilot()) {
307  m_shipMaxAccelTime = (-log(0.001) * m_shipAgility);
308  } else if (!mySE->GetPilot()->IsUndock()) {
309  m_shipMaxAccelTime = (-log(0.001) * m_shipAgility);
310  }
311 
312  // this needs to distinguish between fraction change and speedboost change
313  if ((m_userSpeedFraction) and ((!fraction) or (m_prevSpeed) or (fraction != m_userSpeedFraction))) {
315  } else {
316  m_prevSpeedFraction = 0.0f;
317  }
318 
319  m_userSpeedFraction = fraction;
320  bool isMoving = false;
321  if ((m_currentSpeedFraction > 0.01) or (m_activeSpeedFraction > 0.01))
322  isMoving = true;
323  UpdateVelocity(isMoving);
324 
326  // set state to Ball::Mode::GOTO after setting warp decel variables, so warp completion will decel properly
328  return;
329  }
330 
331  std::vector<PyTuple*> updates;
332  updates.clear();
333  if (fraction) {
334  CmdSetSpeedFraction du;
335  du.entityID = mySE->GetID();
336  du.fraction = fraction;
337  updates.push_back(du.Encode());
338  }
339  if (((mySE->IsNPCSE() or mySE->IsDroneSE()) and !m_hasSentShipUpdates)
340  or mySE->IsMissileSE() or mySE->IsContainerSE() or mySE->IsWreckSE()) {
341  SetBallSpeed ms; //NPCs and Missiles only.
342  ms.entityID = mySE->GetID();
343  ms.speed = m_maxSpeed;
344  updates.push_back(ms.Encode());
345  m_hasSentShipUpdates = true;
346  }
347 
348  if (!updates.empty())
349  SendDestinyUpdate(updates);
350 }
351 
352 void DestinyManager::UpdateVelocity(bool isMoving) {
353  uint8 logType = 0;
354  if ((m_ballMode == Destiny::Ball::Mode::WARP) and (m_warpState != nullptr)) {
355  /* Warp() finished, and ship dropped out of warp at m_speedToLeaveWarp,
356  * reset m_shipMaxAccelTime as a fraction of m_speedToLeaveWarp/m_maxShipSpeed
357  * to set decel correctly, as m_speedToLeaveWarp varies with ship and warp distance.
358  */
359  logType = 1;
360  m_decel = true;
361  m_accel = false;
362  m_targBubble = nullptr;
366  } else if (m_userSpeedFraction) { //moving
367  if (isMoving) { //change speed
369  return;
370  // the times are a bit off when usf < 1.0, but acceptable for now. will revisit later. -allan 21Nov15
371  float delta = 1.0f;
372  logType = 2;
373  if (m_turning) { // if i was smart, i could write this shit where this "turning" check wasnt needed.
374  if (m_decel) {
375  logType = 4;
376  m_accel = true;
377  m_decel = false;
378  m_prevSpeed = 0.0f;
379  m_prevSpeedFraction = 0.0f;
381  delta = 1 - m_currentSpeedFraction;
382  } else {
383  logType = 3;
384  m_decel = true;
385  m_accel = false;
387  m_changeDelay = true; // skip a single tic before making change
390  }
391  } else if (m_decel) {
392  m_accel = true;
393  m_decel = false;
396  //m_activeSpeedFraction = m_currentSpeedFraction;
397  } else if (m_prevSpeed) {
400  // decel from deactivated prop mod
401  m_decel = true;
402  m_accel = false;
403  } else {
404  // accel from activated prop mod
405  m_accel = true;
406  m_decel = false;
407  m_prevSpeedFraction = 0.0f;
408  }
410  // decrease usf
411  m_decel = true;
412  m_accel = false;
413  m_prevSpeedFraction = 0.0f;
414  //get current ship speed and reset for speed change
417  } else {
418  // increase usf
419  m_accel = true;
420  m_decel = false;
422  }
423 
424  m_shipMaxAccelTime *= delta;
425  // will need a test here for (prevMaxSpeed > m_maxSpeed) to set ship velocity correctly (and avoid negatives)
426  if (m_prevSpeed > m_maxSpeed)
428 
429  if ((!m_turning) and (!m_decel)) {
432  }
433  } else { //begin movement
434  logType = 5;
435  m_accel = true;
436  m_decel = false;
437  m_shipMaxAccelTime *= m_userSpeedFraction; // for accel with user speeds <= 1.0
439  // see notes in MoveObject() for information relating to accel equations
440  m_currentSpeedFraction = (1 - exp(-0.01 / m_shipAgility));
442  }
443  if (is_log_enabled(DESTINY__MOVE_TRACE))
444  _log(DESTINY__MOVE_TRACE, "Destiny::UpdateVelocity - %s(%u): Speed Change - USF: %.2f, ASF: %.2f, CSF: %.2f, PSF: %.2f, pSpeed: %.2f, mSpeed: %.2f, accel: %s, decel: %s", \
446  m_accel ? "true" : "false", m_decel ? "true": "false");
447  } else if ((m_currentSpeedFraction) or (m_prevSpeedFraction)) {
448  if (isMoving) { //stop movement
449  logType = 6;
450  // will need a test here for (prevMaxSpeed > m_maxSpeed) to set ship velocity correctly (and avoid negatives)
451  // see notes in MoveObject() for information relating to decel equations
452  m_decel = true;
453  m_accel = false;
455 
456  // this is speed from prop mod, which is now deactivated
457  if (m_prevSpeed) {
459  } else {
461  }
463  } else { //halt
464  logType = 7;
465  Halt();
466  }
467  } else {
468  //WARNING conditional should never arrive here.
469  logType = 8;
470  //assert(0); // change this to a correct error-handling implementation
471  }
472  std::string msg = "";
473  switch (logType) {
474  case 1: { msg = "state == warp. --Begin Decel"; } break;
475  case 2: { msg = "USF != 0 and ship isMoving. --Different Heading or Speed"; } break;
476  case 3: { msg = "USF != 0 and ship isMoving. --Decel for Turn"; } break;
477  case 4: { msg = "USF != 0 and ship isMoving. --Accel after Turn"; } break;
478  case 5: { msg = "USF != 0 and ship stopped. --Begin Accel"; } break;
479  case 6: { msg = "USF == 0 and ship isMoving. --Stop"; } break;
480  case 7: { msg = "USF == 0 and ship stopped. --Halt"; } break;
481  case 8: {
482  _log(DESTINY__ERROR, "Destiny::UpdateVelocity Error! Ship %s(%u) Has No WarpState or Speed Fraction.",
483  mySE->GetName(), mySE->GetID());
484  Halt();
485  return;
486  } break;
487  }
488  if (is_log_enabled(DESTINY__MOVE_TRACE))
489  _log(DESTINY__MOVE_TRACE, "Destiny::UpdateVelocity - %s(%u): %s AccelTime: %.2f, USF: %.2f, ASF: %.2f, CSF: %.2f, PSF: %.2f", \
491 }
492 
493 //Global Actions:
495  if (m_stop)
496  return;
497 
498  // AP not implemented yet in this version -allan 4Mar15
499  //Clear autopilot
500  if (mySE->HasPilot())
501  mySE->GetPilot()->SetAutoPilot(false);
502 
503  if (m_userSpeedFraction == 0.0f) {
504  //state is already at stop. but m_stop wasnt set.
505  // set m_stop and return.
506  m_stop = true;
507  return;
508  } else if ((m_ballMode == Destiny::Ball::Mode::WARP) and (!IsWarping())) {
509  //warp aborted before initialized. standard Stop() applies.
511  } else if (IsMoving()) {
512  //stop called while moving
514  }
515 
516  m_accel = false;
517  m_decel = false;
518  m_prevSpeed = 0.0f;
519  m_prevSpeedFraction = 0.0f;
520 
521  ClearTurn();
522  ClearOrbit();
523 
524  // reset move timers for new state
525  //m_changeDelay = true; // skip a single tic before making change
527  m_stateStamp = sEntityList.GetStamp();
528  //reset max accel time in case it was changed previously
529  m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
530 
531  SetSpeedFraction(0.0f);
532  m_stop = true;
533 
534  CmdStop du;
535  du.entityID = mySE->GetID();
536  PyTuple *up = du.Encode();
538  PyDecRef(up);
539 }
540 
542 
544 
545  // reset ALL movement variables and states. calling this will set object to a COMPLETE and IMMEDIATE stop.
547  m_stop = true;
548  m_accel = false;
549  m_decel = false;
550  m_turning = false;
551  m_maxSpeed = 0.0f;
553  m_moveTime = 0.0;
554  m_prevSpeed = 0.0f;
555  m_stateStamp = 0;
557  m_stopDistance = 0;
558  m_targetDistance = 0;
559  m_followDistance = 0;
560  m_prevSpeedFraction = 0.0f;
561  m_userSpeedFraction = 0.0f;
562  m_activeSpeedFraction = 0.0f;
563  m_currentSpeedFraction = 0.0f;
565 
566  m_targetEntity.first = 0;
567  m_targetEntity.second = nullptr;
568 
569  ClearTurn();
570 
571  if ((m_shipMaxAccelTime < 1.0) and (mySE->IsDynamicEntity()))
572  m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
573 
574  if (is_log_enabled(DESTINY__MOVE_TRACE))
575  _log(DESTINY__MOVE_TRACE, "Destiny::Halt() - %s(%u): m_shipHeading: %.3f,%.3f,%.3f", \
577 }
578 
580 {
581  // basic updates for ejecting from ship
582  Stop();
585 }
586 
587 // Global collision methods
588 // check for collision. called by Move()
590 {
591  double profileStartTime = GetTimeUSeconds();
592 
593  // collision detection code here
594  /* in this case, we are ONLY interested in objects
595  * that have drifted within each others radius (for whatever reason)
596  * this only checks for ships running sub-warp speeds
597  * in relation to other objects in bubble.
598  */
599 
600  // NOTE: object's "massive = true" means it can bump/collide (massive = solid)
601 
602  // initial implementation will ONLY check player ships for bumping.
603  std::vector<Client*> vPlayers;
604  mySE->SysBubble()->GetPlayers(vPlayers);
605  Client* pClient = mySE->GetPilot();
606  GPoint pos(GetPosition());
607  float distance = 0.0f;
608  for (auto cur : vPlayers) {
609  if (cur == pClient)
610  continue;
611  distance = pos.distance(cur->GetShipSE()->GetPosition());
612  distance -= (mySE->GetRadius() - cur->GetShipSE()->GetRadius());
613  if (distance < BUMP_DISTANCE) {
614  Bump(cur->GetShipSE());
615  m_bump = true;
616  } else {
617  m_bump = false;
618  }
619  }
625  if (sConfig.debug.UseProfiling)
626  sProfiler.AddTime(Profile::collision, GetTimeUSeconds() - profileStartTime);
627 }
628 
630 {
631  if (m_bump)
632  return;
633  // bump code here
634  /* determine most massive object...maybe not. use percentiles here (becham math)
635  * determine direction(s) involved
636  * determine speed(s) involved
637  * determine new headings based on above
638  * determine new speed based on above
639  *
640  * NOTE static or large objects wont move, but will apply equal and opposite force.
641  * un-anchored objects WILL move (jetcans, wrecks, portable hangers)
642  */
643 
644  /* bump math, by Scheulagh Santorine, Ph.D.
645  * velocity of bumped object immediately after bump
646  * v2(t=0+) = 2v1*m1/m1+m2
647  */
648 
649  /* run-time options for bumping jetcans, biomass, and other space objects
650  * bump drones?? prolly not, for simplicity
651  */
652  std::string msg1 = "You have bumped ";
653  msg1 += pSE->GetPilot()->GetName();
654  mySE->GetPilot()->SendNotifyMsg(msg1.c_str());
655  // this test isnt needed right now, as it's ONLY checking against players and will always return true.
656  // will keep it in here for later expansion.
657  if (pSE->HasPilot()) {
658  std::string msg2 = "You have been bumped by ";
659  msg2 += mySE->GetPilot()->GetName();
660  pSE->GetPilot()->SendNotifyMsg(msg2.c_str());
661  }
662 }
663 
664 void DestinyManager::Bounce(GVector direction, float speed)
665 {
666  // bounce code here (not used yet)
667  /* this code will update ship movement after being bumped
668  * all items will drift to a complete stop, unless other movement is called.
669  */
671  m_stop = false;
672  m_stateStamp = sEntityList.GetStamp();
674  m_shipMaxAccelTime = 0.1f;
675  m_userSpeedFraction = 1.0f;
676  m_currentSpeedFraction = 1.0f;
679 
680  std::vector<PyTuple*> updates;
681  SetBallVelocity bv;
682  bv.entityID = mySE->GetID();
683  bv.x = m_velocity.x;
684  bv.y = m_velocity.y;
685  bv.z = m_velocity.z;
686  updates.push_back(bv.Encode());
687  CmdGotoDirection du;
688  du.entityID = mySE->GetID();
689  du.x = m_shipHeading.x;
690  du.y = m_shipHeading.y;
691  du.z = m_shipHeading.z;
692  updates.push_back(du.Encode());
693  SendDestinyUpdate(updates);
694  Stop();
695 }
696 
697 // main movement method
699  if (mySE->SysBubble() == nullptr)
701 
702  if (m_stateStamp > sEntityList.GetStamp()) {
703  if (is_log_enabled(DESTINY__MOVE_TRACE))
704  _log(DESTINY__MOVE_TRACE, "Destiny::MoveObject() - %s(%u): stateStamp (%u) > GetStamp (%u).", \
705  mySE->GetName(), mySE->GetID(), m_stateStamp, sEntityList.GetStamp());
706  return;
707  }
708 
709  if (m_changeDelay) {
710  m_changeDelay = false;
712  _log(DESTINY__MOVE_TRACE, "Destiny::MoveObject() - ChangeDelay - %s(%u): stateStamp: %u", \
714  return;
715  }
716 
717  //apply our velocity to our position for 1 unit of time (second)
718 
719  /* acceleration and deceleration are both logarithmic, and the server needs to keep up with client position.
720  * this is another step towards getting ready for collision detection.
721  *
722  * formula for time taken to accelerate from v to V, from https://wiki.eveonline.com/en/wiki/Acceleration
723  *
724  * t=IM(10^-6) * -ln(1-(v/V))
725  *
726  * as this uses the natural log, the higher the speed, the slower the acceleration, to the limits of ln(0)
727  * since lim ln(x) = -INFINITY where x->0+. and ln(0) is undefined, we will use
728  *
729  * m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
730  *
731  * to define the time it will take a given ship to reach 99.9999% of m_maxShipSpeed, at which point,
732  * the server will set m_velocity = (m_maxShipSpeed * direction) (or 100% ship speed).
733  *
734  * to define speed at X time, we will use the following equation.
735  *
736  * Vt = Vm * (1 - e(-t * 10^6 / IM))
737  *
738  * where
739  * Vt = ships velocity at t
740  * Vm = ships maximum velocity
741  * t = time
742  * I = ships inertia in s/kg
743  * M = ships mass in kg
744  * e = base of natural logarithms
745  */
746 
747  /* **UPDATE** this now uses time AND (m_currentSpeedFraction > 0.9999f) for min/max speeds. -allan 6Aug14
748  * **UPDATE** this is now tracking ALL speed changes correctly. -allan 21Nov15
749  * **UPDATE** initial orbit implementation. -allan 13July16
750  * **UPDATE** removed speed fraction checks for min/max speeds. -allan 02Jul17
751  */
752 
753  double timeStamp(0); // keep all these timers in seconds.
754  // check for moving ship changing heading
755  if (m_userSpeedFraction) {
757  Turn();
758  }
759 
760  timeStamp = (GetTimeMSeconds() - m_moveTime) /1000;
761 
762  float speed(0.0f);
763  std::string move = "";
764  // check to make sure we dont overrun usf/asf
766  m_currentSpeedFraction = 1.0f;
767 
768  if (timeStamp > m_shipMaxAccelTime) { // and ((m_currentSpeedFraction > 0.9999f) or (m_activeSpeedFraction > m_prevSpeedFraction)))
769  m_accel = m_decel = false;
770  if (m_prevSpeed > m_maxSpeed) {
772  if (is_log_enabled(DESTINY__MOVE_TRACE))
773  _log(DESTINY__MOVE_TRACE, "Destiny::MoveObject() - %s(%u) has decel'd from %.2fm/s to %.2fm/s of max %.2f in %.3fs.", \
774  mySE->GetName(), mySE->GetID(), m_prevSpeed, speed, m_maxSpeed, timeStamp);
775  m_prevSpeed = 0.0f;
776  m_prevSpeedFraction = 0.0f;
777  m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
778  }
779  if (m_userSpeedFraction) {
780  // ship has reached full speed (whatever the fraction was set to)
781  m_currentSpeedFraction = 1.0f;
783  move = "at constant speed, going";
785  } else {
786  //ship has reached full stop
787  // update position one final time (for last bit of drift) and exit movement functions by calling Halt()
788  SetPosition(m_position + m_velocity, sConfig.debug.PositionHack); // (PositionHack == true) here will force position update to client
789  if (is_log_enabled(DESTINY__MOVE_TRACE))
790  _log(DESTINY__MOVE_TRACE, "Destiny::MoveObject() - %s(%u) is at full stop after %.3f seconds.", \
791  mySE->GetName(), mySE->GetID(), timeStamp);
792  Halt();
793  return;
794  }
795  } else {
796  //not full speed yet or has changed speed
797  if (m_turning) {
798  move = "turning";
799  } else {
800  move = "accelerating";
801  }
802 
803  m_currentSpeedFraction = (1 - exp(-timeStamp / m_shipAgility));
804 
805  if (m_accel) {
807  } else if (m_decel) {
811  } else if (m_tractored or m_tractorPause or (m_activeSpeedFraction == 1)) {
812  ; // do nothing here. this is to remove error reporting from next line.
813  } else {
814  _log(DESTINY__ERROR, "Destiny::MoveObject() - %s(%u) - move checks are not set right. Acc:%s, Dec:%s, Turn:%s, Tic:%u, Tractored:%s, TractorPause:%s", \
815  mySE->GetName(), mySE->GetID(), (m_accel ? "True" : "False"), (m_decel ? "True" : "False"), (m_turning ? "True" : "False"), \
816  m_turnTic, (m_tractored ? "True" : "False"), (m_tractorPause ? "True" : "False"));
817  }
818 
819  if (m_prevSpeed) {
822  //else if (m_turning)
823  // speed = m_maxSpeed * m_activeSpeedFraction;
824  } else if (m_prevSpeed > m_maxSpeed) {
826  //else if (m_prevSpeed < m_maxSpeed)
827  // speed = (m_maxSpeed - m_prevSpeed) * m_activeSpeedFraction;
828  } else {
830  }
831  } else {
833  }
834 
835  if ((!m_userSpeedFraction) or m_decel) {
836  if (m_turning) {
837  // decel for turn
838  move = "decelerating for turn";
839  } else {
840  //just decelerating
841  move = "decelerating";
842  }
843 
844  //if (m_prevSpeedFraction < 1.0f) {
845  // //m_currentSpeedFraction -= m_activeSpeedFraction + m_prevSpeedFraction;
846  // speed = m_maxSpeed * m_currentSpeedFraction;
847  //} else {
848  if (m_prevSpeed) {
849  speed = m_maxSpeed + speed;
850  } else {
851  speed = m_maxSpeed - speed;
852  }
854  //}
855  }
856  }
857  // ships tend to "level out" when stopping. try to mimic that here (wip)
858  if (m_stop and (m_currentSpeedFraction < 0.85f) and (m_currentSpeedFraction > 0.09f)) {
859  if (m_shipHeading.y < -0.05) {
860  m_shipHeading.y += 0.03;
861  } else if (m_shipHeading.y > 0.05) {
862  m_shipHeading.y -= 0.03;
863  }
864  }
865 
866  if (m_orbiting)
868  // object is orbiting...set orbit speed correctly.
869  speed *= m_maxOrbitSpeedFraction;
870  move += " in orbit";
871  }
872 
873  if ((m_prevSpeed) or (m_prevSpeedFraction)) {
874  if (is_log_enabled(DESTINY__MOVE_TRACE))
875  _log(DESTINY__MOVE_TRACE, "Destiny::MoveObject() - %s(%u) is %s at %.4f m/s (csf:%.4f asf:%.4f pSpeed:%.2f(%.3f), sec: %.3f).", \
877  } else {
878  if (is_log_enabled(DESTINY__MOVE_TRACE))
879  _log(DESTINY__MOVE_TRACE, "Destiny::MoveObject() - %s(%u) is %s at %.4f m/s (csf:%.4f asf:%.4f sec: %.3f).", \
880  mySE->GetName(), mySE->GetID(), move.c_str(), speed, m_currentSpeedFraction, m_activeSpeedFraction, timeStamp);
881  }
882 
883  //set speed, direction and position for this round of movement
884  m_velocity = m_shipHeading * speed;
885  SetPosition(m_position + m_velocity, sConfig.debug.PositionHack); // (PositionHack == true) here will force position update to client
886 
887  if (is_log_enabled(DESTINY__MOVE_DEBUG))
888  _log(DESTINY__MOVE_DEBUG, "Destiny::MoveObject() - %s(%u) Pos:%.2f,%.2f,%.2f Vel:%.3f,%.3f,%.3f Head:%.3f,%.3f,%.3f", \
891 
892  if (sConfig.cosmic.BumpEnabled)
893  if (mySE->HasPilot() and mySE->SysBubble()->HasPlayers()) // no players in bubble = nothing to check against (for now)
894  CheckBump();
895 
896  if (sEntityList.GetTracking()) {
897  // create jetcan to visualize object movement
898  std::string str = mySE->GetName();
899  str += " ";
900  str += itoa(timeStamp);
901  ItemData idata(23, ownerSystem, mySE->GetLocationID(), flagNone, str.c_str(), m_position, "Position Test");
903  if (iRef.get() != nullptr) {
904  // create new container
905  FactionData data = FactionData();
906  ContainerSE* cSE = new ContainerSE(iRef, mySE->GetServices(), mySE->SystemMgr(), data);
907  if (cSE == nullptr)
908  return;
909  iRef->SetMySE(cSE);
910  mySE->SystemMgr()->AddMarker(cSE);
911  }
912  }
913 }
914 
915 bool DestinyManager::IsTurn() { //this is working. dont change
916  if (m_targetPoint.isZero()) {
917  _log(DESTINY__ERROR, "Destiny::IsTurn() - %s(%u): TargetPoint is null.", mySE->GetName(), mySE->GetID());
918  ClearTurn();
919  Halt();
920  return false;
921  }
922  // if ship is stopped, there is no turn. immediately begin movement in desired direction
923  if ((m_currentSpeedFraction < 0.1) and (m_activeSpeedFraction < 0.1)) {
925  toVec.normalize();
926  m_shipHeading = toVec;
927  return false;
928  }
929 
930  // check for turning angle. returns true if angle is enough to change movement variables
931  // create isosceles triangle where legs are current direction and destination, then find angle between legs
932  // it will set m_radians in the range of [-pi,pi].
935  toVec.normalize();
936  float dot(toVec.dotProduct(m_shipHeading));
937  if ((dot > 1.0f) or (dot < -1.0f)) {
938  sLog.Error("Destiny::IsTurn()", "%s(%u) - shipHeading has screwed up. dot is %.5f", mySE->GetName(), mySE->GetID(), dot);
939  _log(DESTINY__ERROR, "Destiny::IsTurn() m_shipHeading: %.3f,%.3f,%.3f. m_targetHeading: %.3f,%.3f,%.3f, toVec:%.3f,%.3f,%.3f", \
941  // try to correct for bad heading vector and retest...
942  if (m_shipHeading.x > 1.0f) { m_shipHeading.x -= 1; }
943  else if (m_shipHeading.x < 1.0f) { m_shipHeading.x += 1; }
944  if (m_shipHeading.y > 1.0f) { m_shipHeading.y -= 1; }
945  else if (m_shipHeading.y < 1.0f) { m_shipHeading.y += 1; }
946  if (m_shipHeading.z > 1.0f) { m_shipHeading.z -= 1; }
947  else if (m_shipHeading.z < 1.0f) { m_shipHeading.z += 1; }
948  dot = toVec.dotProduct(m_shipHeading);
949  if ((dot > 1.0f) or (dot < -1.0f)) {
950  sLog.Error("Destiny::IsTurn()", "%s(%u) - shipHeading has screwed up AGAIN. dot is %.5f", mySE->GetName(), mySE->GetID(), dot);
951  return false;
952  }
953  }
954  m_radians = std::acos(dot);
955  float degrees(EvE::Trig::Rad2Deg(m_radians));
956  if (degrees < TURN_ALIGNMENT/*4*/) {
957  m_shipHeading = toVec;
958  return false;
959  }
960  if (is_log_enabled(DESTINY__TURN_TRACE)) {
961  _log(DESTINY__TURN_TRACE, "Destiny::IsTurn() - %s(%u): dot: %.5f, radians:%.5f, degrees:%.3f",\
962  mySE->GetName(), mySE->GetID(), dot, m_radians, degrees);
963  _log(DESTINY__TURN_TRACE, "Destiny::IsTurn() m_shipHeading: %.3f,%.3f,%.3f. m_targetHeading: %.3f,%.3f,%.3f", \
965  }
966  return true;
967 }
968 
969 /* Quaternion slerp(Quaternion const &v0, Quaternion const &v1, double t) {
970  * // v0 and v1 should be unit length or else something broken will happen.
971  *
972  * // Compute the cosine of the angle between the two vectors.
973  * double dot = dot_product(v0, v1);
974  *
975  * const double DOT_THRESHOLD = 0.9995;
976  * if (dot > DOT_THRESHOLD) {
977  * // If the inputs are too close for comfort, linearly interpolate
978  * // and normalize the result.
979  *
980  * Quaternion result = v0 + t*(v1 – v0);
981  * result.normalize();
982  * return result;
983  * }
984  *
985  * Clamp(dot, -1, 1); // Robustness: Stay within domain of acos()
986  * double theta_0 = acos(dot); // theta_0 = angle between input vectors
987  * double theta = theta_0*t; // theta = angle between v0 and result
988  *
989  * Quaternion v2 = v1 – v0*dot;
990  * v2.normalize(); // { v0, v2 } is now an orthonormal basis
991  *
992  * return v0*cos(theta) + v2*sin(theta);
993  * }
994  */
995 
996 //from new source at eve/client/script/ui/services\flightControls.py
997 // self.curve = trinity.Tr2QuaternionLerpCurve()
998 void DestinyManager::Turn() { // tracking within 900m for Frigates, 1k4m for BS. 05Jun17
999  if (mySE->HasPilot())
1000  if (mySE->GetPilot()->IsUndock())
1001  return;
1002 
1003  if (!IsTurn()) {
1004  if (m_turning)
1005  ClearTurn();
1006  return;
1007  }
1008  /*when changing directions....
1009  * m_moveTime will have to be reset - call UpdateVelocity() when turn starts
1010  * m_shipHeading will have to be reset - reset here and used in MoveObject() (our calling function)
1011  * check for decel, then call UpdateVelocity() to set variables as needed. Move() will handle the rest.
1012  *
1013  * m_degPerTic = (65.0f - m_shipAgility) /10; ([this file]:2317, reset for ab/mwd [this file]:2154)
1014  */
1015 
1016  // this is off for rookie ship (maybe others)
1017  float turnTime(m_shipAgility /2);
1018  if (!m_turning) {
1019  m_turning = true;
1020  //m_radians is set in IsTurn() on every tic
1021  m_turnFraction = std::sqrt((std::cos(m_radians) + 1) / 2);
1022  //this isnt used yet...used as comparison for testing time calc's
1024  if (is_log_enabled(DESTINY__TURN_TRACE))
1025  _log(DESTINY__TURN_TRACE, "Destiny::Turn() - %s(%u): Agility:%.3f, Inertia:%.3f, alignTime:%.3f, turnTime:%.3f, turnFraction:%.3f, m_degPerTic:%.3f", \
1027  }
1028 
1029  ++m_turnTic;
1030 
1031  // logic to determine speed changes for turning
1032  if (m_turnTic == 1)
1034  UpdateVelocity(true);
1035 
1036  // need to check turnFraction vs m_currentSpeedFraction to hold speed when turning.
1037 
1038  /* class agility
1039  * Capsule .06
1040  * Shuttle 1.6
1041  * Rookie 5
1042  * Frigates 3 - 6 (adv. 3 - 4) (2s) 1s < 0.15 not enough.
1043  * Destroyers 4 - 5
1044  * Cruisers 4 - 8
1045  * T3 Cruiser 2.4 - 2.8
1046  * HAC 5 - 7
1047  * Battlecruisers 6 - 9
1048  * Battleships 8 - 14 (12s) 4s 0.15 works well 0.2 is very well. > 0.25 is too much.
1049  * Industrials 8 - 12
1050  * Marauder ~12
1051  * Orca 40 (40s) 18s 0.05 turnPercent seems to work very well. > 0.1 is wrong.
1052  * Freighters ~60
1053  * Supercarrier ~60
1054  * Command ~9
1055  * Transport 5 or 19
1056  * Barges 10 - 18
1057  * Dreadnought ~55
1058  * Zephyr 5
1059  */
1060  // set ship turn amount based on position in turn, current speed and ship agility
1061  GVector deltaHeading(m_shipHeading, m_targetHeading);
1062  float turnPercent(0.1f);
1063  float degrees(EvE::Trig::Rad2Deg(m_radians));
1064  if (degrees > 100) {
1065  if (m_decel and (m_turnTic > turnTime)) {
1066  // turn half of remaining turn (simulate greatest turn angle when (turn > 90*) and (speed < time)
1067  turnPercent = 0.4f;
1068  } else {
1069  turnPercent = m_degPerTic / (degrees - 100);
1070  }
1071  } else if (degrees > m_degPerTic) {
1072  turnPercent = m_degPerTic / (degrees * 0.5);
1073  } else {
1074  // degrees < m_degPerTic, so complete turn and continue accel
1075  if (m_decel)
1076  UpdateVelocity(true);
1077  }
1078 
1079  if (turnPercent > 1.0) {
1080  _log(DESTINY__ERROR, "Destiny::Turn() - turnTic:%u, degRemain:%.3f, turnPercent:%.2f", m_turnTic, degrees, turnPercent);
1081  turnPercent = 0.9;
1082  }
1083  deltaHeading *= turnPercent;
1084  m_shipHeading += deltaHeading;
1085  if (is_log_enabled(DESTINY__TURN_TRACE))
1086  _log(DESTINY__TURN_TRACE, "Destiny::Turn() - csf:%.3f, turnTic:%u, degRemain:%.3f (deltaHeading:%.5f, %.5f, %.5f * turnPercent:%.2f) = shipHeading:%.3f, %.3f, %.3f", \
1087  m_currentSpeedFraction, m_turnTic, degrees, deltaHeading.x, deltaHeading.y, deltaHeading.z, turnPercent, m_shipHeading.x, m_shipHeading.y, m_shipHeading.z);
1088 }
1089 
1091  //SetPosition(m_position, sConfig.debug.PositionHack); // (PositionHack == true) here will force position update to client
1092  SetPosition(m_position, true);
1093  m_turnTic = 0;
1094  m_turning = false;
1095  m_radians = 0.0f;
1096  m_turnFraction = 0.0f;
1098 }
1099 
1101  // Follow is also used by client as AlignTo.
1102  const GPoint& target_point = m_targetEntity.second->GetPosition();
1103  GVector heading(m_position, target_point);
1104  m_targetDistance = (uint32)(heading.length() - m_radius);
1105 
1107  if (mySE->HasPilot())
1108  if (mySE->GetPilot()->IsAutoPilot()) {
1109  SetSpeedFraction(0.1);
1110  _log(AUTOPILOT__TRACE, "DestinyManager::Follow() - Target within FollowDistance. SpeedFraction = 0.1.");
1111  return;
1112  }
1113  // this will allow following entities to keep their follow state, yet stop movement if within their follow distance.
1114  // by keeping their follow state, once the distance is greater than their follow distance, they will begin movement again.
1115  if (m_tractored) {
1116  // specific to tractored entities. sudden halt to mimic tractor stopping
1117  if (!m_tractorPause) {
1118  std::vector<PyTuple*> updates;
1119  CmdSetSpeedFraction ssf;
1120  ssf.entityID = mySE->GetID();
1121  ssf.fraction = 0;
1122  updates.push_back(ssf.Encode());
1123  SendDestinyUpdate(updates);
1124  }
1126  m_tractorPause = true;
1128  return;
1129  } else {
1130  if ((m_targetEntity.second->IsDynamicEntity()) and (m_targetEntity.second->DestinyMgr()->IsMoving())) {
1131  // this will mimic real movement, where ship will decel instead of a sudden halt
1132  // still need to call MoveObject() here
1133  SetSpeedFraction(0.2);
1134  } else {
1135  Stop();
1136  }
1137  }
1138  } else {
1139  if (m_tractored and m_tractorPause) {
1140  // tractored object is outside follow distance. begin movement again
1141  if (m_tractorPause) {
1142  std::vector<PyTuple*> updates;
1143  CmdSetSpeedFraction ssf;
1144  ssf.entityID = mySE->GetID();
1145  ssf.fraction = 1;
1146  updates.push_back(ssf.Encode());
1147  SendDestinyUpdate(updates);
1148  }
1149  m_tractorPause = false;
1152  m_stateStamp = sEntityList.GetStamp();
1153  m_prevSpeedFraction = 0.0f;
1154  // there is no accel/decel for tractor'd items
1156  } else if (m_userSpeedFraction != 0.0f) {
1157  SetSpeedFraction(1.0f);
1158  }
1159  }
1160 
1161  heading.normalize();
1162  m_targetPoint = target_point + (heading * m_targetDistance);
1163 
1164  MoveObject();
1165 }
1166 
1167 /*eve/client/script/ui/services\flightPredictionSvc.py
1168 """
1169 Prediction service for in-space flight
1170 """
1171 */
1173  // data consistency checks...
1175  // well, something fucked up. stop object and throw error. player can reset if they want to.
1176  if (mySE->HasPilot())
1177  mySE->GetPilot()->SendErrorMsg("Internal Server Error. Ref: ServerError 35412");
1178  sLog.Error("Destiny::Orbit()", "%s(%u) - Distance check OOB. ", mySE->GetName(), mySE->GetID());
1179  Stop();
1180  return;
1181  }
1182 
1183  // this will set position of ship relative to target, based on period of orbit.
1184 
1185  /* destiny variables used here
1186  * m_position - probably the most important calculated value.
1187  * m_velocity - 2nd most important calculated value
1188  * m_targetDistance - commanded orbit distance
1189  * m_followDistance - calculated orbit distance based on mass, velocity, gravity, and other ship variables
1190  * m_targetHeading - direction to target from current position
1191  * m_targetPoint - calculated distant point from above variable
1192  * m_shipHeading - current direction ship is pointed
1193  * m_stateStamp - time movement started. 1Hz tic
1194  * m_orbiting - 0=no orbit, >0=in orbit, 1=at distance, 2=too close , 3=too far, 4=way too close, 5=way too far
1195  * m_orbitRadTic - rad/sec in current orbit. set by Orbit() (~2090)
1196  * m_maxOrbitSpeedFraction - calculated max speed to maintain commanded orbit distance. set in Orbit() but not used here yet
1197  *
1198  * our target variables
1199  * Tr = target radius
1200  * Tp = target position (updated for movement, if applicable)
1201  * Tv = target velocity
1202  * Th = target heading (updated for movement, if applicable)
1203  * Tm = target mass
1204  *
1205  * centers = distance between object and target centers
1206  * edges = distance between object and target closest edges (counting for radius)
1207  *
1208  */
1209 
1211  // get current times
1212  uint32 timeStamp = sEntityList.GetStamp() - m_stateStamp;
1213  float Tr = m_targetEntity.second->GetRadius();
1214  //float Tm = m_targetEntity.second->GetSelf()->GetAttribute(AttrMass).get_float();
1215  GPoint Tp(m_targetEntity.second->GetPosition());
1216 
1217  // current and edges are used to determine ship's orbit distance, and adjust position accordingly
1218  double centers(m_position.distance(Tp));
1219  double edges(centers - m_radius - Tr);
1220  if (is_log_enabled(DESTINY__ORBIT_TRACE))
1221  _log(DESTINY__ORBIT_TRACE, "1 - %s(%u): time:%u, centers:%.2f, edges:%.2f, target:%u, follow:%u", \
1222  mySE->GetName(), mySE->GetID(), timeStamp, centers, edges, m_targetDistance, m_followDistance);
1223 
1224  // distances checks for orbit calculations
1225  GPoint mPos(NULL_ORIGIN);
1226  float mPosAdj(0.0f);
1227  // check distances for this tic
1228  if ((edges /2) > m_followDistance) {
1230  MoveObject();
1231  return;
1232  }
1233  // too far to realistically orbit.
1235  // TODO: update this to determine orbit and set heading/target to smoothly go from turn into orbit trajectory
1236  // set point to side of target (based on current position), to avoid near-zero angular velocity
1237  double radTarg = atan2(Tp.z - m_position.z, Tp.x - m_position.x); // rad from '0' to target
1238  radTarg += atan2(m_followDistance, edges); // rad from 'distance line' to target 'offset'
1239  mPos.x = m_followDistance * cos(radTarg);
1240  mPos.z = m_followDistance * sin(radTarg);
1241  if (Tp.y > m_position.y) { // target is above us. set point below target using calculated distance
1242  mPos.y = Tp.y - m_position.y;
1243  } else { // opposite of above
1244  mPos.y = m_position.y - Tp.y;
1245  }
1246  m_targetPoint = Tp + mPos;
1247  GVector heading(m_position, m_targetPoint);
1248  heading.normalize();
1249  m_shipHeading = heading; // this sets object velocity using speed
1250  _log(DESTINY__ORBIT_TRACE, "2 - way too far - rads:%.3f, heading: %.3f, %.3f, %.3f", \
1252  MoveObject();
1253  return;
1254  } else if ( (centers + m_targetDistance / 3) < m_followDistance) {
1256  MoveObject();
1257  return;
1258  }
1259  // to close to realistically orbit. move away from target
1261  // set point to side of target (based on current position), to avoid near-zero angular velocity
1262  double radTarg = atan2(Tp.z - m_position.z, Tp.x - m_position.x); // rad from '0' to target
1263  //radTarg += atan2(m_followDistance, edges); // rad from 'distance line' to target 'offset'
1264  mPos.x = m_followDistance * cos(radTarg);
1265  mPos.z = m_followDistance * sin(radTarg);
1266  if (Tp.y > m_position.y) { // target is above us. set point below target using calculated distance
1267  mPos.y = Tp.y - m_position.y;
1268  } else { // opposite of above
1269  mPos.y = m_position.y - Tp.y;
1270  }
1271  m_targetPoint = Tp + mPos;
1272  GVector heading(m_position, m_targetPoint);
1273  heading.normalize();
1274  m_shipHeading = heading; // this sets object velocity using speed
1275  _log(DESTINY__ORBIT_TRACE, "2 - way too close - rads:%.3f, heading: %.3f, %.3f, %.3f", \
1277  MoveObject();
1278  return;
1279  } else if ((edges - m_targetDistance /4) > m_followDistance) {
1281  // fudge distance for a smaller orbit
1282  // modify this based on calculated distance
1283  mPosAdj = -m_followDistance / 25;
1284  _log(DESTINY__ORBIT_TRACE, "2 - too far");
1285  } else if (centers < m_followDistance) {
1287  // fudge distance for larger orbit
1288  // modify this based on calculated distance
1289  mPosAdj = m_followDistance / 25;
1290  _log(DESTINY__ORBIT_TRACE, "2 - too close");
1291  } else {
1293  _log(DESTINY__ORBIT_TRACE, "2 - within tolerance");
1294  }
1295 
1296  #define LogMacro(v) _log(DESTINY__ORBIT_TRACE, "m - " #v ": (%.3f, %.3f, %.3f) len=%.3f", v.x, v.y, v.z, v.length())
1297 
1298  // new orbit code
1299  float radius = m_followDistance + mPosAdj;// fudge a bit as using targetDistance is a hair too close
1300  // angle around y axis (from +x) - horizontal movement - ccw from +x using ships orbit in rad/tic
1301  float theta = EvE::Trig::Pi2 - EvE::Trig::Deg2Rad(360) - (m_orbitRadTic * timeStamp);
1302  // angle around xz axis (from 0) - vertical movement
1303  //GVector target(m_position, Tp);
1304  //LogMacro(target);
1305  //float hyp = sqrt(pow(target.z, 2) + pow(target.x, 2));
1306  float inclination = 45; //atan(hyp / target.y);
1307  // fractional value of orbit period (0 < x < 1)
1308  float period = fmod(timeStamp, m_orbitTime) /m_orbitTime;
1309  // calculate a pendulum value here to adjust elevation (+/-y) where +x is 1, 0x is 0, -x is -1
1310  float c = cos(EvE::Trig::Deg2Rad(360 * period));
1311  // get elevation modifier based on orbit period
1312  float phi = EvE::Trig::Deg2Rad(inclination * c);
1313  // set xz plane modifier from elevation
1314  float s = sin(EvE::Trig::Deg2Rad(360 * period));
1315  float mu = EvE::Trig::Deg2Rad(inclination * s);
1316  // here we will adjust orbit plane by adding OrbitRotation angle to theta
1317  // calculate position
1318  mPos.x = radius /* mu */* cos( theta );
1319  mPos.z = radius /* mu */* sin( theta );
1320  mPos.y = radius * phi;
1321  _log(DESTINY__ORBIT_TRACE, "4 - theta:%.5f, phi:%.3f, mu:%.2f period:%.5f, radius:%.3f, inc:%.5f", theta,phi,mu,period,radius,inclination);
1322  LogMacro(mPos);
1323  // apply origin to our calculated position
1324  mPos += Tp;
1325  // set position for this tic
1326  m_position = mPos;
1327 
1328  // set heading for this tic
1329  GPoint mPosNext(NULL_ORIGIN);
1330  theta += m_orbitRadTic;
1331  period = fmod(timeStamp+1, m_orbitTime) /m_orbitTime;
1332  c = cos(EvE::Trig::Deg2Rad(360 * period));
1333  phi = EvE::Trig::Deg2Rad(inclination * c);
1334  mPosNext.x = radius * cos( theta );
1335  mPosNext.z = radius * sin( theta );
1336  mPosNext.y = radius * phi;
1337  LogMacro(mPosNext);
1338  // determine where our target should be next tic, and figure that into our heading calculation
1339  float Tv = (m_targetEntity.second->DestinyMgr() != nullptr ? m_targetEntity.second->DestinyMgr()->GetSpeed() : 0);
1340  GVector Th(m_targetEntity.second->DestinyMgr() != nullptr ? m_targetEntity.second->DestinyMgr()->GetHeading() : NULL_ORIGIN_V);
1341  Tp += (Tv*Th); // use Tv*Th and add to position to account for target movement. Tv for non-moving targets return 0.
1342  mPosNext += Tp;
1343  GVector heading(m_position, mPosNext);
1344  heading.normalize();
1345  m_shipHeading = heading;
1346  m_targetPoint = m_position + (m_shipHeading * 1.0e16);
1347  LogMacro( heading );
1348 
1350  if (is_log_enabled(DESTINY__ORBIT_TRACE))
1351  _log(DESTINY__ORBIT_TRACE, "5(%u) - orbiting at %.2f. timestamp:%u, speed:%.2f", \
1352  m_orbiting, m_position.distance(Tp), timeStamp, curSpeed);
1353 
1354  MoveObject();
1355 }
1356 
1358  /*
1359  * orbital definitions for EVEmu:
1360  * node line = ascending node
1361  * ascending node = line of intersection between orbit plane and reference plane, on the 'upward' side
1362  * periapsis = closest point of orbit to target point
1363  * apoapsis = farthest point of orbit to target point
1364  * ecliptic = plane of orbit
1365  * reference direction = line on reference plane inline with periapsis
1366  *
1367  * primary orbital elements:
1368  * Y = reference direction (vector on reference plane that lines up with periapsis)
1369  * i = inclination to the positive ecliptic at node line
1370  * a = semi-major axis, or mean distance to target. this will adjust ship's orbit based on distance
1371  * e = eccentricity (0=circle, 0-1=ellipse, 1=parabola)
1372  * w = argument of periapsis, angle from the node line to the periapsis
1373  * N = longitude of the ascending node (from Y, ccw to node line)
1374  * NOTE: w + N = 360*
1375  * M = mean anomaly, radians between our current position and periapsis. increases uniformly with time from 0 to 2pi (360_deg)
1376  *
1377  * calculated orbital elements:
1378  * L = M + p = mean longitude, measure of how far around its orbit a body has progressed since passing the argument of periapsis (w)
1379  * P = orbital period, time in seconds to complete one orbit (assuming all other variables remain constant)
1380  * T = Epoch_of_M - (M(deg)/360_deg) / P = time of periapsis
1381  * v = true anomaly, position of the orbiting body along the orbit at a specific time, measured from w
1382  * E = eccentric anomaly, angle from target side of center point of line qQ, at which we are located
1383  *
1384  * Under ideal conditions of a perfectly spherical central body and zero perturbations,
1385  * all orbital elements except the mean anomaly (M) are constants.
1386  *
1387  * As we're using circular orbits, the reference direction (Y) will be +x
1388  *
1389  */
1390 
1391  GPoint Tp(m_targetEntity.second->GetPosition());
1392  //i = inclination to the positive ecliptic (plane of our orbit) at node line
1393  double adj = sqrt(pow(m_position.x - Tp.x, 2) * pow(m_position.z - Tp.z, 2));
1394  double opp = m_position.y - Tp.y;
1395  double i = atan2(opp, adj);
1396 
1397  /* not needed yet, but here just in case...
1398  // to determine orbiters position relative to target, use RA and dec (from their point of view, with vernal equinox being their heading)
1399  // calculating right ascension (RA)
1400  double A = cos(w) * cos(N) - sin(w) * sin(N) * cos(i);
1401  double B = cos(cos(w) * cos(N) + sin(w) * sin(N) * cos(i)) - sin(sin(w) * sin(i));
1402  double RA = atan2(B, A);
1403  // calculating declination (dec)
1404  double C = sin(cos(w) * cos(N) + sin(w) * sin(N) * cos(i)) + cos(sin(w) * sin(i));
1405  double dec = asin(C);
1406  */
1407 
1408  GPoint mPos(NULL_ORIGIN);
1409  // fig 8 on nw of targ sphere
1410  float radius = m_targetDistance + (m_radius *2); // fudge a bit as using targetDistance is a hair too close
1411  // angle around y axis (from +x) - horizontal movement - cw from +x using ships orbit in rad/tic
1412  float theta = m_orbitRadTic * 0/*timeStamp*/;
1413  // angle around xz axis (from 0) - vertical movement
1414  GVector target(m_position, Tp);
1415  LogMacro(target);
1416  float hyp = sqrt(pow(target.z, 2) + pow(target.x, 2));
1417  float inclination = 45; //atan(hyp / target.y);
1418  // fractional value of orbit period (0 < x < 1)
1419  float period = fmod(0/*timeStamp*/, m_orbitTime) /m_orbitTime;
1420  // calculate a pendulum value here to adjust elevation (+/-y) where +x is 1, 0x is 0, -x is -1
1421  float c = cos(EvE::Trig::Deg2Rad(360 * period));
1422  // get elevation modifier based on orbit period
1423  float phi = EvE::Trig::Deg2Rad(inclination * c);
1424  // set xz plane modifier from elevation
1425  //float s = sin(EvE::Trig::Deg2Rad(360 * period));
1426  //float mu = EvE::Trig::Deg2Rad(inclination * s);
1427  // here we will adjust orbit plane by adding OrbitRotation angle to theta
1428  // calculate position using trig.
1429  mPos.x = radius /* mu */* cos( theta );
1430  mPos.z = radius /* mu */* sin( theta );
1431  mPos.y = radius * phi;
1432 
1433  //_log(DESTINY__ORBIT_TRACE, "Destiny::ComputePosition() - a:%.5f, i:%.5f, v:%.5f, N:%.5f, M:%.5f, w:%.5f, e:%.5f, E:%f, P:%f", a,i,v,N,M,w,e,E,P);
1434  //_log(DESTINY__ORBIT_TRACE, "Destiny::ComputePosition() - mPos(%.3f, %.3f, %.3f) - radius check %.3f, q:%.3f", mPos.x, mPos.y, mPos.z, r,q);
1435 
1436  // position test
1437  if (mPos.isNaN()) {
1438  _log(DESTINY__ERROR, "mPos calculated as NaN. Stopping Orbit.");
1439  Stop();
1440  return NULL_ORIGIN;
1441  }
1442  // get new position as reference to target
1443  return mPos;
1444 }
1445 
1448  m_orbitTime = 0.0f;
1449  m_orbitRadTic = 0.0f;
1450  m_targetDistance = 0;
1451  m_followDistance = 0;
1452  m_maxOrbitSpeedFraction = 1.0f;
1453 }
1454 
1456  //init warp:
1457 
1458  // warp time and distance math
1459  // allan 1Nov14 - 14Nov14
1460  // rewrite 3jan15 to use distance instead of time for warping. more accurate now, and covers ALL distances.
1461  // calculation and implementation update 9Jan15 accuracy is within 1000m
1462 
1463  /* my research into warp formulas, i have used these sites, with a few excerpts and ideas from each...
1464  * https://wiki.eveonline.com/en/wiki/Acceleration
1465  * http://oldforums.eveonline.com/?a=topic&threadID=1251486
1466  * http://gaming.stackexchange.com/questions/115271/how-does-one-calculate-a-ships-agility
1467  * http://eve-search.com/thread/1514884-0
1468  * http://www.eve-search.com/thread/478431-0/page/1
1469  */
1470 
1471  /* this is my version of how warp should be timed and followed by the server.
1472  * checks here for distance < warp speed and distance < 2AU, (with all distances in meters)
1473  * and adjusts accel/decel times accordingly
1474  *
1475  * accel/decel are logarithmic per ccp (see above).
1476  *
1477  * times are as follows, per this table. http://cdn1.eveonline.com/www/newssystem/media/65418/1/numbers_table.png
1478  *
1479  * all warps are in same time groups for all ships, except freighters and caps.
1480  * distance checks are separated into 4 time groups, with subgroups for freighters and caps.
1481  *
1482  * the client seems to accept and agree with the math here.
1483  */
1484  // reset turn variables for warping
1485  if (m_turning)
1486  ClearTurn();
1487 
1488  if (is_log_enabled(DESTINY__WARP_TRACE))
1489  _log(DESTINY__WARP_TRACE, "Destiny::InitWarp(): %s(%u) has initialized warp.", mySE->GetName(), mySE->GetID());
1490 
1491  double warpSpeedInMeters(m_shipWarpSpeed * ONE_AU_IN_METERS);
1492 
1493  /* this is from http://community.eveonline.com/news/dev-blogs/warp-drive-active/
1494  * x = e^(k*t)
1495  * v = k*e^(k*t)
1496  *
1497  * x = distance in meters
1498  * t = time in seconds
1499  * v = speed in m/s
1500  * k = 3 for accel, 1 for decel
1501  *
1502  * this gives distances as functions of time.
1503  * the client seems to agree with this reasoning, and follows the same idea.
1504  */
1505 
1506  bool cruise(true);
1507  float cruiseTime(0.0f);
1508  double accelDistance(0.0), decelDistance(0.0), cruiseDistance(0.0);
1509  // fudge this a bit for accel/decel distances
1510  if (m_targetDistance < warpSpeedInMeters) {
1511  // short warp....no cruise
1512  // this isnt very accurate....times and distances are a bit off....
1513  cruise = false;
1514  // accel = 1/3 decel
1515  accelDistance = (m_targetDistance /3);
1516  decelDistance = (m_targetDistance - accelDistance);
1517  warpSpeedInMeters = accelDistance;
1518  m_warpDecelTime = log(decelDistance /3);
1519  m_warpAccelTime = log(accelDistance /3) /3;
1520  } else {
1521  // all ships base time is 29s for distances > ship warp speed
1522  m_warpAccelTime = 7;
1523  m_warpDecelTime = 21; // accel *3
1524  decelDistance = exp(m_warpDecelTime); // ship warp speed in meters * 1.7
1525  accelDistance = exp(3 * m_warpAccelTime); // ship warp speed in meters
1526  cruiseDistance = (m_targetDistance - accelDistance - decelDistance);
1527  cruiseTime = (cruiseDistance / warpSpeedInMeters);
1528  }
1529 
1530  // set total warp time based on above math.
1531  float warpTime(m_warpAccelTime + m_warpDecelTime + std::floor(cruiseTime));
1532 
1533  GVector warp_vector(m_position, m_targetPoint);
1534  warp_vector.normalize();
1535 
1536  if (is_log_enabled(DESTINY__WARP_TRACE)) {
1537  _log(DESTINY__WARP_TRACE, "Destiny::InitWarp():Calculate - %s(%u): Warp will accelerate for %us, cruise for %.3f, then decelerate for %us, with total time of %.3fs, and warp speed of %.4f m/s.", \
1538  mySE->GetName(), mySE->GetID(), m_warpAccelTime, cruiseTime, m_warpDecelTime, warpTime, warpSpeedInMeters);
1539  _log(DESTINY__WARP_TRACE, "Destiny::InitWarp():Calculate - %s(%u): Accel distance is %.4f. Cruise distance is %.4f. Decel distance is %.4f. Direction is %.3f,%.3f,%.3f.", \
1540  mySE->GetName(), mySE->GetID(), accelDistance, cruiseDistance, decelDistance, warp_vector.x, warp_vector.y, warp_vector.z);
1541  _log(DESTINY__WARP_TRACE, "Destiny::InitWarp():Calculate - %s(%u): We will exit warp at %.2f,%.2f,%.2f at a distance of %.4f AU (%um).", \
1543  GPoint destination = m_position + (warp_vector * m_targetDistance);
1544  _log(DESTINY__WARP_TRACE, "Destiny::InitWarp():Calculate - %s(%u): calculated exit is %.2f,%.2f,%.2f and vector is %.4f,%.4f,%.4f.", \
1545  mySE->GetName(), mySE->GetID(), destination.x, destination.y, destination.z, warp_vector.x, warp_vector.y, warp_vector.z);
1546  GVector diff(m_targetPoint, destination);
1547  _log(DESTINY__WARP_TRACE, "Destiny::InitWarp():Calculate - target vs calculated is %.2fm.", diff.length());
1548  }
1549  // reset deceltime (from duration to time) for time check in WarpDecel()
1550  m_warpDecelTime = m_warpAccelTime + floor(cruiseTime);
1551  m_stateStamp = sEntityList.GetStamp();
1552 
1554  m_warpState = new WarpState(
1555  m_stateStamp,
1557  warpSpeedInMeters,
1558  accelDistance,
1559  cruiseDistance,
1560  decelDistance,
1561  warpTime,
1562  true,
1563  false,
1564  false,
1565  warp_vector );
1566 
1567  //drain cap
1568  if (mySE->HasPilot()) {
1570  mySE->GetShipSE()->Warp();
1571  m_capNeeded = 0;
1572  }
1573 
1574  //clear targets
1576  //mySE->TargetMgr()->OnTarget(nullptr, TargMgr::Mode::Clear, TargMgr::Msg::WarpingOut);
1577 
1578  WarpAccel(0);
1579 }
1580 
1581 void DestinyManager::WarpAccel(uint16 sec_into_warp) {
1582  /* For acceleration, k = 3.
1583  * distance = e^(k*s)
1584  * speed = k*e^(k*s)
1585  */
1586  double currentDistance = exp(3 * sec_into_warp);
1587 
1588  if (m_inBubble)
1589  if (currentDistance > BUBBLE_RADIUS_METERS)
1590  if (mySE->SysBubble() != m_targBubble) {
1591  if (is_log_enabled(DESTINY__WARP_TRACE))
1592  _log(DESTINY__WARP_TRACE, "Destiny::WarpAccel(): %s(%u) is being removed from bubble %u.",\
1593  mySE->GetName(), mySE->GetID(), mySE->SysBubble()->GetID());
1594  mySE->SysBubble()->Remove(mySE);
1595  m_inBubble = false;
1596  }
1597 
1598  if (currentDistance > m_warpState->accelDist) {
1599  currentDistance = m_warpState->accelDist;
1600  m_warpState->accel = false;
1601  if (m_warpState->cruiseDist > 0) {
1602  m_warpState->cruise = true;
1603  } else {
1604  m_warpState->decel = true;
1605  }
1606  }
1607 
1608  m_targetDistance -= currentDistance;
1609  double currentShipSpeed = (3 * currentDistance);
1610 
1611  if (m_warpState->accel)
1612  if (is_log_enabled(DESTINY__WARP_TRACE))
1613  _log(DESTINY__WARP_TRACE, "Destiny::WarpAccel(): %s(%u) - Warp Accelerating(%us): velocity %.4f m/s with %u m left to go. Current distance %.4f from origin.", \
1614  mySE->GetName(), mySE->GetID(), sec_into_warp, currentShipSpeed, m_targetDistance, currentDistance);
1615 
1616  WarpUpdate(currentShipSpeed);
1617 }
1618 
1619 void DestinyManager::WarpCruise(uint16 sec_into_warp) {
1620  /* in cruise....calculate distance only to update internal position data. */
1622 
1624  m_warpState->cruise = false;
1625  m_warpState->decel = true;
1626  }
1627 
1628  if (is_log_enabled(DESTINY__WARP_TRACE))
1629  _log(DESTINY__WARP_TRACE, "Destiny::WarpCruise(): %s(%u) - Warp Crusing(%us): velocity %.4f m/s. with %u m left to go.", \
1630  mySE->GetName(), mySE->GetID(), sec_into_warp, m_warpState->warpSpeed, m_targetDistance);
1631 
1633 }
1634 
1635 void DestinyManager::WarpDecel(uint16 sec_into_warp) {
1636  /* For deceleration, k = -1.
1637  * distance = e^(k*s)
1638  * speed = -k*e^(k*s)
1639  */
1640  uint8 decelTime = (sec_into_warp - m_warpDecelTime);
1641  double currentDistance = (m_warpState->total_distance - (exp(-decelTime) * m_warpState->decelDist));
1642  m_targetDistance = (int32)(m_warpState->total_distance - currentDistance);
1643  double currentShipSpeed = (m_warpState->warpSpeed * exp(-decelTime));
1644 
1645  if (is_log_enabled(DESTINY__WARP_TRACE))
1646  _log(DESTINY__WARP_TRACE, "Destiny::WarpDecel(): %s(%u) - Warp Decelerating(%us/%us): velocity %.4f m/s with %u m left to go.", \
1647  mySE->GetName(), mySE->GetID(), decelTime, sec_into_warp, currentShipSpeed, m_targetDistance);
1648 
1649  WarpUpdate(currentShipSpeed);
1650  if (currentShipSpeed <= m_speedToLeaveWarp)
1651  WarpStop(currentShipSpeed);
1652 }
1653 
1654 void DestinyManager::WarpUpdate(double currentShipSpeed) {
1655  // update position and velocity for all stages.
1656  // this method is ~1000m off actual. could be due to rounding. -allan 9Jan15
1657  m_velocity = (m_warpState->warp_vector * currentShipSpeed);
1659 
1660  if (m_warpState->decel) {
1661  if (!m_inBubble) {
1662  if (is_log_enabled(DESTINY__WARP_TRACE))
1663  _log(DESTINY__WARP_TRACE, "Destiny::WarpUpdate() %s(%u): Ship is %f from center of target bubble %u.",\
1665  if (m_targBubble->InBubble(m_position, true)) {
1666  if (is_log_enabled(DESTINY__WARP_TRACE))
1667  _log(DESTINY__WARP_TRACE, "Destiny::WarpUpdate() %s(%u): Ship at %.2f,%.2f,%.2f is calling Add() for bubble %u.", \
1669  m_targBubble->Add(mySE);
1670  SetPosition(m_position, true);
1671  m_inBubble = true;
1672  }
1673  }
1674  }
1675 }
1676 
1677 void DestinyManager::WarpStop(double currentShipSpeed) {
1678  if (is_log_enabled(DESTINY__WARP_TRACE)) {
1679  _log(DESTINY__WARP_TRACE, "Destiny::WarpStop(): %s(%u) - Warp complete. Exit velocity %.4f m/s with %u m left to go.", \
1680  mySE->GetName(), mySE->GetID(), currentShipSpeed, m_targetDistance);
1681  _log(DESTINY__WARP_TRACE, "Destiny::WarpStop(): %s(%u): Ship currently at %.2f,%.2f,%.2f.", \
1683  }
1684  if (mySE->IsShipSE())
1685  _log(AUTOPILOT__MESSAGE, "Destiny::WarpStop(): %s(%u) - Warp complete.", mySE->GetName(), mySE->GetID());
1686  m_targetPoint += (m_warpState->warp_vector *10000);
1687  // SetSpeedFraction() checks for m_state = Warp and warpstate != null to set decel variables correctly with warp decel.
1688  // have to call this BEFORE deleting or reseting m_state or WarpState.
1689  SetSpeedFraction(0.0f);
1690  m_stop = true;
1692  m_targBubble = nullptr;
1693  if ((mySE->IsNPCSE()) and (mySE->GetNPCSE()->GetAIMgr() != nullptr))
1695 }
1696 
1697 //called whenever an entity is going away and can no longer be used as a target
1699  if (m_targetEntity.second == pSE) {
1700  m_targetEntity.first = 0;
1701  m_targetEntity.second = nullptr;
1702 
1703  switch(m_ballMode) {
1706  _log(DESTINY__DEBUG, "%u: Our target entity has gone away. Stopping.", mySE->GetID());
1707  Stop();
1708  } break;
1709  }
1710  }
1711 }
1712 
1714 {
1716  if (mySE->SystemMgr()->GetSE(m_targetEntity.first) == nullptr) {
1717  // Our target was removed
1718  Stop();
1719  return true;
1720  }
1721  if (!m_targetEntity.second->IsDynamicEntity())
1722  return false;
1723  if (m_targetEntity.second->HasPilot()) {
1724  if (m_targetEntity.second->GetPilot()->IsDocked()) { // Our target docked, so STOP
1725  //mySE->TargetMgr()->ClearTarget(m_targetEntity.second);
1726  Stop();
1727  return true;
1728  }
1729  }
1730  if (m_targetEntity.second->DestinyMgr()->IsWarping()) { // The target is warping
1731  //mySE->TargetMgr()->ClearTarget(m_targetEntity.second);
1732  Stop();
1733  return true;
1734  }
1735  return false;
1736 }
1737 
1738 // Basic Movement Calls:
1740  // common movement for all types
1741  if (!m_hasSentShipUpdates) {
1742  // error fix for setting ship movement variables before ship is in bubble (cannot BubbleCast)
1743  std::vector<PyTuple*> updates;
1744  SetBallAgility sbagility;
1745  sbagility.entityID = mySE->GetID();
1746  sbagility.agility = m_shipInertia;
1747  updates.push_back(sbagility.Encode());
1748  SetBallMassive sbmassive;
1749  sbmassive.entityID = mySE->GetID();
1750  sbmassive.is_massive = false; // disable client-side bump checks
1751  updates.push_back(sbmassive.Encode());
1752  SetBallMass sbmass;
1753  sbmass.entityID = mySE->GetID();
1754  sbmass.mass = m_mass;
1755  updates.push_back(sbmass.Encode());
1756  SendDestinyUpdate(updates); //consumed
1757  m_hasSentShipUpdates = true;
1758  }
1759 
1760  // reset turn and movement checks for possible velocity change.
1761  m_turnTic = 0;
1762  m_stop = m_accel = m_decel = m_turning = false;
1763 
1764  //reset max accel time in case it was changed previously
1765  m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
1766 
1767  if (!mySE->IsNPCSE() or (mySE->IsNPCSE() and mySE->GetNPCSE()->GetAIMgr()->IsIdle())) {
1769  m_stateStamp = sEntityList.GetStamp();
1770  }
1771 
1772  // do any of these next 3 need to be on every movement check??
1773  if (m_position.isNaN()) {
1774  _log(DESTINY__ERROR, "%s position is NaN.", mySE->GetName());
1775  }
1776  if (m_position.isZero()) {
1777  _log(DESTINY__ERROR, "%s position is zero.", mySE->GetName());
1778  }
1779  if (m_position.isInf()) {
1780  _log(DESTINY__ERROR, "%s position is inf.", mySE->GetName());
1781  }
1782 
1783  if (m_targetPoint.isNotZero()) {
1784  GVector targHeading(m_position, m_targetPoint);
1785  targHeading.normalize();
1786  m_targetHeading = targHeading;
1787  if (m_shipHeading.isZero())
1788  m_shipHeading = targHeading;
1789  }
1790 
1792  GVector point(m_position);
1793  point.normalize();
1794  m_targetPoint = (point * 1.0e16);
1795  GVector targHeading(m_position, m_targetPoint);
1796  targHeading.normalize();
1797  m_targetHeading = m_shipHeading = targHeading;
1798  }
1799 
1801  // reset target distance just in case it changed.
1802  GVector shipVector(m_position, m_targetPoint);
1803  m_targetDistance = (uint32)shipVector.length();
1804  m_orbitRadTic = 0.0f;
1806  }
1807 
1808  // this will have to be adjusted for cloak mod.
1809  if (IsCloaked())
1810  UnCloak();
1811 
1812  // if ship is not moving, set initial movement variables
1813  if (m_userSpeedFraction < 0.05) {
1814  SetSpeedFraction(1.0f, true);
1815  //MoveObject();
1816  } else {
1817  // reset m_moveTime for current ship speed vs time to allow correct movement calculations after velocity change
1818  double newTime = (-log(1 - m_currentSpeedFraction) * m_shipAgility);
1819  m_moveTime = (GetTimeMSeconds() - (newTime * 1000));
1820  // TODO: verify m_moveTime is being set properly here.
1822  // dont call MoveObject() here, as changes wont take affect till next tic.
1823  }
1824 
1825  SetPosition(m_position, sConfig.debug.PositionHack); // (PositionHack == true) here will force position update to client
1826  MoveObject();
1827 }
1828 
1830  //called from client as 'CmdFollowBall'
1831  // also used by 'Approach'
1833  and (m_targetEntity.second == pSE)
1834  and (m_followDistance == distance)
1835  and (m_userSpeedFraction))
1836  return;
1837 
1838  //reset orbit vars in case we were orbiting before
1839  if (m_orbiting)
1840  ClearOrbit();
1841 
1843  m_targetPoint = pSE->GetPosition();
1844 
1845  if (pSE->IsStationSE()) {
1846  // this makes ship approach station dock elevation (y), instead of approaching to stations "center point" position (where icon is)
1847  m_targetPoint.y = stDataMgr.GetDockPosY(pSE->GetID());
1848  }
1849 
1850  m_targetEntity.first = pSE->GetID();
1851  m_targetEntity.second = pSE;
1852  m_followDistance = distance;
1853  BeginMovement();
1854 
1855  CmdFollowBall du;
1856  du.entityID = mySE->GetID();
1857  du.targetID = pSE->GetID();
1858  du.range = (int32)distance;
1859  PyTuple *up = du.Encode();
1860  SendSingleDestinyUpdate(&up); // consumed
1861 }
1862 
1864  Follow(ent, 0);
1865 }
1866 
1867 void DestinyManager::GotoDirection(const GPoint& direction) {
1868  //reset orbit vars in case we were orbiting before
1869  if (m_orbiting)
1870  ClearOrbit();
1871 
1873  m_targetPoint = direction *1.0e16;
1874  BeginMovement();
1875 
1876  CmdGotoDirection du;
1877  du.entityID = mySE->GetID();
1878  du.x = direction.x;
1879  du.y = direction.y;
1880  du.z = direction.z;
1881  PyTuple* up = du.Encode();
1882  SendSingleDestinyUpdate(&up); // consumed
1883 }
1884 
1885 void DestinyManager::GotoPoint(const GPoint& point) {
1886  //reset orbit vars in case we were orbiting before
1887  if (m_orbiting)
1888  ClearOrbit();
1889 
1891  m_targetPoint = point;
1892  BeginMovement();
1893 
1894  CmdGotoPoint gtpoint;
1895  gtpoint.entityID = mySE->GetID();
1896  gtpoint.x = m_targetPoint.x;
1897  gtpoint.y = m_targetPoint.y;
1898  gtpoint.z = m_targetPoint.z;
1899  PyTuple* up = gtpoint.Encode();
1900  SendSingleDestinyUpdate(&up); // consumed
1901 }
1902 
1903 void DestinyManager::WarpTo(const GPoint& where, int32 distance/*0*/, bool autoPilot/*false*/, SystemEntity* pSE/*nullptr*/) {
1904  /* warp order..
1905  * pick destination -> align/accel -> aura "warp drive active" -> cap drain -> accel
1906  * -> enter warp -> warp -> decel -> leave warp -> coast -> stop
1907  */
1909 
1910  // check for autopilot. it has 'special' checks in client for auto-disable by destiny update
1911  if (autoPilot) {
1912  Follow(pSE, distance);
1913  } else {
1914  GotoPoint(where);
1915  }
1916 
1917  m_targetEntity.first = 0;
1918  m_targetEntity.second = nullptr;
1919 
1920  m_stopDistance = distance;
1921  // get warp target point
1922  GVector warp_distance(m_position, where);
1923  m_targetDistance = warp_distance.length();
1925  // change to heading
1926  warp_distance.normalize();
1927  // adjust for stop distance from our travel direction
1928  warp_distance *= m_stopDistance;
1929  // adjust target point by calculated stopping point
1930  m_targetPoint -= warp_distance;
1931 
1933  if (is_log_enabled(DESTINY__WARP_TRACE))
1934  _log(DESTINY__TRACE, "Destiny::WarpTo() - %s(%u) target bubble: %u m_stopDistance: %i m_targetDistance: %u",
1936 
1937  // npcs have no warp restrictions (yet)
1938  if (mySE->IsNPCSE() or mySE->IsDroneSE()) {
1939  // do drones warp??
1941 
1942  std::vector<PyTuple*> updates;
1943  CmdWarpTo wt;
1944  wt.entityID = mySE->GetID();
1945  wt.dest_x = m_targetPoint.x;
1946  wt.dest_y = m_targetPoint.y;
1947  wt.dest_z = m_targetPoint.z;
1948  wt.distance = m_stopDistance;
1949  wt.warpSpeed = GetWarpSpeed();
1950  updates.push_back(wt.Encode());
1951  OnSpecialFX10 sfx;
1952  sfx.guid = "effects.Warping";
1953  sfx.entityID = mySE->GetID();
1954  sfx.isOffensive = false;
1955  sfx.start = true;
1956  sfx.active = true;
1957  updates.push_back(sfx.Encode());
1958  SendDestinyUpdate(updates);
1959  if (is_log_enabled(NPC__MESSAGE))
1960  _log(NPC__MESSAGE, "Destiny::WarpTo() NPC %s(%u) to:%u from:%u, m_targetPoint: %.2f,%.2f,%.2f m_stopDistance: %i m_targetDistance: %u",\
1963  return;
1964  }
1965 
1966  /*supercap warp modifiers
1967  * these will go here, and modify distance, target, and range accordingly
1968  *
1969  * AttrWarpAccuracyMaxRange = 1021,
1970  * AttrWarpAccuracyFactor = 1022,
1971  * AttrWarpAccuracyFactorMultiplier = 1023,
1972  * AttrWarpAccuracyMaxRangeMultiplier = 1024,
1973  * AttrWarpAccuracyFactorPercentage = 1025,
1974  * AttrWarpAccuracyMaxRangePercentage = 1026,
1975  */
1976 
1977  if (mySE->HasPilot()) {
1979  mySE->GetPilot()->SendErrorMsg("That is too close for your Warp Drive.");
1980  // warp distance too close. cancel warp and return
1981  // we may need to send pos update
1982  if (sConfig.debug.PositionHack)
1983  SetPosition(mySE->GetPosition(), true);
1986  return;
1987  }
1988 
1989  Client *pClient = mySE->GetPilot();
1990 
1991  /* capacitor for warp formulas from https://oldforums.eveonline.com/?a=topic&threadID=332116
1992  * Energy to warp = warpCapacitorNeed * mass * au * (1 - warp_drive_operation_skill_level * 0.10)
1993  */
1994  float currentShipCap = pClient->GetShip()->GetAttribute(AttrCapacitorCharge).get_float();
1995  float capNeeded = m_mass * m_warpCapacitorNeed * (m_targetDistance / ONE_AU_IN_METERS);
1996  capNeeded *= (1.0f - (0.1f *pClient->GetChar()->GetSkillLevel(EvESkill::WarpDriveOperation)));
1997 
1998  _log(DESTINY__WARNING, "Warp Cap need for %s(%u) is %.4f", mySE->GetName(), mySE->GetID(), capNeeded);
1999 
2000  // check if ship has enough capacitor to warp full distance
2001  if (capNeeded > currentShipCap) {
2002  // not enough cap. reset everything based on available cap
2003  capNeeded = (currentShipCap /m_warpCapacitorNeed) /m_mass;
2004  if (capNeeded > 1) {
2005  m_targetDistance = (uint32)capNeeded * ONE_AU_IN_METERS;
2006  GVector warp_direction(m_position, where);
2007  GPoint newTarget(m_position + (warp_direction *m_targetDistance));
2008 
2009  m_targBubble = sBubbleMgr.GetBubble(mySE->SystemMgr(), newTarget);
2010  if (is_log_enabled(DESTINY__WARP_TRACE))
2011  _log(DESTINY__TRACE, "Destiny::WarpTo():Update - %s(%u) target bubble: %u m_stopDistance: %i m_targetDistance: %u",
2013  } else {
2014  // if not enough cap to do min warp, cancel and return
2015  pClient->SendErrorMsg("You don't have enough capacitor charge to warp.");
2016  _log(DESTINY__WARNING, "Destiny::WarpTo() - %s(%u): Capacitor needed vs current %.3f / %.3f",
2017  mySE->GetName(), mySE->GetID(), capNeeded, currentShipCap);
2018 
2020  m_targBubble = nullptr;
2022  return;
2023  }
2024  } else {
2025  capNeeded = currentShipCap - capNeeded;
2026  }
2027 
2028  m_capNeeded = capNeeded;
2029  }
2030 
2031  /* TODO PUT CHECK HERE FOR WARP BUBBLES
2032  * and other things that affect warp-in point.....when we get to there.
2033  * AttrWarpBubbleImmune = 1538,
2034  * AttrWarpBubbleImmuneModifier = 1539,
2035  * NOTE: warp bubble in path (or within 100km of m_targetPoint) will change m_targetDistance and m_targetPoint
2036  * however, this does NOT affect original calculations for energy needed, etc...
2037  */
2038  if (m_targBubble->HasWarpBubble()) {
2040  ; // not immune to bubble
2041  }
2042 
2044 
2045  // send client updates
2046  std::vector<PyTuple*> updates;
2047  // acknowledge client's warpto request
2048  CmdWarpTo wt;
2049  wt.entityID = mySE->GetID();
2050  wt.dest_x = m_targetPoint.x;
2051  wt.dest_y = m_targetPoint.y;
2052  wt.dest_z = m_targetPoint.z;
2053  wt.distance = m_stopDistance;
2054  wt.warpSpeed = GetWarpSpeed(); // warp speed x10
2055  updates.push_back(wt.Encode());
2056  //send a warp effect...
2057  OnSpecialFX10 sfx;
2058  sfx.guid = "effects.Warping";
2059  sfx.entityID = mySE->GetID();
2060  sfx.isOffensive = false;
2061  sfx.start = true;
2062  sfx.active = true;
2063  updates.push_back(sfx.Encode());
2064  SendDestinyUpdate(updates);
2065  updates.clear();
2066  //set massive for warp, per client, but self-only
2067  SetBallMassive bm;
2068  bm.entityID = mySE->GetID();
2069  bm.is_massive = false; // disable client-side bump checks
2070  PyTuple *up = bm.Encode();
2071  SendSingleDestinyUpdate(&up, true); // consumed
2072 
2073  if (is_log_enabled(DESTINY__WARP_TRACE))
2074  _log(DESTINY__WARP_TRACE, "Destiny::WarpTo() toBubble:%u from:%u, m_targetPoint: %.2f,%.2f,%.2f m_stopDistance: %i m_targetDistance: %u",
2076 }
2077 
2078 void DestinyManager::Orbit(SystemEntity *pSE, uint32 distance/*0*/) {
2080  and (m_targetEntity.second == pSE)
2081  and (m_targetDistance == distance))
2082  return;
2083 
2084  if (m_orbiting)
2086 
2087  /* this initial Orbit() call will, based on position data, determine the orbit plane, rotation (cw/ccw)
2088  * initial heading, actual orbit radius, actual orbit velocity, and some other shit i havent thought about yet.
2089  *
2090  * m_targetPoint - updated in Orbit()
2091  * m_shipHeading - updated in Orbit()
2092  * m_targetEntity - SE object to orbit
2093  * m_targetDistance - commanded orbit distance
2094  * m_followDistance - calculated orbit distance based on mass, velocity, gravity, and other ship variables
2095  * m_stateStamp (via BeginMovement())
2096  * speed fractions (usf, csf, asf - via SetSpeedFraction() to begin or alter speed)
2097  * m_maxOrbitSpeedFraction - calculated max speed to maintain commanded orbit distance. set in Orbit()
2098  */
2101  m_targetEntity.first = pSE->GetID();
2102  m_targetEntity.second = pSE;
2103  m_targetPoint = pSE->GetPosition();
2104  m_targetDistance = distance;
2105  BeginMovement();
2106 
2107  if (is_log_enabled(DESTINY__ORBIT_TRACE))
2108  _log(DESTINY__ORBIT_TRACE, "%s(%u) - Ship Data - agility:%.3f, inertia:%.3f, massMkg:%.3f, maxSpeed:%.2f, radius:%.2f", \
2110  //EvE::traceStack();
2111 
2112  // Target (orbited object)
2113  double Tr = pSE->GetRadius();
2114  double Tm = pSE->GetSelf()->GetAttribute(AttrMass).get_float();
2115  if (Tm != 0.0)
2116  Tm = pSE->GetSelf()->type().mass();
2117 
2118  if (is_log_enabled(DESTINY__ORBIT_TRACE))
2119  _log(DESTINY__ORBIT_TRACE, "%s(%u) - Target Data - mass:%.3f, speed:%.2f, radius:%.2f", \
2120  mySE->GetName(), mySE->GetID(), Tm, (pSE->DestinyMgr() ? pSE->DestinyMgr()->GetSpeed() : 0 ), Tr);
2121 
2122  // fudge distance to work 'close enough' with all targets...this was trial-n-error
2123  double Rc = ((distance + 150 + m_radius - (pSE->GetRadius() /12)) * 1.2);
2124  double Rc2 = std::pow(Rc,2);
2125  double Vm2 = std::pow(m_maxShipSpeed,2);
2126  double t2 = std::pow(m_shipAgility,2);
2127 
2128  // the following equation is from "Ship Motion in Eve Online" by Scheulagh Santorine, Ph.D
2129  // radius needs target mass and grav const factored in....somehow.
2130  // orbit radius
2131  /* r = sqrt(6 * cbrt(108t^2*Vm^2 * Rc^2 + 8Rc^6 + 12sqrt(81t^4 *Vm^4 + 12t^2 * Vm^2 * Rc^10))
2132  * + (24Rc^4 / (108t^2 * Vm^2 * Rc^2 + 8Rc^2 + 12sqrt(81t^4 * Vm^4 * Rc^8 + 12t^2 * Vm^2 * Rc^10)^1/3)) + 12Rc^2) /6
2133  */
2134  double one = (108 * t2 * Vm2 * Rc2);
2135  double two = (12 * t2 * Vm2 * std::pow(Rc,10));
2136  double three = (12 * std::sqrt(81 * std::pow(m_shipAgility,4) * std::pow(m_maxShipSpeed,4) + two));
2137  double four = (6 * std::cbrt(one + 8 * std::pow(Rc,6) + three));
2138  double five = std::cbrt( std::sqrt(three * std::pow(Rc,8) + two));
2139  double six = (one + (8 * Rc2) + (12 * five));
2140  m_followDistance = std::sqrt(four + (24 * std::pow(Rc, 4) / six) + 12 * Rc2) / 6;
2141 
2142  double velocity = m_maxShipSpeed * ((distance / m_followDistance) + 0.065); // dunno where i got this from but seems to work very well.
2144 
2145  double circ = EvE::Trig::Pi2 * m_followDistance;
2146  m_orbitTime = circ / velocity;
2148 
2149  if (is_log_enabled(DESTINY__ORBIT_TRACE))
2150  _log(DESTINY__ORBIT_TRACE, "%s(%u) - Orbit Data - Rc:%.3f, velocity:%.2f, osf:%.2f, targetDistance:%u, followDistance:%u, orbitTime:%.1f, radTic:%.5f", \
2151  mySE->GetName(), mySE->GetID(), Rc, velocity, m_maxOrbitSpeedFraction, \
2153 /* dont really need this here yet.....maybe not at all.
2154  double current = m_position.distance(pSE->GetPosition());
2155  double actual = (current - m_radius - Tr);
2156  // m_orbiting: -2=way too close -1=too close, 0=no orbit, 1=at distance 2=too far, 3=way too far
2157  if ((actual - m_followDistance) > m_followDistance) {
2158  // too far to engage target.
2159  m_orbiting = 3;
2160  } else if (current > m_followDistance) {
2161  // too far outside orbit. move closer
2162  m_orbiting = 2;
2163  } else if (actual < m_followDistance) {
2164  // way too close inside orbit. move away quickly.
2165  m_orbiting = -2;
2166  } else if (current < m_followDistance) {
2167  // too close inside orbit; move away slowly.
2168  m_orbiting = -1;
2169  } else {
2170  // within orbit distance tolerance
2171  m_orbiting = 1;
2172  }
2173 
2174  if (m_orbiting > 1) {
2175  // outside target distance. set orbit parameters based on current position.
2176 
2177  }
2178 */
2179  if (m_followDistance == 0) {
2180  _log(DESTINY__ERROR, "%s(%u) - FollowDistance is 0.", mySE->GetName(), mySE->GetID());
2181  m_followDistance = (uint32)(m_targetDistance + Tr + m_radius); // fudge something here. will have to fix later, but this is close enough
2182  }
2183 
2184  CmdOrbit du;
2185  du.entityID = mySE->GetID();
2186  du.orbitEntityID = pSE->GetID();
2187  du.distance = (int32)m_targetDistance;
2188  PyTuple *up = du.Encode();
2189  SendSingleDestinyUpdate(&up); // consumed
2190 }
2191 
2193 {
2194  if (m_shipHeading.isZero()) {
2195  GVector moveVector(m_position, targetPoint);
2196  moveVector.normalize();
2197  m_shipHeading = moveVector;
2198  }
2199  GVector toVec(m_position, targetPoint);
2200  toVec.normalize();
2201  float dot = toVec.dotProduct(m_shipHeading);
2202  float degrees = EvE::Trig::Rad2Deg(std::acos(dot));
2203  if (degrees < TURN_ALIGNMENT)
2204  return true;
2205  return false;
2206 }
2207 
2209  //set movement direction
2210  m_targetPoint = dir *1.0e16;
2211  m_shipHeading = GVector(dir);
2212  SetUndockSpeed();
2213  if (mySE->IsShipSE())
2214  mySE->GetShipSE()->GetShipItemRef()->SetUndocking(false);
2215 }
2216 
2218  //start ship movement @ max velocity for undocking.
2219  // this simulates being forcefully "ejected" from station (and is currently ~500m off)
2220  m_stop = false;
2221  m_orbiting = 0;
2222  m_stateStamp = sEntityList.GetStamp();
2223  //m_moveTime = GetTimeMSeconds();
2224  m_changeDelay = true; // skip a single tic before making change
2225  m_shipMaxAccelTime = 0.5f;
2226  m_prevSpeedFraction = 0.0f;
2227  m_userSpeedFraction = 1.1f;
2230  // may need to tweak these for larger ships...
2231  m_activeSpeedFraction = 1.1f;
2232  m_currentSpeedFraction = 1.1f;
2233 
2235  return;
2236 
2238  std::vector<PyTuple*> updates;
2239  SetBallVelocity bv;
2240  bv.entityID = mySE->GetID();
2241  bv.x = m_velocity.x;
2242  bv.y = m_velocity.y;
2243  bv.z = m_velocity.z;
2244  updates.push_back(bv.Encode());
2245  CmdGotoDirection du;
2246  du.entityID = mySE->GetID();
2247  du.x = m_shipHeading.x;
2248  du.y = m_shipHeading.y;
2249  du.z = m_shipHeading.z;
2250  updates.push_back(du.Encode());
2251  SendDestinyUpdate(updates);
2252 }
2253 
2255  Client *pClient = mySE->GetPilot();
2256  uint32 stationID = pClient->GetDockStationID();
2257  SystemEntity *station = mySE->SystemMgr()->GetSE(stationID);
2258 
2259  if (station == nullptr) {
2260  codelog(CLIENT__ERROR, "%s: Station %u not found.", pClient->GetName(), stationID);
2261  pClient->SendErrorMsg("Station Not Found, Docking Aborted.");
2262  return PyStatic.NewNone();
2263  }
2264 
2265  //get the station Docking Perimiter
2266  const GPoint stationPos = station->GetPosition();
2267  double rangeToStationPerimiter = m_position.distance(stationPos);
2268  rangeToStationPerimiter -= mySE->GetRadius();
2269  rangeToStationPerimiter -= station->GetRadius();
2270 
2271  // Verify range to station is within docking perimeter of 2500 meters:
2272  _log(DESTINY__TRACE, "Destiny::AttemptDockOperation() rangeToStationPerimiter is %.2fm", rangeToStationPerimiter);
2273  if (rangeToStationPerimiter > 2500.0) {
2274  AlignTo( station ); // Turn ship and move toward docking point - client will usually call Dock() automatically...sometimes
2275  if (mySE->HasPilot() and mySE->GetPilot()->CanThrow())
2276  throw UserError ("DockingApproach");
2277  }
2278 
2279  pClient->SetStateTimer(Player::State::Dock, sConfig.world.StationDockDelay *1000); // default @ 4sec();
2280  pClient->SetAutoPilot(false);
2281 
2282  return new PyLong(GetFileTimeNow());
2283 }
2284 
2286 {
2287  Stop();
2288  UnCloak();
2289  Client *pClient = mySE->GetPilot();
2290  if (pClient == nullptr)
2291  return;
2292 
2293  SystemEntity *pSE = mySE->SystemMgr()->GetSE(pClient->GetDockStationID());
2294  if (pSE == nullptr)
2295  return;
2296 
2297  const GPoint stationPos = pSE->GetPosition();
2298  OnDockingAccepted oda;
2299  oda.ship_x = m_position.x;
2300  oda.ship_y = m_position.y;
2301  oda.ship_z = m_position.z;
2302  oda.station_x = stationPos.x;
2303  oda.station_y = stationPos.y;
2304  oda.station_z = stationPos.z;
2305  oda.stationID = pClient->GetDockStationID();
2306  PyTuple* ev = oda.Encode();
2307  pClient->SendNotification("OnDockingAccepted", "charid", &ev);
2308 }
2309 
2310 void DestinyManager::SetPosition(const GPoint &pt, bool update /*false*/) {
2311  _log(DESTINY__TRACE, "Destiny::SetPosition() called by %s(%u)", mySE->GetName(), mySE->GetID());
2312 
2313  if (pt.isZero()) {
2314  _log(DESTINY__TRACE, "Destiny::SetPosition() - %s(%u) point is zero", mySE->GetName(), mySE->GetID());
2315  EvE::traceStack();
2316  }
2317 
2318  m_position = pt;
2319 
2320  // this sets InventoryItemRef.m_position correctly, which is used for all position references
2322 
2323  //according to packet sniffs, this is only used for 'Structure' and 'Probe" items. 'update' is for syncing client position data with ours
2324  if (mySE->IsPOSSE() or mySE->IsProbeSE() or update) {
2325  SetBallPosition du;
2326  du.entityID = mySE->GetID();
2327  du.x = m_position.x;
2328  du.y = m_position.y;
2329  du.z = m_position.z;
2330  PyTuple* up = du.Encode();
2331  SendSingleDestinyUpdate(&up); // consumed
2332  }
2333 }
2334 
2335 // settings for ship, npc and missile max speeds
2336 void DestinyManager::SetMaxVelocity(float maxVelocity)
2337 {
2338  float maxSpeed = mySE->GetSelf()->GetAttribute(AttrMaxVelocity).get_float();
2339  /*
2340  if (mySE->IsMissileSE() or mySE->IsNPCSE())
2341  maxSpeed = mySE->GetSelf()->GetAttribute(AttrMaxVelocity).get_float();
2342  else if (mySE->IsShipSE() or mySE->IsDroneSE())
2343  maxSpeed = mySE->GetSelf()->GetAttribute(AttrMaxDirectionalVelocity).get_float(); // this is depreciated. used as an absolute max speed, accounting for ab/mwd
2344  else
2345  ; // make error here?
2346  */
2347  if (mySE->IsShipSE())
2348  if (is_log_enabled(DESTINY__TRACE))
2349  _log(DESTINY__TRACE, "Destiny::SetMaxVelocity() - Ship:%s(%u) Pilot:%s(%u) - AttrMaxDirectionalVelocity is %.1f", \
2352 
2353  if (maxVelocity > maxSpeed) {
2354  m_maxShipSpeed = maxSpeed;
2355  } else {
2356  m_maxShipSpeed = maxVelocity;
2357  }
2358 }
2359 
2360 void DestinyManager::SpeedBoost(bool deactivate/*false*/)
2361 {
2362  float timeStamp = (GetTimeMSeconds() - m_moveTime) /1000;
2363  m_currentSpeedFraction = (1 - exp(-timeStamp / m_shipAgility));
2364  m_prevSpeed = m_maxSpeed * m_currentSpeedFraction; //get current ship speed
2365 
2366  // prop mod state changed. reset ship movement variables and update current movement, if applicable
2368  m_massMKg = m_mass / 1000000; //changes mass from Kg to MillionKg (10^-6)
2370  m_alignTime = (-log(0.25) * m_shipAgility);
2371  m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
2372  m_degPerTic = (65.0f - m_shipAgility) /10; // this isnt right....
2374  float fracCheck = m_prevSpeed / m_maxShipSpeed;
2375 
2376  if (is_log_enabled(DESTINY__MOVE_TRACE)) {
2377  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost() - nMass: %.5f, nAg: %.5f, csf: %.2f, usf: %.2f, asf: %.3f", \
2378  m_mass, m_shipAgility, m_currentSpeedFraction, m_userSpeedFraction, m_activeSpeedFraction);
2379  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost() - pSpeed:%.2f, prevMaxSpeed:%.2f, newMaxShipSpeed:%.2f, fraction: %.3f", \
2380  m_prevSpeed, m_maxSpeed, m_maxShipSpeed, fracCheck);
2381  }
2382 
2383  // check current movement and reset variables using modified values
2384  // ship is currently....
2385  if (deactivate) {
2386  // ....deactivating prop mod
2387  // - use accel formula to determine decel time
2388  // t=IM(10^-6) * -ln(1-(v/V))
2390  // - set speed fractions and decel timers for new max speed
2392  // reset ship max speed using updated m_maxShipSpeed
2394  // reset csf
2395  m_currentSpeedFraction = 1 - m_activeSpeedFraction;
2396  // reset move timer
2398  if (is_log_enabled(DESTINY__MOVE_TRACE))
2399  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost()::Deactivate - decelTime: %.3f, deltaTime: %.3f, maxspeed: %.2f", \
2400  m_shipMaxAccelTime, deltaTime, m_maxSpeed);
2401  } else if ((m_userSpeedFraction < m_currentSpeedFraction) and (m_prevSpeedFraction)) {
2402  // ....moving and decelerating
2403  // - this hits when prop mod activated while ship is decel
2404  m_maxSpeed = m_maxShipSpeed * m_prevSpeedFraction; // reset ship max speed using updated m_maxShipSpeed
2405  m_activeSpeedFraction = fracCheck;
2406  m_currentSpeedFraction = m_prevSpeed / m_maxSpeed; //get updated csf
2407  if (is_log_enabled(DESTINY__MOVE_TRACE))
2408  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost()::(psf!=0&csf>usf) - decelerating. - csf: %.3f. asf: %.3f, accelTime: %.3f, newMaxSpeed:%.2f", \
2409  m_currentSpeedFraction, m_activeSpeedFraction, m_shipMaxAccelTime, m_maxSpeed);
2410  } else if (m_activeSpeedFraction) {
2411  // ....moving and not decelerating (this includes turning)
2412  m_activeSpeedFraction = fracCheck;
2413  m_currentSpeedFraction = fracCheck;
2414  // reset ship max speed using updated m_maxShipSpeed
2416  // adjust m_moveTime time to fit current speed onto new max speed range. (previous max < new max)
2417  float test = -log(1 - fracCheck) * m_shipAgility;
2418  m_moveTime = (GetTimeMSeconds() - ( test * 1000));
2419  if (is_log_enabled(DESTINY__MOVE_TRACE))
2420  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost()::(0<asf<=usf) - test: %.2f, asf: %.3f, csf: %.2f, check: %.3f, accelTime: %.3f, newMaxSpeed:%.2f", \
2421  test, m_activeSpeedFraction, m_currentSpeedFraction, fracCheck, m_shipMaxAccelTime, m_maxSpeed);
2422  } else {
2423  // ....sitting still
2424  // - do nothing
2425  if (m_userSpeedFraction) {
2426  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost()::(usf>asf=0) - sitting still.");
2427  } else {
2428  _log(DESTINY__MOVE_TRACE, "Destiny::SpeedBoost()::(usf=asf=0) - sitting still.");
2429  }
2430  }
2431 
2432  // for SpeedBoost (prop mods)
2433  std::vector<PyTuple*> updates;
2434  SetBallAgility sbagility;
2435  sbagility.entityID = mySE->GetID();
2436  sbagility.agility = m_shipInertia;
2437  updates.push_back(sbagility.Encode());
2438  SetBallMass sbmass;
2439  sbmass.entityID = mySE->GetID();
2440  sbmass.mass = m_mass;
2441  updates.push_back(sbmass.Encode());
2442  SetBallSpeed sbms;
2443  sbms.entityID = mySE->GetID();
2444  sbms.speed = m_maxShipSpeed;
2445  updates.push_back(sbms.Encode());
2446  SendDestinyUpdate(updates);
2447  m_hasSentShipUpdates = true; // just in case, as this is re-sent in BeginMovement() (called from Orbit())
2448 
2450 }
2451 
2452 void DestinyManager::WebbedMe(InventoryItemRef modRef, bool apply/*false*/)
2453 {
2454  if (apply) {
2455  m_maxShipSpeed *= (1 + (modRef->GetAttribute(AttrSpeedFactor).get_float() / 100.0f));
2456  } else {
2457  m_maxShipSpeed /= (1 + (modRef->GetAttribute(AttrSpeedFactor).get_float() / 100.0f));
2458  }
2460  std::vector<PyTuple*> updates;
2461  SetBallSpeed sbms;
2462  sbms.entityID = mySE->GetID();
2463  sbms.speed = m_maxShipSpeed;
2464  updates.push_back(sbms.Encode());
2465  SendDestinyUpdate(updates);
2466  m_hasSentShipUpdates = true; // just in case, as this is re-sent in BeginMovement()
2467 
2469 }
2470 
2471 // called from Client::CreateShipSE(), Client::ResetAfterPodded(), NPC::NPC(), Concord::Concord(), Drone::Drone(), DestinyManager::UpdateNewShip()
2473 {
2474  /*
2475 Frigates (incl. CovOps, Inty, AF) have an agility of 3.1
2476 Destroyers 3.5
2477 Industrials 1.0
2478 Cruisers 0.55 (Elite/Faction 0.65)
2479 Battlecruisers 1.1
2480 Battleships 0.155
2481 */
2482  /* this sets variables needed for correct movement math.
2483  * these attribs are set from ship item when shipSE created. DO NOT modify anything here
2484  * this is also called when fleet boosts are updated.
2485  */
2487  InventoryItemRef sRef = mySE->GetSelf();
2489  m_massMKg = m_mass / 1000000; //changes mass from Kg to milliKg (10^-6)
2490 
2491  // this will catch speeds/needs for all ships (player and npc), and is easier to do here.
2494  if (sRef->HasAttribute(AttrInetia))
2496  if (sRef->HasAttribute(AttrMaxVelocity))
2500 
2501  if (mySE->IsNPCSE() or mySE->IsDroneSE())
2503 
2504  /* per https://forums.eveonline.com/default.aspx?g=posts&m=3912843 post#103
2505  *
2506  * Ships will exit warp mode when their warping speed drops below
2507  * 75% of sub-warp max speed, or 100m/s, whichever is the lower.
2508  */
2510  if ((m_speedToLeaveWarp < 100) and (m_maxShipSpeed > 135)) // 75% of 135 is 101.25
2511  m_speedToLeaveWarp = 100;
2512 
2513  /* The product of Mass and the Inertia Modifier gives the ship's agility
2514  * Agility = Mass x Inertia Modifier
2515  * agility is an internal-use variable.
2516  */
2518  m_degPerTic = (65.0f - m_shipAgility) /10;
2519  // set a maximum acceleration time (based on ship variables)
2520  // this is no longer correct. Vmax/T is the correct formula
2521  m_shipMaxAccelTime = (-log(0.0001) * m_shipAgility);
2522 
2523  // both of these formulas have identical products
2524  //TimeToWarp = -ln(0.25) x Mass Mkg x Inertia Mod
2525  //float alignTime = ((log(2) * m_shipInertia * m_mass) / 500000);
2526  m_alignTime = (-log(0.25) * m_shipAgility);
2528 
2529  m_hasSentShipUpdates = true;
2530 
2531  if (!mySE->HasPilot())
2532  return;
2533  if (mySE->GetPilot()->IsInSpace() and (mySE->SysBubble() != nullptr)) {
2534  std::vector<PyTuple*> updates;
2535  SetBallAgility sbagility;
2536  sbagility.entityID = mySE->GetID();
2537  sbagility.agility = m_shipInertia;
2538  updates.push_back(sbagility.Encode());
2539  SetBallMassive sbmassive;
2540  sbmassive.entityID = mySE->GetID();
2541  sbmassive.is_massive = false; // disable client-side bump checks
2542  updates.push_back(sbmassive.Encode());
2543  SetBallMass sbmass;
2544  sbmass.entityID = mySE->GetID();
2545  sbmass.mass = m_mass;
2546  updates.push_back(sbmass.Encode());
2547  SetBallSpeed sbspeed;
2548  sbspeed.entityID = mySE->GetID();
2549  sbspeed.speed = m_maxShipSpeed;
2550  updates.push_back(sbspeed.Encode());
2551  SendDestinyUpdate(updates); //consumed
2552  } else {
2553  m_hasSentShipUpdates = false;
2554  }
2555 }
2556 
2558  SetMaxVelocity(pMissile->GetSpeed());
2559  SetPosition(pMissile->GetSelf()->position());
2560  m_mass = pMissile->GetSelf()->type().mass();
2561  m_massMKg = m_mass / 1000000; //changes mass from Kg to MillionKg (10^-6)
2564 
2565  m_stop = false;
2567  m_stateStamp = sEntityList.GetStamp();
2568 
2569  SystemEntity* pTarget = pMissile->GetTargetSE();
2570  m_targetPoint = GPoint(pTarget->GetPosition());
2571  m_targetEntity.first = pTarget->GetID();
2572  m_targetEntity.second = pTarget;
2574 
2575  GVector moveVector(m_position, m_targetPoint);
2576  moveVector.normalize(); //change vector to direction
2577  m_shipHeading = moveVector;
2578 
2579  SetUndockSpeed(); /* sets all needed variables for max velocity */
2580  mySE->SystemMgr()->AddEntity(pMissile, false); // we are not adding missiles to anomaly map
2581 
2582  std::vector<PyTuple*> updates;
2583  SetBallSpeed maxspeed;
2584  maxspeed.entityID = pMissile->GetID();
2585  maxspeed.speed = m_maxShipSpeed;
2586  updates.push_back(maxspeed.Encode());
2587  Rsp_LaunchMissile miss;
2588  miss.shipID = pMissile->GetLauncherID();
2589  miss.targetID = pTarget->GetID();
2590  miss.missileID = pMissile->GetID();
2591  miss.unk1 = 1;
2592  miss.unk2 = 1;
2593  updates.push_back(miss.Encode());
2594  SendDestinyUpdate(updates); //consumed
2595 }
2596 
2599  return;
2600 
2601  Client* pClient = mySE->GetPilot();
2602  if (pClient == nullptr)
2603  return;
2604  // exactly why do we need this here??
2605  PyDict* slim = new PyDict();
2606  slim->SetItemString("name", new PyString(newShipRef->itemName()));
2607  slim->SetItemString("itemID", new PyInt(newShipRef->itemID()));
2608  slim->SetItemString("typeID", new PyInt(newShipRef->typeID()));
2609  slim->SetItemString("ownerID", new PyInt(mySE->GetOwnerID()));
2610  slim->SetItemString("charID", new PyInt(pClient->GetCharacterID()));
2611  slim->SetItemString("corpID", IsCorp(mySE->GetCorporationID()) ? new PyInt(mySE->GetCorporationID()) : PyStatic.NewNone());
2612  slim->SetItemString("allianceID", IsAlliance(mySE->GetAllianceID()) ? new PyInt(mySE->GetAllianceID()) : PyStatic.NewNone());
2613  slim->SetItemString("warFactionID", IsFaction(mySE->GetWarFactionID()) ? new PyInt(mySE->GetWarFactionID()) : PyStatic.NewNone());
2614  slim->SetItemString("bounty", new PyFloat(pClient->GetBounty()));
2615  slim->SetItemString("securityStatus", new PyFloat(pClient->GetSecurityRating()));
2616  if (newShipRef->typeID() == itemTypeCapsule) {
2617  slim->SetItemString("launcherID", new PyInt(mySE->GetShipSE()->GetLauncherID()));
2618  slim->SetItemString("modules", new PyList());
2619  } else {
2620  slim->SetItemString("categoryID", new PyInt(newShipRef->categoryID()));
2621  slim->SetItemString("groupID", new PyInt(newShipRef->groupID()));
2622  slim->SetItemString("modules", newShipRef->ShipGetModuleList());
2623  }
2624 
2625  std::vector<PyTuple*> updates;
2626  PyTuple* shipData = new PyTuple(2);
2627  shipData->SetItem(0, new PyLong(newShipRef->itemID()));
2628  shipData->SetItem(1, new PyObject( "foo.SlimItem", slim));
2629  PyTuple* shipItem = new PyTuple(2);
2630  shipItem->SetItem(0, new PyString("OnSlimItemChange"));
2631  shipItem->SetItem(1, shipData);
2632  updates.push_back(shipItem);
2633  SendDestinyUpdate(updates);
2634 
2636  SendBallInteractive(newShipRef, true);
2637 }
2638 
2640 {
2641  if (pShipSE->IsDead())
2642  return;
2643  PyDict* slimPod = new PyDict();
2644  slimPod->SetItemString("itemID", new PyInt(pShipSE->GetID()));
2645  slimPod->SetItemString("typeID", new PyInt(pShipSE->GetTypeID()));
2646  slimPod->SetItemString("categoryID", new PyInt(pShipSE->GetCategoryID()));
2647  slimPod->SetItemString("ownerID", new PyInt(pShipSE->GetOwnerID()));
2648  slimPod->SetItemString("charID", PyStatic.NewNone());
2649  slimPod->SetItemString("corpID", new PyInt(pShipSE->GetCorporationID()));
2650  slimPod->SetItemString("allianceID", new PyInt(pShipSE->GetAllianceID()));
2651  slimPod->SetItemString("warFactionID", new PyInt(pShipSE->GetWarFactionID()));
2652  slimPod->SetItemString("bounty", PyStatic.NewNone());
2653  slimPod->SetItemString("securityStatus", PyStatic.NewNone());
2654  PyTuple* shipData = new PyTuple(2);
2655  shipData->SetItem(0, new PyLong(pShipSE->GetID()));
2656  shipData->SetItem(1, new PyObject( "foo.SlimItem", slimPod));
2657  PyTuple* shipItem = new PyTuple(2);
2658  shipItem->SetItem(0, new PyString("OnSlimItemChange"));
2659  shipItem->SetItem(1, shipData);
2660  SendSingleDestinyUpdate(&shipItem); // consumed
2661 
2662  SendBallInteractive(pShipSE->GetShipItemRef(), false);
2663  m_hasSentShipUpdates = false;
2664 }
2665 
2666 void DestinyManager::Jump(bool showCloak)
2667 {
2668  Halt();
2669  if (showCloak) {
2670  m_cloaked = true;
2671  }
2672  if (mySE->SysBubble() != nullptr)
2674 }
2675 
2677  if (m_cloaked)
2678  return;
2679  m_cloaked = true;
2680  SendCloakFx(true);
2681  if (mySE->SysBubble() != nullptr)
2683 }
2684 
2686  if (!m_cloaked)
2687  return;
2688  m_cloaked = false;
2689  SendCloakFx();
2690  if (mySE->SysBubble() != nullptr)
2692 }
2693 
2695 {
2698 
2699  m_stop = false;
2700  m_accel = false;
2701  m_decel = false;
2702  m_turning = false;
2703  m_tractored = true;
2705  m_stateStamp = sEntityList.GetStamp();
2706 
2707  m_targetPoint = pShipSE->GetPosition();
2708  GVector moveVector(m_position, m_targetPoint);
2709  m_targetDistance = moveVector.length();
2710  moveVector.normalize();
2711  m_shipHeading = moveVector;
2712 
2713  m_maxShipSpeed = speed.get_float(); //AttrMaxTractorVelocity
2716 
2717  m_followDistance = 500 + pShipSE->GetRadius();
2718  m_shipMaxAccelTime = 0.1f;
2719 
2721 
2722  m_targetEntity.first = pShipSE->GetID();
2723  m_targetEntity.second = pShipSE;
2724 
2725  std::vector<PyTuple*> updates;
2726  SetBallSpeed ms;
2727  ms.entityID = mySE->GetID();
2728  ms.speed = m_maxShipSpeed;
2729  updates.push_back(ms.Encode());
2730  SetBallFree bf;
2731  bf.entityID = mySE->GetID();
2732  bf.is_free = 1;
2733  updates.push_back(bf.Encode());
2734  SetBallMass sbmass;
2735  sbmass.entityID = mySE->GetID();
2736  sbmass.mass = 10000;
2737  updates.push_back(sbmass.Encode());
2738  CmdSetSpeedFraction ssf;
2739  ssf.entityID = mySE->GetID();
2740  ssf.fraction = 1;
2741  updates.push_back(ssf.Encode());
2742  CmdFollowBall fb;
2743  fb.entityID = mySE->GetID();
2744  fb.targetID = pShipSE->GetID();
2745  fb.range = m_followDistance;
2746  updates.push_back(fb.Encode());
2747  SendDestinyUpdate(updates);
2748 }
2749 
2751 {
2752  Halt();
2753  m_tractored = false;
2754  std::vector<PyTuple*> updates;
2755  SetBallSpeed ms;
2756  ms.entityID = mySE->GetID();
2757  ms.speed = 0;
2758  updates.push_back(ms.Encode());
2759  SetBallFree bf;
2760  bf.entityID = mySE->GetID();
2761  bf.is_free = 0;
2762  updates.push_back(bf.Encode());
2763  SetBallMass sbmass;
2764  sbmass.entityID = mySE->GetID();
2765  sbmass.mass = m_mass;
2766  updates.push_back(sbmass.Encode());
2767  SendDestinyUpdate(updates);
2768 }
2769 
2770 /*
2771  [PyTuple 2 items]
2772  [PyInt 62696]
2773  [PyTuple 2 items]
2774  [PyString "OnSpecialFX"]
2775  [PyTuple 10 items]
2776  [PyIntegerVar 9000000000001190976]
2777  [PyNone]
2778  [PyNone]
2779  [PyNone]
2780  [PyNone]
2781  [PyList 0 items]
2782  [PyString "effects.Jettison"]
2783  [PyInt 0]
2784  [PyInt 1]
2785  [PyInt 0]
2786  */
2788  OnSpecialFX10 effect;
2789  effect.entityID = mySE->GetID();
2790  effect.guid = "effects.Jettison";
2791  effect.isOffensive = 0;
2792  effect.start = 1;
2793  effect.active = 0;
2794  PyTuple* up = effect.Encode();
2795  SendSingleDestinyUpdate(&up); // consumed
2796 }
2797 /*
2798  * [PyTuple 2 items]
2799  * [PyInt 8087]
2800  * [PyTuple 2 items]
2801  * [PyString "OnSpecialFX"]
2802  * [PyTuple 14 items]
2803  * [PyIntegerVar 1002332856217]
2804  * [PyIntegerVar 1002332856217]
2805  * [PyInt 12235]
2806  * [PyNone]
2807  * [PyNone]
2808  * [PyList 0 items]
2809  * [PyString "effects.AnchorDrop"]
2810  * [PyBool False]
2811  * [PyInt 1]
2812  * [PyInt 1]
2813  * [PyInt -1]
2814  * [PyInt 0]
2815  * [PyIntegerVar 129516974756172792]
2816  * [PyNone]
2817  */
2819  OnSpecialFX13 effect;
2820  effect.entityID = mySE->GetID();
2821  effect.moduleID = mySE->GetID();
2822  effect.moduleTypeID = mySE->GetTypeID();
2823  effect.guid = "effects.AnchorDrop";
2824  effect.isOffensive = 0;
2825  effect.start = 1;
2826  effect.active = 1;
2827  effect.startTime = GetFileTimeNow();
2828  PyTuple* up = effect.Encode();
2829  SendSingleDestinyUpdate(&up); // consumed
2830 }
2831 
2833  OnSpecialFX13 effect;
2834  effect.entityID = mySE->GetID();
2835  effect.moduleID = mySE->GetID();
2836  effect.moduleTypeID = mySE->GetTypeID();
2837  effect.guid = "effects.AnchorLift";
2838  effect.isOffensive = 0;
2839  effect.start = 1;
2840  effect.startTime = GetFileTimeNow();
2841  PyTuple* up = effect.Encode();
2842  SendSingleDestinyUpdate(&up); // consumed
2843 }
2844 
2845 /*
2846  [PyTuple 2 items]
2847  [PyInt 517]
2848  [PyTuple 2 items]
2849  [PyString "OnSpecialFX"]
2850  [PyTuple 10 items]
2851  [PyIntegerVar 1002332228246]
2852  [PyNone]
2853  [PyNone]
2854  [PyNone]
2855  [PyNone]
2856  [PyList 0 items]
2857  [PyString "effects.Cloak"]
2858  [PyInt 0]
2859  [PyInt 1]
2860  [PyInt 0]
2861  [PyTuple 2 items]
2862  [PyInt 517]
2863  [PyTuple 2 items]
2864  [PyString "OnSpecialFX"]
2865  [PyTuple 14 items]
2866  [PyIntegerVar 1002332228246]
2867  [PyIntegerVar 1002333797260]
2868  [PyInt 11578]
2869  [PyNone]
2870  [PyNone]
2871  [PyList 0 items]
2872  [PyString "effects.Cloaking"]
2873  [PyBool False]
2874  [PyInt 1]
2875  [PyInt 1]
2876  [PyInt -1]
2877  [PyInt 0]
2878  [PyIntegerVar 129527563080275219]
2879  [PyNone]
2880  [PyBool False]
2881  */
2882 
2884 void DestinyManager::SendCloakFx(bool apply/*false*/, bool module/*false*/) const {
2885  PyTuple *up(nullptr);
2886  if (module) {
2887  OnSpecialFX13 effect;
2888  effect.entityID = mySE->GetID();
2889  effect.isOffensive = 0;
2890  if (apply) {
2891  effect.guid = "effects.Cloaking";
2892  effect.start = 1;
2893  effect.active = 1;
2894  } else {
2895  effect.guid = "effects.Uncloak";
2896  }
2897  up = effect.Encode();
2898  } else {
2899  OnSpecialFX10 effect;
2900  if (apply) {
2901  effect.guid = "effects.Cloak";
2902  } else {
2903  effect.guid = "effects.Uncloak";
2904  }
2905  effect.entityID = mySE->GetID();
2906  effect.isOffensive = 0;
2907  effect.start = 1;
2908  effect.active = 0;
2909  up = effect.Encode();
2910  }
2911  SendSingleDestinyUpdate(&up); // consumed
2912 }
2913 
2914 // def OnSpecialFX(shipID, moduleID, moduleTypeID, targetID, otherTypeID, area, guid, isOffensive, start, active, duration = -1, repeat = None, startTime = None, graphicInfo = None):
2915 
2916 void DestinyManager::SendSpecialEffect10(uint32 entityID, uint32 targetID, std::string guid, bool isOffensive, bool start, bool isActive) const
2917 {
2918  OnSpecialFX10 effect;
2919  effect.entityID = entityID;
2920  effect.targetID = targetID;
2921  effect.guid = guid;
2922  effect.area = new PyList(); // this is unused variable in client.
2923  effect.isOffensive = isOffensive;
2924  effect.start = start;
2925  effect.active = isActive;
2926  PyTuple *up = effect.Encode();
2927  SendSingleDestinyUpdate(&up); // consumed
2928 }
2929 
2930 // def OnSpecialFX(shipID, moduleID, moduleTypeID, targetID, otherTypeID, area, guid, isOffensive, start, active, duration = -1, repeat = None, startTime = None, graphicInfo = None):
2931 
2932 void DestinyManager::SendSpecialEffect(uint32 entityID, uint32 moduleID, uint32 moduleTypeID, uint32 targetID,
2933  uint32 chargeTypeID, std::string guid, bool isOffensive, bool start,
2934  bool isActive, int32 duration, uint32 repeat, int32 graphicInfo/*0*/) const
2935 {
2936  OnSpecialFX13 effect;
2937  effect.entityID = entityID;
2938  effect.moduleID = moduleID;
2939  effect.moduleTypeID = moduleTypeID; // npc typeID for npc's/drones
2940  effect.targetID = (targetID == 0 ? PyStatic.NewNone() : new PyInt(targetID));
2941  effect.chargeTypeID = (chargeTypeID == 0 ? PyStatic.NewNone() : new PyInt(chargeTypeID));
2942  effect.guid = guid;
2943  effect.isOffensive = isOffensive; // bool
2944  effect.start = start; // bool
2945  effect.active = isActive; // bool
2946  effect.duration = duration;
2947  effect.repeat = repeat;
2948  effect.startTime = GetFileTimeNow();
2949  effect.graphicInfo = (graphicInfo == 0 ? PyStatic.NewNone() : new PyInt(graphicInfo));
2950  PyTuple *up = effect.Encode();
2951  SendSingleDestinyUpdate(&up); // consumed
2952 }
2953 /*
2954  [PyTuple 2 items]
2955  [PyInt 62565]
2956  [PyTuple 2 items]
2957  [PyString "OnSpecialFX"]
2958  [PyTuple 14 items]
2959  [PyIntegerVar 9000000000001190096]
2960  [PyIntegerVar 9000000000001190096]
2961  [PyInt 11931]
2962  [PyNone]
2963  [PyNone]
2964  [PyList 0 items]
2965  [PyString "effects.ShieldBoosting"]
2966  [PyBool False]
2967  [PyInt 1]
2968  [PyInt 1]
2969  [PyFloat 5000]
2970  [PyInt 1]
2971  [PyIntegerVar 129756560173255648]
2972  [PyNone]
2973  */
2974 
2976  OnSpecialFX10 effect;
2977  effect.entityID = mySE->GetID();
2978  effect.targetID = gateID;
2979  effect.guid = "effects.JumpOut";
2980  effect.isOffensive = 0;
2981  effect.start = 1;
2982  effect.active = 0;
2983  PyTuple *up = effect.Encode();
2984  SendSingleDestinyUpdate(&up); // consumed
2985 }
2986 
2988  OnSpecialFX10 du;
2989  du.entityID = gateID;
2990  du.guid = "effects.GateActivity";
2991  du.isOffensive = 0;
2992  du.start = 1;
2993  du.active = 0;
2994  PyTuple* up = du.Encode();
2995  SendSingleDestinyUpdate(&up); // consumed
2996 }
2997 
2998 void DestinyManager::SendBallInteractive(const ShipItemRef shipRef, bool set/*false*/) const {
2999  // interactive means "ship has pilot"
3000  SetBallInteractive sbi;
3001  sbi.entityID = shipRef->itemID();
3002  sbi.interactive = set;
3003  PyTuple* up = sbi.Encode();
3004  SendSingleDestinyUpdate(&up); // consumed
3005 }
3006 
3007 void DestinyManager::SendJumpOutEffect(std::string JumpEffect, uint32 locationID) const {
3008  std::vector<PyTuple*> updates;
3009  CmdStop du;
3010  du.entityID = mySE->GetID();
3011  updates.push_back(du.Encode());
3012  OnSpecialFX10 effect;
3013  effect.entityID = mySE->GetID();
3014  effect.targetID = locationID;
3015  effect.guid = "effects.JumpDriveOut"; /* JumpDriveInBO */
3016  effect.isOffensive = 0;
3017  effect.start = 1;
3018  effect.active = 0;
3019  updates.push_back(effect.Encode());
3020  SendDestinyUpdate(updates);
3021 }
3022 
3023 void DestinyManager::SendJumpInEffect(std::string JumpEffect) const {
3024  std::vector<PyTuple*> updates;
3025  OnSpecialFX10 effect;
3026  effect.guid = "effects.JumpDriveIn";
3027  effect.entityID = mySE->GetID();
3028  effect.isOffensive = 0;
3029  effect.start = 1;
3030  effect.active = 0;
3031  updates.push_back(effect.Encode());
3032  CmdSetSpeedFraction ssf;
3033  ssf.entityID = mySE->GetID();
3034  ssf.fraction = 0.0;
3035  updates.push_back(ssf.Encode());
3036  SetBallVelocity sbv;
3037  sbv.entityID = mySE->GetID();
3038  sbv.x = 0.0;
3039  sbv.y = 0.0;
3040  sbv.z = 0.0;
3041  updates.push_back(sbv.Encode());
3042  SendDestinyUpdate(updates);
3043 }
3044 
3045 void DestinyManager::SendTerminalExplosion(uint32 shipID, uint32 bubbleID, bool isGlobal/*false*/) const {
3046  //exploders = [ x[1][1][0] for x in state if x[1][0] == 'TerminalExplosion' ]
3047  /*
3048  [PyTuple 2 items] x
3049  [PyInt 62609] x[0]
3050  [PyTuple 2 items] x[1]
3051  [PyString "TerminalExplosion"] x[1][0]
3052  [PyTuple 3 items] x[1][1]
3053  [PyIntegerVar 9000000000001190702] x[1][1][0]
3054  [PyInt 39]
3055  [PyBool False]
3056  */
3057  //send an explosion special effects update...
3058  TerminalExplosion du;
3059  du.shipID = shipID;
3060  du.bubbleID = bubbleID;
3061  du.ballIsGlobal = isGlobal;
3062  PyTuple* up = du.Encode();
3063  SendSingleDestinyUpdate(&up); // consumed
3064 }
3065 
3067  if (!mySE->HasPilot())
3068  return;
3069 
3070  if (is_log_enabled(DESTINY__MESSAGE))
3071  _log(DESTINY__MESSAGE, "Destiny::SendSetState() Called for Ship:%s(%u) Pilot:%s(%u)", \
3073 
3074  SetState ss;
3075  ss.stamp = sEntityList.GetStamp();
3076  ss.ego = mySE->GetID();
3077 
3079  PyTuple* tmp(ss.Encode());
3080  //setstate should be alone and immediate. send directly
3081  mySE->GetPilot()->QueueDestinyUpdate(&tmp, true, true); // consumed
3082  mySE->GetPilot()->SetStateSent(true);
3083 }
3084 
3085 void DestinyManager::SendSingleDestinyEvent(PyTuple** ev, bool self_only/*false*/) const
3086 {
3087  std::vector<PyTuple*> updates;
3088  std::vector<PyTuple*> events(1, *ev); // create vector of size "1" and insert "*ev" into it
3089  SendDestinyUpdate(updates, events, self_only);
3090 }
3091 
3092 void DestinyManager::SendSingleDestinyUpdate(PyTuple **up, bool self_only/*false*/) const {
3093  std::vector<PyTuple*> updates(1, *up);
3094  std::vector<PyTuple*> events;
3095  SendDestinyUpdate(updates, events, self_only);
3096 }
3097 
3098 void DestinyManager::SendDestinyUpdate(std::vector<PyTuple*> &updates, bool self_only/*false*/) const {
3099  std::vector<PyTuple*> events;
3100  SendDestinyUpdate(updates, events, self_only);
3101 }
3102 
3103 void DestinyManager::SendDestinyUpdate( std::vector<PyTuple*>& updates, std::vector<PyTuple*>& events, bool self_only/*false*/) const {
3104  // this check shouldnt be needed...
3105  if (!mySE->SystemMgr()->IsLoaded())
3106  return;
3107  if (self_only) {
3108  if (!mySE->HasPilot()) {
3109  // this entity is NOT a player ship...change to BubbleCast (or silently fail)
3110  if (mySE->SysBubble() != nullptr) {
3111  if (is_log_enabled(DESTINY__UPDATES))
3112  _log( DESTINY__UPDATES, "[%u] BubbleCasting destiny update (u:%u, e:%u) for stamp %u to bubbleID %u from %s(%u)", \
3113  sEntityList.GetStamp(), updates.size(), events.size(), sEntityList.GetStamp(), mySE->SysBubble()->GetID(), mySE->GetName(), mySE->GetID() );
3114  mySE->SysBubble()->BubblecastDestiny( updates, events, "destiny" );
3115  }
3116  return;
3117  }
3118  if (is_log_enabled(PLAYER__MESSAGE))
3119  _log(PLAYER__MESSAGE, "[%u] DestinyManager::SendDestinyUpdate() (u:%i, e:%i) called as 'self_only' for %s(%u)", \
3120  sEntityList.GetStamp(), updates.size(), events.size(), mySE->GetPilot()->GetName(), mySE->GetPilot()->GetCharacterID());
3121 
3122  for (std::vector<PyTuple*>::iterator cur = updates.begin(); cur != updates.end(); ++cur) {
3123  PyIncRef(*cur);
3124  mySE->GetPilot()->QueueDestinyUpdate(&(*cur));
3125  }
3126 
3127  for (std::vector<PyTuple*>::iterator cur = events.begin(); cur != events.end(); ++cur) {
3128  PyIncRef(*cur);
3129  mySE->GetPilot()->QueueDestinyEvent(&(*cur));
3130  }
3131  } else if (mySE->IsOperSE()) { //These are global entities, so we have to send update to all bubbles in a system
3132  if (is_log_enabled(DESTINY__UPDATES))
3133  _log(DESTINY__UPDATES, "[%u] BubbleCasting global structure destiny update (u:%u, e:%u) for stamp %u to all bubbles from %s(%u)", \
3134  sEntityList.GetStamp(), updates.size(), events.size(), sEntityList.GetStamp(), \
3135  (mySE->HasPilot()?mySE->GetPilot()->GetName():mySE->GetName()),\
3136  (mySE->HasPilot()?mySE->GetPilot()->GetCharID():mySE->GetID()) );
3137 
3138  //Get all clients in the system which the SE is in
3139  std::vector<Client*> cv;
3140  mySE->SystemMgr()->GetClientList(cv);
3141  for(auto const& value: cv) {
3142  value->GetShipSE()->SysBubble()->BubblecastDestiny(updates, events, "destiny");
3143  }
3144  } else if (mySE->SysBubble() != nullptr) {
3145  if (is_log_enabled(DESTINY__UPDATES))
3146  _log(DESTINY__UPDATES, "[%u] BubbleCasting destiny update (u:%u, e:%u) for stamp %u to bubbleID %u from %s(%u)", \
3147  sEntityList.GetStamp(), updates.size(), events.size(), sEntityList.GetStamp(), mySE->SysBubble()->GetID(), \
3148  (mySE->HasPilot()?mySE->GetPilot()->GetName():mySE->GetName()),\
3149  (mySE->HasPilot()?mySE->GetPilot()->GetCharID():mySE->GetID()) );
3150  mySE->SysBubble()->BubblecastDestiny( updates, events, "destiny" );
3151  } else {
3152  _log(DESTINY__WARNING, "[%u] Cannot BubbleCast destiny update (u:%u, e:%u); entity (%u) is not in any bubble.", \
3153  sEntityList.GetStamp(), updates.size(), events.size(), mySE->GetID() );
3154  if (sConfig.debug.IsTestServer)
3155  EvE::traceStack();
3156  //sBubbleMgr.Add(mySE);
3157  //mySE->SysBubble()->BubblecastDestiny( updates, events, "destiny" );
3158  }
3159 }
#define sConfig
A macro for easier access to the singleton.
unsigned __int8 uint8
Definition: eve-compat.h:46
void SetPosition(const GPoint &pos)
Definition: SystemEntity.h:212
double GetSpeed()
Definition: Missile.h:66
void SendNotification(const PyAddress &dest, EVENotificationStream &noti, bool seq=true)
Definition: Client.cpp:2245
SystemEntity * GetSE(uint32 entityID) const
GaExpInl GaFloat dotProduct(const GaVec3 &oth) const
Definition: GaTypes.h:150
GaExpInl bool isNotZero() const
Definition: GaTypes.h:191
float m_currentSpeedFraction
void AddEntity(SystemEntity *pSE, bool addSignal=true)
bool IsAligned(GPoint &targetPoint)
GaExpInl GaFloat length() const
Definition: GaTypes.h:156
void WarpCruise(uint16 sec_into_warp)
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
void AddBallExclusive(SystemEntity *about_who)
#define _log(type, fmt,...)
Definition: logsys.h:124
#define stDataMgr
void SendAnchorLift() const
void Disable()
Definition: timer.h:39
Python string.
Definition: PyRep.h:430
virtual bool IsNPCSE()
Definition: SystemEntity.h:186
double GetRadius()
Definition: SystemEntity.h:208
SystemEntity * GetTargetSE()
Definition: Missile.h:58
float GetSecurityRating() const
Definition: Client.h:172
static const uint32 minWarpDistance(130000)
void ClearAllTargets(bool notify=true)
void SetAutoPilot(bool set=false)
Definition: Client.cpp:669
Python's dictionary.
Definition: PyRep.h:719
bool HasAttribute(const uint16 attrID) const
void SendGateActivity(uint32 gateID) const
virtual bool IsShipSE()
Definition: SystemEntity.h:190
const GPoint & position() const
void GotoDirection(const GPoint &direction)
void SendJettisonPacket() const
void MakeMissile(Missile *missile)
virtual NPC * GetNPCSE()
Definition: SystemEntity.h:134
SystemBubble * SysBubble()
Definition: SystemEntity.h:195
bool InBubble(const GPoint &pt, bool inWarp=false) const
static const float TURN_ALIGNMENT
virtual ShipSE * GetShipSE()
Definition: SystemEntity.h:137
void SendCloakFx(bool apply=false, bool module=false) const
void SendJumpInEffect(std::string JumpEffect) const
void SendJumpOutEffect(std::string JumpEffect, uint32 locationID) const
ShipItemRef GetShipItemRef()
Definition: Ship.h:362
float m_prevSpeedFraction
double Rad2Deg(double rad)
Definition: Trig.h:26
uint32 GetOwnerID()
Definition: SystemEntity.h:219
virtual bool HasPilot()
Definition: SystemEntity.h:258
void QueueDestinyEvent(PyTuple **multiEvent)
Definition: Client.cpp:2124
GaExpInl bool isNaN() const
Definition: GaTypes.h:194
virtual bool IsProbeSE()
Definition: SystemEntity.h:159
#define sProfiler
Definition: dbcore.cpp:39
itemID[count] Create count or of the specified() x() entityID Translocate to the specified entity Immediately stops setting then Sends Bubble AddBalls and Destiny SetState(resets spaceview with current server data)" ) COMMAND( sendstate
virtual Client * GetPilot()
Definition: SystemEntity.h:259
static CargoContainerRef SpawnTemp(ItemData &data)
Definition: Container.cpp:87
Python floating point number.
Definition: PyRep.h:292
int32 GetCharacterID() const
Definition: Client.h:113
virtual bool IsOperSE()
Definition: SystemEntity.h:180
void SendDestinyUpdate(std::vector< PyTuple * > &updates, bool self_only=false) const
const double Pi2
Definition: Trig.h:21
void SetMaxVelocity(float maxVelocity)
bool IsUndock()
Definition: Client.h:236
#define sEntityList
Definition: EntityList.h:208
void GetClientList(std::vector< Client * > &cVec)
WarpState * m_warpState
bool IsIdle()
Definition: NPCAI.h:70
void AddMarker(SystemEntity *pSE, bool sendBall=false, bool addSignal=false)
this is a class that kinda mimics how python polymorph's numbers.
Definition: EvilNumber.h:59
bool IsInSpace()
Definition: Client.h:228
void SendNotifyMsg(const char *fmt,...)
Definition: Client.cpp:2776
TargetManager * TargetMgr()
Definition: SystemEntity.h:197
CharacterRef GetChar() const
Definition: Client.h:164
Python tuple.
Definition: PyRep.h:567
GaFloat x
Definition: GaTypes.h:207
SystemEntity *const mySE
Definition: Ship.h:301
void SendSetState() const
const GPoint & GetPosition() const
Definition: SystemEntity.h:211
float m_maxOrbitSpeedFraction
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
void Bounce(GVector direction, float speed)
float m_activeSpeedFraction
void UpdateOldShip(ShipSE *pShipSE)
uint16 groupID() const
signed __int32 int32
Definition: eve-compat.h:49
void WarpStop(double currentShipSpeed)
GaExpInl GaFloat normalize()
Definition: GaTypes.h:163
void Warp()
Definition: Ship.cpp:2564
const ItemType & type() const
NPCAIMgr * GetAIMgr()
Definition: NPC.h:81
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
std::pair< uint32, SystemEntity * > m_targetEntity
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
void WarpDecel(uint16 sec_into_warp)
Definition: gpoint.h:33
void SendAnchorDrop() const
void BubblecastDestiny(std::vector< PyTuple * > &updates, std::vector< PyTuple * > &events, const char *desc) const
DestinyManager * DestinyMgr()
Definition: SystemEntity.h:198
SystemManager * SystemMgr()
Definition: SystemEntity.h:196
double GetTimeUSeconds()
Definition: utils_time.cpp:116
InventoryItemRef GetSelf()
Definition: SystemEntity.h:202
int32 GetWarpSpeed()
double GetTimeMSeconds()
Definition: utils_time.cpp:104
Python object.
Definition: PyRep.h:826
uint32 GetLocationID()
Definition: SystemEntity.h:209
virtual bool IsStationSE()
Definition: SystemEntity.h:151
float GetBounty() const
Definition: Client.h:171
float m_speedToLeaveWarp
double Deg2Rad(double deg)
Definition: Trig.h:25
#define codelog(type, fmt,...)
Definition: logsys.h:128
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
virtual bool IsContainerSE()
Definition: SystemEntity.h:157
uint16 GetID()
Definition: SystemBubble.h:91
void SendJumpOut(uint32 gateID) const
static const GPoint NULL_ORIGIN(0, 0, 0)
void AlignTo(SystemEntity *pSE)
virtual bool IsMissileSE()
Definition: SystemEntity.h:189
float mass() const
Definition: ItemType.h:69
#define LogMacro(v)
Python integer.
Definition: PyRep.h:231
bool CanThrow()
Definition: Client.h:431
void WarpOutComplete()
Definition: NPCAI.h:80
uint8 GetCategoryID()
Definition: SystemEntity.h:205
bool HasWarpBubble()
Definition: SystemBubble.h:128
GVector m_shipHeading
void SetSpeedFraction(float fraction=1.0f, bool startMovement=false)
int32 GetAllianceID()
Definition: SystemEntity.h:216
void SetUndocking(bool set=false)
Definition: Ship.h:151
void SetAttribute(uint16 attrID, int num, bool notify=true)
uint32 GetID()
Definition: SystemEntity.h:207
bool HasPlayers() const
Definition: SystemBubble.h:85
#define PyStatic
Definition: PyRep.h:1209
void EntityRemoved(SystemEntity *pSE)
ShipItemRef GetShip() const
Definition: Client.h:167
const char * GetName() const
Definition: Client.h:94
uint32 GetCharID()
Definition: Client.h:166
void SetStateSent(bool set=false)
Definition: Client.h:249
const char * GetName() const
Definition: SystemEntity.h:210
void WarpAccel(uint16 sec_into_warp)
void UpdateVelocity(bool isMoving=false)
void UpdateNewShip(const ShipItemRef newShipRef)
void QueueDestinyUpdate(PyTuple **update, bool DoPackage=false, bool IsSetState=false)
Definition: Client.cpp:2131
#define PyDecRef(op)
Definition: PyRep.h:57
GaExpInl bool isInf() const
Definition: GaTypes.h:197
void Jump(bool showCloak=true)
Python object "ccp_exceptions.UserError".
Definition: PyExceptions.h:121
static const GVector NULL_ORIGIN_V(0, 0, 0)
Definition: Client.h:66
void RemoveExclusive(SystemEntity *pSE)
uint32 GetLauncherID()
Definition: Missile.h:57
unsigned __int32 uint32
Definition: eve-compat.h:50
uint32 m_followDistance
void SetPosition(const GPoint &pt, bool update=false)
#define PyIncRef(op)
Definition: PyRep.h:56
bool IsAutoPilot()
Definition: Client.h:256
virtual bool IsPOSSE()
Definition: SystemEntity.h:163
virtual bool IsFrozen()
Definition: SystemEntity.h:185
uint32 GetDockStationID()
Definition: Client.h:225
void SendSingleDestinyUpdate(PyTuple **up, bool self_only=false) const
virtual bool IsWreckSE()
Definition: SystemEntity.h:188
#define IsCorp(itemID)
Definition: EVE_Defines.h:234
void Bump(SystemEntity *who)
void GotoPoint(const GPoint &point)
GaFloat y
Definition: GaTypes.h:207
double GetFileTimeNow()
Definition: utils_time.cpp:84
uint32 GetLauncherID()
Definition: Ship.h:360
GVector m_targetHeading
void WarpTo(const GPoint &where, int32 distance=0, bool autoPilot=false, SystemEntity *pSE=nullptr)
void SendSpecialEffect10(uint32 entityID, uint32 targetID, std::string guid, bool isOffensive, bool start, bool isActive) const
PyServiceMgr & GetServices()
Definition: SystemEntity.h:194
void MakeSetState(const SystemBubble *pBubble, SetState &into) const
uint32 GetCorporationID()
Definition: SystemEntity.h:218
const std::string & itemName() const
void Remove(SystemEntity *pSE)
void SendTerminalExplosion(uint32 shipID, uint32 bubbleID, bool isGlobal=false) const
EvilNumber GetAttribute(const uint16 attrID) const
void SendSingleDestinyEvent(PyTuple **ev, bool self_only=false) const
GaExpInl bool isZero() const
Definition: GaTypes.h:188
#define IsFaction(itemID)
Definition: EVE_Defines.h:250
void SetStateTimer(int8 state, uint32 time=Player::Timer::Default)
Definition: Client.cpp:1599
void Undock(GPoint dir)
void TractorBeamStart(SystemEntity *pShipSE, EvilNumber speed)
float m_shipMaxAccelTime
int8 GetSkillLevel(uint16 skillTypeID, bool zeroForNotInjected=true) const
Definition: Character.cpp:575
double m_warpCapacitorNeed
void WarpUpdate(double currentShipSpeed)
virtual bool IsDynamicEntity()
Definition: SystemEntity.h:182
GPoint ComputePosition(double curRad)
const GPoint & GetPosition() const
void SpeedBoost(bool deactivate=false)
void traceStack(void)
Definition: misc.cpp:169
const bool IsDead()
Definition: SystemEntity.h:239
DestinyManager(SystemEntity *self)
uint32 m_targetDistance
#define IsAlliance(itemID)
Definition: EVE_Defines.h:244
void SendBallInteractive(const ShipItemRef shipRef, bool set=false) const
float get_float()
Definition: EvilNumber.cpp:184
Definition: gpoint.h:70
GaExpInl GaFloat distance(const GaVec3 &oth) const
Definition: GaTypes.h:158
SystemBubble * m_targBubble
uint16 GetTypeID()
Definition: SystemEntity.h:203
PyResult AttemptDockOperation()
static const uint16 BUMP_DISTANCE
#define sBubbleMgr
static const float WARP_ALIGNMENT
unsigned __int16 uint16
Definition: eve-compat.h:48
void Add(SystemEntity *pSE)
virtual bool IsDroneSE()
Definition: SystemEntity.h:187
Mode
Definition: EVE_LSC.h:91
float m_userSpeedFraction
PyList * ShipGetModuleList()
Definition: Ship.cpp:2247
uint16 typeID() const
uint8 categoryID() const
GPoint GetCenter()
Definition: SystemBubble.h:93
Python list.
Definition: PyRep.h:639
GaFloat z
Definition: GaTypes.h:207
void WebbedMe(InventoryItemRef modRef, bool apply=false)
const char * itoa(int64 num)
Convers num to string.
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
static const int64 ONE_AU_IN_METERS
Definition: EVE_Consts.h:40
int32 GetWarFactionID()
Definition: SystemEntity.h:217
void GetPlayers(std::vector< Client * > &into) const
Python long integer.
Definition: PyRep.h:261
static const float BUBBLE_RADIUS_METERS
Definition: BubbleManager.h:32