EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
InventoryItem.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  Updates: Allan
25 */
26 
27 #include "eve-server.h"
28 
29 #include "../../eve-common/EVE_Mail.h"
30 
31 #include "Client.h"
32 #include "ConsoleCommands.h"
33 #include "EntityList.h"
34 #include "character/Skill.h"
36 #include "exploration/Probes.h"
38 #include "pos/Structure.h"
39 #include "ship/Ship.h"
41 #include "station/Station.h"
42 #include "station/StationOffice.h"
43 #include "system/Asteroid.h"
44 #include "system/Celestial.h"
45 #include "system/Container.h"
46 
47 /*
48  * InventoryItem
49  */
50 InventoryItem::InventoryItem(uint32 _itemID, const ItemType& _type, const ItemData& _data)
51 : RefObject(0),
52 pAttributeMap(new AttributeMap(*this)),
53 pInventory(nullptr), // this is created/destroyed in derived classes as needed.
54 m_data(_data),
55 m_type(_type),
56 m_itemID(_itemID),
57 m_timestamp(0), // placeholder for fx timestamp, once implemented
58 m_delete(false)
59 {
60  // assert for data consistency
61  assert(_data.typeID == _type.id());
62  m_modifiers.clear();
63 
64  _log(ITEM__TRACE, "II::C'tor - Created Generic Item %p for item %s (%u).", this, m_data.name.c_str(), m_itemID);
65 }
66 
67 // copy c'tor
69 : RefObject(0),
70 pAttributeMap(oth.pAttributeMap),
71 pInventory(oth.pInventory),
72 m_itemID(oth.m_itemID),
73 m_data(oth.m_data),
74 m_type(oth.m_type),
75 m_timestamp(oth.m_timestamp),
76 m_delete(false)
77 {
78  sLog.Error("InventoryItem()", "InventoryItem copy c'tor called.");
80  assert(0);
81 }
82 
83 // move c'tor
85 : RefObject(0),
86 pAttributeMap(oth.pAttributeMap),
87 pInventory(oth.pInventory),
88 m_itemID(oth.m_itemID),
89 m_data(oth.m_data),
90 m_type(oth.m_type),
91 m_timestamp(oth.m_timestamp),
92 m_delete(false)
93 {
94  sLog.Error("InventoryItem()", "InventoryItem move c'tor called.");
96  assert(0);
97 }
98 
99 // copy assignment =delete
100 /*
101 InventoryItem& InventoryItem::operator= ( const InventoryItem& oth )
102 {
103  sLog.Error("InventoryItem()", "InventoryItem copy assign called.");
104  EvE::traceStack();
105  assert(0);
106 
107  oth.GetAttributeMap()->CopyAttributes(pAttributeMap->mAttributes);
108 
109  m_itemID = oth.itemID();
110  m_type = oth.type();
111 } */
112 
113 // move assignment =delete
114 /*
115 InventoryItem& InventoryItem::operator= ( InventoryItem&& oth ) noexcept
116 {
117  sLog.Error("InventoryItem()", "InventoryItem move assign called.");
118  EvE::traceStack();
119  assert(0);
120 
121  oth.GetAttributeMap()->CopyAttributes(pAttributeMap->mAttributes);
122 
123  m_itemID = oth.itemID();
124  m_type = oth.type();
125 } */
126 
127 
129 {
131 }
132 
134 {
135  return InventoryItem::Load<InventoryItem>(itemID);
136 }
137 
139 {
140  if (data.quantity == 0)
141  return InventoryItemRef(nullptr);
142  const ItemType *iType = sItemFactory.GetType(data.typeID);
143  if (iType == nullptr)
144  return InventoryItemRef(nullptr);
145  InventoryItemRef iRef = InventoryItemRef(new InventoryItem(itemID, *iType, data));
146  if (iRef.get() != nullptr)
147  iRef->_Load();
148 
149  return iRef;
150 }
151 
153  // obtain type of new item
154  const ItemType *iType = sItemFactory.GetType(data.typeID);
155  if (iType == nullptr) {
156  codelog(ITEM__ERROR, "II::CreateItemID() - Invalid type returned for typeID %u", data.typeID);
157  return 0;
158  }
159  // fix the name (if empty)
160  if (data.name.empty())
161  data.name = iType->name();
162 
163  if (data.locationID == 0)
164  if (is_log_enabled(ITEM__MESSAGE)) {
165  _log(ITEM__MESSAGE, "II::CreateItemID() - LocationID = 0 for item");
166  EvE::traceStack();
167  }
168 
169  // insert new entry into DB
170  return ItemDB::NewItem(data);
171 }
172 
173 /* This Spawn function is meant for in-memory only items created from the following categories...
174  * EVEDB::invCategories::Entity (for npcs)
175  * EVEDB::invCategories::Charge (for launched missiles only)
176  * EVEDB::invCategories::Container (for Position Tracking only)
177  * all dungeon/anomaly items
178  *
179  * these items meant to never be saved to database
180  * and be thrown away on server shutdown.
181  * Updated 29May15 -Allan
182  */
184  // obtain type of new item
185  // this also checks that the type is valid
186  const ItemType *iType = sItemFactory.GetType(data.typeID);
187  if (iType == nullptr) {
188  codelog(ITEM__ERROR, "II::CreateTempItemID() - Invalid ItemType returned for typeID %u", data.typeID);
189  return 0;
190  }
191 
192  // fix the name if empty
193  if (data.name.empty())
194  data.name = iType->name();
195 
196  // Get a new Entity ID from ItemFactory's ID Authority:
197  // may need more testing to verify that ONLY NPC's and jetcan markers use this method
199  return sItemFactory.GetNextNPCID();
200 
201  if (data.flag == EVEItemFlags::flagMissile)
202  return sItemFactory.GetNextMissileID();
203 
204  return sItemFactory.GetNextTempID();
205 }
206 
208  if (!pAttributeMap->Load()) {
209  _log(ITEM__WARNING, "II::_Load() - Failed to load attribute map for %s(%u).", m_data.name.c_str(), m_itemID);
210  return false;
211  }
212 
213  return true;
214 }
215 
216 template<class _Ty>
218  switch(type.categoryID() ) {
220  case EVEDB::invCategories::Material: // includes minerals
227  case EVEDB::invCategories::Accessories: { // this is for bookmark vouchers
228  // Generic item, create one
229  return InventoryItemRef(new InventoryItem(itemID, type, data ));
230  } break;
231  case EVEDB::invCategories::Deployable: // these may need their own class. not sure yet
235  // create Generic item for now
236  return InventoryItemRef(new InventoryItem(itemID, type, data ));
237  } break;
240  return ModuleItem::_LoadItem<ModuleItem>(itemID, type, data);
241  } break;
243  return Character::_LoadItem<Character>(itemID, type, data);
244  } break;
246  return Skill::_LoadItem<Skill>(itemID, type, data);
247  } break;
249  return Blueprint::_LoadItem<Blueprint>(itemID, type, data);
250  } break;
252  if (IsAsteroidID(itemID))
253  return AsteroidItem::_LoadItem<AsteroidItem>(itemID, type, data);
254  // mined ore. create default item
255  return InventoryItemRef(new InventoryItem(itemID, type, data ));
256  } break;
258  return ShipItem::_LoadItem<ShipItem>(itemID, type, data);
259  } break;
264  return StructureItem::_LoadItem<StructureItem>(itemID, type, data);
265  } break;
266  case EVEDB::invCategories::Charge: { // probes are charges.
267  switch (type.groupID()) {
271  case EVEDB::invGroups::Obsolete_Probes: { // make error for these?
272  return ProbeItem::_LoadItem<ProbeItem>(itemID, type, data);
273  } break;
274  default: {
275  // create generic item for other charge types
276  return InventoryItemRef(new InventoryItem(itemID, type, data ));
277  } break;
278  }
279  } break;
281  // test for office first
282  if (type.id() == 27) {
283  return StationOffice::_LoadItem<StationOffice>(itemID, type, data);
284  } else if (type.groupID() != EVEDB::invGroups::Station) {
285  return StationItem::_LoadItem<StationItem>(itemID, type, data);
286  }/* else if (type.groupID() != EVEDB::invGroups::Station_Services) {
287  this isnt written yet...
288  } */
289  } break;
291  switch (type.groupID()) {
293  return SolarSystem::_LoadItem<SolarSystem>(itemID, type, data);
294  } break;
300  return CargoContainer::_LoadItem<CargoContainer>(itemID, type, data);
301  } break;
303  return WreckContainer::_LoadItem<WreckContainer>(itemID, type, data);
304  } break;
306  default: {
307  // generic Celestial Object
308  return CelestialObject::_LoadItem<CelestialObject>(itemID, type, data);
309  } break;
310  }
311  } break;
312  // there are 216 groups in the entity category. im not testing all of them.
314  switch (type.groupID()) {
316  return CargoContainer::_LoadItem<CargoContainer>(itemID, type, data);
317  } break;
319  return CelestialObject::_LoadItem<CelestialObject>(itemID, type, data);
320  } break;
322  // the rest are drones and npcs....i think
323  // they are *somewhat* separated for eventual classification into their own itemtypes
326  // added checks for all npc's -allan 26Dec14
331 
336 
339 
346 
350  /* start at EVEDB::invGroups::Deadspace_Sleeper_Sleepless_Defender:
351  end at EVEDB::invGroups::Deadspace_Sleeper_Emergent_Patroller:
352  */
358  _log(ITEM__WARNING, "item %u (type %u, group %u) defaulting to generic InventoryItem.", itemID, type.id(), type.groupID());
359  } break;
360  default: {
361  _log(ITEM__WARNING, "item %u (type %u, group %u, cat %u) is not handled in II::_LoadItem::Entity.", itemID, type.id(), type.groupID(), type.categoryID());
362  }
363  }
364  }
365  default: {
366  _log(ITEM__WARNING, "item %u (type %u, group %u, cat %u) is not handled in II::_LoadItem.", itemID, type.id(), type.groupID(), type.categoryID());
367  } break;
368  }
369  // Generic item, create one:
370  return InventoryItemRef(new InventoryItem(itemID, type, data ));
371 }
372 
374 {
376  // for now, return generic item
377  // obtain type of new item
378  const ItemType *iType = sItemFactory.GetType(data.typeID);
379  if (iType == nullptr) {
380  codelog(ITEM__ERROR, "II::SpawnTemp() - Invalid type returned for typeID %u", data.typeID);
381  return InventoryItemRef(nullptr);
382  }
383 
385  return CargoContainer::SpawnTemp(data);
386 
388 }
389 
390 // called from generic SpawnItem()
392 {
393  // obtain type of new item
394  const ItemType *iType = sItemFactory.GetType(data.typeID);
395  if (iType == nullptr) {
396  codelog(ITEM__ERROR, "II::Spawn() - Invalid type returned for typeID %u", data.typeID);
397  return InventoryItemRef(nullptr);
398  }
399 
400  switch(iType->categoryID() ) { //23
402  assert(0); // this needs to make a serious error here....these CANNOT be called from here using the generic InventoryItem::Spawn() method
403  } break;
405  case EVEDB::invCategories::Bonus: // no clue what this is
408  case EVEDB::invCategories::Asteroid: // ore is "asteroid" also
409  case EVEDB::invCategories::Material: // includes minerals
412  case EVEDB::invCategories::Accessories: // this is for bookmark vouchers
413  case EVEDB::invCategories::AncientRelics: // t3 bpc from sleepers
414  // these *may* need their own class
420  _log(ITEM__WARNING, "II::Spawn creating generic item for type %u, cat %u.", iType->id(), iType->categoryID());
421  // Spawn generic item:
423  return InventoryItem::SpawnItem(itemID, data);
424  } break;
428  return StructureItem::Spawn(data);
429  } break;
431  // this needs to distinguish between copy and orig
432  EvERam::bpData bdata = EvERam::bpData();
433  bdata.runs = -1;
434  return Blueprint::Spawn(data, bdata);
435  } break;
437  return Skill::Spawn(data);
438  } break;
440  return ShipItem::Spawn(data);
441  } break;
443  switch (iType->groupID()) {
445  return CargoContainer::Spawn(data);
446  } break;
451  return InventoryItem::SpawnItem(itemID, data);
452  } break;
453  case EVEDB::invGroups::Sentry_Gun: // these are not saved
455  default: { // *should* be all npcs
456  // use temp items and NOT save to db.
458  return InventoryItem::SpawnItem(itemID, data);
459  } break;
460  }
461  } break;
464  return ModuleItem::Spawn(data);
465  } break;
467  if (!sConfig.testing.EnableDrones)
468  return InventoryItemRef(nullptr);
469  //return DroneItem::Spawn(data);
470  } // allow fallthru until DroneItem is written
472  // Spawn generic item:
474  InventoryItemRef itemRef = InventoryItem::SpawnItem(itemID, data);
475  if (itemRef.get() == nullptr)
476  return InventoryItemRef(nullptr);
477  // THESE SHOULD BE MOVED INTO A _type::Spawn() function that does not exist yet
478  itemRef->SetAttribute(AttrMass, iType->mass(), false); // Mass
479  itemRef->SetAttribute(AttrRadius, iType->radius(), false); // Radius
480  itemRef->SetAttribute(AttrVolume, iType->volume(), false); // Volume
481  itemRef->SetAttribute(AttrCapacity, iType->capacity(), false); // Capacity
482  return itemRef;
483  } break;
485  uint32 itemID = 0;
486  InventoryItemRef itemRef;
487  switch (data.flag) {
489  // Spawn launched missile item in MISSILE_ID range and does NOT save missile to db
490  itemID = InventoryItem::CreateTempItemID(data);
491  itemRef = InventoryItem::SpawnItem(itemID, data);
492  } break;
493  default: {
494  switch (iType->groupID()) {
498  return ProbeItem::Spawn(data);
499  } break;
501  // make error for these
502  _log(ITEM__ERROR, "II::Spawn - Obsolete Probe group called.");
503  return InventoryItemRef(nullptr);
504  }
505  default: {
506  // create generic item for other charge types
507  itemID = InventoryItem::CreateItemID(data);
508  itemRef = InventoryItem::SpawnItem(itemID, data);
509  } break;
510  }
511  }
512  }
513  if (itemRef.get() == nullptr)
514  return InventoryItemRef(nullptr);
515  itemRef->SetAttribute(AttrMass, iType->mass(), false); // Mass
516  itemRef->SetAttribute(AttrRadius, iType->radius(), false); // Radius
517  return itemRef;
518  } break;
520  if (iType->groupID() == EVEDB::invGroups::Station) {
522  if (itemID == 0)
523  return StationItemRef(nullptr);
524  StationItemRef stationRef = StationItem::Load(itemID);
525  if (stationRef.get() == nullptr)
526  return StationItemRef(nullptr);
527  // THESE SHOULD BE MOVED INTO A Station::Spawn() function that does not exist yet
528  stationRef->SetAttribute(AttrShieldCharge, stationRef->GetAttribute(AttrShieldCapacity), false); // Shield Charge
529  stationRef->SetAttribute(AttrArmorDamage, EvilZero, false); // Armor Damage
530  stationRef->SetAttribute(AttrMass, iType->mass(), false); // Mass
531  stationRef->SetAttribute(AttrRadius, iType->radius(), false); // Radius
532  stationRef->SetAttribute(AttrVolume, iType->volume(), false); // Volume
533  stationRef->SetAttribute(AttrCapacity, iType->capacity(), false); // Capacity
534  return stationRef;
535  } else if (iType->groupID() == EVEDB::invGroups::Station_Services) {
536  // this should never hit...throw error
537  codelog(INV__ERROR, "II::Spawn called for unhandled item type %u, cat %u in locID: %u.", iType->id(), iType->categoryID(), data.locationID);
538  }
539  } break;
545  or (iType->groupID() == EVEDB::invGroups::Mission_Container)) {
546  return CargoContainer::Spawn(data);
547  } else if (iType->groupID() == EVEDB::invGroups::Wreck) {
548  return WreckContainer::Spawn(data);
549  } else if (iType->groupID() == EVEDB::invGroups::Force_Field) {
550  // Spawn force field item in TEMP_ENTITY_ID range and does NOT save Force_Field to db
552  return InventoryItem::SpawnItem(itemID, data);
553  } else {
554  // Spawn new Celestial Object
555  return CelestialObject::Spawn(data);
556  }
557  } break;
558  }
559 
560  // log error and return nullref for items not handled here
561  _log(ITEM__WARNING, "II::Spawn item not handled - type %u, grp %u, cat %u.", iType->id(), iType->groupID(), iType->categoryID());
562  return InventoryItemRef(nullptr);
563 }
564 
565 // item manipulation methods
567 {
568  // make error for invalid inventory?
569  // only happens on char creation, when system isnt loaded yet, so we really dont need it.
570  if (pInventory != nullptr)
571  pInventory->AddItem(iRef);
572 }
573 
575 {
576  if (pInventory != nullptr) // just in case
577  pInventory->RemoveItem(iRef);
578 }
579 
581 {
582  m_delete = true;
583 
584  // get out of client's sight.
585  if (!IsNPCCorp(m_data.ownerID) and (m_data.ownerID > 1)) {
586  Move(locJunkyard, flagNone, true);
587  } else {
588  // remove from current container's inventory
591  if (iRef.get() != nullptr) {
593  } else {
594  _log(ITEM__ERROR, "II::Delete() - Cant find location %u containing %s.", m_data.locationID, m_data.name.c_str());
595  }
596  }
597  }
598 
599  // remove from DB
601  // remove from factory cache
602  sItemFactory.RemoveItem(m_itemID);
603 }
604 
606 {
607  InventoryItemRef iRef = sItemFactory.GetItemContainer(m_itemID, false);
608  if (iRef.get() != nullptr) {
609  // verify this gets inventory containing item before trying to manipulate
610  //Inventory* pInv = iRef->GetMyInventory();
611  if (pInventory != nullptr) // this isnt right...this item wont be in it's own inventory
613  }
614  if (pAttributeMap != nullptr) // should never be null, but just in case
616 
617  //notify about the changes.
618  std::map<int32, PyRep *> changes;
620  SendItemChange(m_data.ownerID, changes); //changes is consumed
622 
623  //take ourself out of the DB
625  //delete ourselves from factory cache
626  sItemFactory.RemoveItem(m_itemID);
627 }
628 
629 void InventoryItem::Rename(std::string name)
630 {
631  m_data.name = name;
632  SaveItem();
633 
634  PyList* list = new PyList();
635  list->AddItem(new PyInt(m_itemID));
636  list->AddItem(new PyString(name));
637  list->AddItem(new PyFloat(0));
638  list->AddItem(new PyFloat(0));
639  list->AddItem(new PyFloat(0));
640  PyTuple* tuple = new PyTuple(2);
641  tuple->SetItem(0, new PyString("evelocations"));
642  tuple->SetItem(1, list);
643 
644  // get owner
646  // this will be the most-used case
647  Client* pClient = sEntityList.FindClientByCharID(m_data.ownerID);
648  if (pClient == nullptr)
649  return; // make error here?
650  if (pClient->IsDocked()) {
651  pClient->SendNotification("OnCfgDataChanged", "charid", &tuple, false); //unsequenced.
652  } else { // client in space. sent update to all clients in bubble
653  pClient->GetShipSE()->SysBubble()->BubblecastSendNotification("OnCfgDataChanged", "solarsystemid", &tuple, false);
654  }
655  } else if (IsPlayerCorp(m_data.ownerID)) {
656  // bcast to all online corp members
657  if (sDataMgr.IsStation(m_data.locationID)) {
658  sEntityList.CorpNotify(m_data.ownerID, Notify::Types::ItemUpdateStation, "OnCfgDataChanged", "charid", tuple);
659  } else {
660  sEntityList.CorpNotify(m_data.ownerID, Notify::Types::ItemUpdateSystem, "OnCfgDataChanged", "solarsystemid", tuple);
661  }
662  }
663 
665 }
666 
667 void InventoryItem::Donate(uint32 new_owner/*ownerSystem*/, uint32 new_location/*locTemp*/, EVEItemFlags new_flag/*flagNone*/, bool notify/*true*/)
668 {
669  if (!IsValidOwner(new_owner)) {
670  _log(ITEM__ERROR, "II::Donate() - %u is invalid owner", new_owner);
671  return;
672  }
673 
674  if (!IsValidLocation(new_location)) {
675  _log(ITEM__ERROR, "II::Donate() - %u is invalid location", new_location);
676  return;
677  }
678 
679  if ((new_location == m_data.locationID) and (new_flag == m_data.flag) and (new_owner == m_data.ownerID) and !notify)
680  return; //nothing to do...
681 
682  InventoryItemRef iRef(nullptr);
683  uint32 old_location = m_data.locationID, old_owner = m_data.ownerID;
684  EVEItemFlags old_flag = m_data.flag;
685 
686  if ((new_location != m_data.locationID) // diff container
687  or ((new_location == m_data.locationID) // or same container
688  and (new_flag != m_data.flag))) { // but different flag
689  // remove from current location
691  iRef = sItemFactory.GetItem(m_data.locationID);
692  if (iRef.get() != nullptr) {
693  iRef->RemoveItem(InventoryItemRef(this));
694  } else {
695  _log(ITEM__ERROR, "II::Donate() - Cant find location %u containing %s.", m_data.locationID, m_data.name.c_str());
696  }
697  }
698  }
699 
700  // update data
701  m_data.flag = new_flag;
702  m_data.ownerID = new_owner;
703  m_data.locationID = new_location;
704 
705  if ((old_location != m_data.locationID) // diff container
706  or ((old_location == m_data.locationID) // or same container
707  and (old_flag != m_data.flag))) { // but different flag
708  // add to new location
709  iRef = sItemFactory.GetItem(m_data.locationID);
710  if (iRef.get() != nullptr) {
711  iRef->AddItem(InventoryItemRef(this));
712  } else {
713  _log(ITEM__ERROR, "II::Donate() - Cant find location %u containing %s.", m_data.locationID, m_data.name.c_str());
714  }
715  }
716 
717  SaveItem();
718  //ItemDB::UpdateLocation(m_itemID, m_data.locationID, m_data.flag);
719 
720  // changes are cleared after sending, so make 2 sets to send to old owner and new owner
721  if (notify) {
722  std::map<int32, PyRep *> changes;
723  if (new_flag != old_flag)
724  changes[Inv::Update::Flag] = new PyInt(old_flag);
725  if (new_owner != old_owner)
726  changes[Inv::Update::Owner] = new PyInt(old_owner);
727  if (new_location != old_location)
728  changes[Inv::Update::Location] = new PyInt(old_location);
729  SendItemChange(m_data.ownerID, changes);
730  if (new_owner != old_owner) {
731  if (new_flag != old_flag)
732  changes[Inv::Update::Flag] = new PyInt(old_flag);
733  if (new_owner != old_owner)
734  changes[Inv::Update::Owner] = new PyInt(old_owner);
735  if (new_location != old_location)
736  changes[Inv::Update::Location] = new PyInt(old_location);
737  SendItemChange(old_owner, changes);
738  }
739  }
740 }
741 
742 void InventoryItem::Move(uint32 new_location/*locTemp*/, EVEItemFlags new_flag/*flagNone*/, bool notify/*false*/) {
743  if ((new_location == m_data.locationID) and (new_flag == m_data.flag) and !notify)
744  return; //nothing to do...
745 
746  InventoryItemRef iRef(nullptr);
747  uint32 old_location = m_data.locationID;
748  EVEItemFlags old_flag = m_data.flag;
749 
750  if ((new_location != m_data.locationID) // diff container
751  or ((new_location == m_data.locationID) // or same container
752  and (new_flag != m_data.flag))) { // but different flag
753  // remove from current location
755  iRef = sItemFactory.GetItem(m_data.locationID);
756  if (iRef.get() != nullptr) {
757  iRef->RemoveItem(InventoryItemRef(this));
758  } else {
759  _log(ITEM__ERROR, "II::Move() - Cant find location %u containing %s.", m_data.locationID, m_data.name.c_str());
760  }
761  }
762  }
763 
764  // update data
765  m_data.flag = new_flag;
766  m_data.locationID = new_location;
767 
768  if ((old_location != m_data.locationID) // diff container
769  or ((old_location == m_data.locationID) // or same container
770  and (old_flag != m_data.flag))) { // but different flag
771  // add to new location
773  iRef = sItemFactory.GetItem(m_data.locationID);
774  if (iRef.get() != nullptr) {
775  iRef->AddItem(InventoryItemRef(this));
776  } else {
777  _log(ITEM__ERROR, "II::Move() - Cant find location %u to add %s.", m_data.locationID, m_data.name.c_str());
778  }
779  }
780  }
781 
783  return;
784 
787 
788  //notify about the changes.
789  if (notify) {
790  std::map<int32, PyRep *> changes;
791  if (m_data.locationID != old_location)
792  changes[Inv::Update::Location] = new PyInt(old_location);
793  if (m_data.flag != old_flag)
794  changes[Inv::Update::Flag] = new PyInt(old_flag);
795  SendItemChange(m_data.ownerID, changes); //changes is consumed
796  }
797 }
798 
799 InventoryItemRef InventoryItem::Split(int32 qty/*0*/, bool notify/*true*/, bool silent/*false*/) {
800  if (qty < 1) {
801  _log(ITEM__ERROR, "II::Split() - %s(%u): Asked to split into a chunk of %i", m_data.name.c_str(), m_itemID, qty);
802  return InventoryItemRef(nullptr);
803  }
804  if (!AlterQuantity(-qty, notify)) {
805  _log(ITEM__ERROR, "II::Split() - %s(%u): Failed to remove quantity of %i during split.", m_data.name.c_str(), m_itemID, qty);
806  return InventoryItemRef(nullptr);
807  }
808 
809  ItemData idata(m_type.id(), m_data.ownerID, locTemp, m_data.flag, qty);
810  InventoryItemRef iRef = sItemFactory.SpawnItem(idata);
811  if (iRef.get() == nullptr)
812  return InventoryItemRef(nullptr); // couldnt spawn new item...we'll get over it.
813 
814  if (silent)
815  return iRef;
816 
817  iRef->Move(m_data.locationID, m_data.flag, notify);
818  return iRef;
819 }
820 
822  if ((locID == m_data.locationID) and (flag == m_data.flag))
823  return; //nothing to do...
824 
825  InventoryItemRef iRef(nullptr);
826  uint32 old_location = m_data.locationID;
827  EVEItemFlags old_flag = m_data.flag;
828 
829  // update data
830  m_data.flag = flag;
831  m_data.locationID = locID;
832 
833  if ((old_location != m_data.locationID) // diff container
834  or ((old_location == m_data.locationID) // or same container
835  and (old_flag != m_data.flag))) { // but different flag
836  // add to new location
838  iRef = sItemFactory.GetItem(m_data.locationID);
839  if (iRef.get() != nullptr) {
840  iRef->AddItem(InventoryItemRef(this));
841  } else {
842  _log(ITEM__ERROR, "II::Relocate(): new location %u not found for %s.",
843  m_data.locationID, m_data.name.c_str());
844  }
845  }
846  }
847 
850 
851  //notify about the changes.
852  std::map<int32, PyRep *> changes;
853  if (m_data.locationID != old_location)
854  changes[Inv::Update::Location] = new PyInt(old_location);
855  if (m_data.flag != old_flag)
856  changes[Inv::Update::Flag] = new PyInt(old_flag);
857  SendItemChange(m_data.ownerID, changes); //changes is consumed
858 }
859 
860 bool InventoryItem::Merge(InventoryItemRef to_merge, int32 qty/*0*/, bool notify/*true*/) {
861  if (to_merge.get() == nullptr)
862  return false;
863 
864  if (m_data.singleton or to_merge->isSingleton())
865  throw CustomError ("You cannot stack assembled items.");
866 
867  if (m_type.id() != to_merge->typeID()) {
868  _log(ITEM__WARNING, "II::Merge() - %s (%u): Asked to merge with %s (%u).", m_data.name.c_str(), m_itemID, to_merge->name(), to_merge->itemID());
869  return false;
870  }
871 
872  if (qty < 1)
873  qty = to_merge->quantity();
874 
875  // AlterQuantity will delete items with qty < 1
876  if (!to_merge->AlterQuantity(-qty, notify)) {
877  _log(ITEM__ERROR, "II::Merge() - %s (%u): Failed to remove quantity %u.", to_merge->name(), to_merge->itemID(), qty);
879  Client* pClient = sEntityList.FindClientByCharID(m_data.ownerID);
880  if (pClient != nullptr)
881  pClient->SendErrorMsg("Internal Server Error. Ref: ServerError 63138");
882  }
883  return false;
884  }
885 
886  if (!AlterQuantity(qty, notify)) {
887  _log(ITEM__ERROR, "%s (%u): Failed to add quantity %u.", m_data.name.c_str(), m_itemID, qty);
889  Client* pClient = sEntityList.FindClientByCharID(m_data.ownerID);
890  if (pClient != nullptr)
891  pClient->SendErrorMsg("Internal Server Error. Ref: ServerError 63238");
892  }
893  return false;
894  }
895 
896  return true;
897 }
898 
900 {
901  // get existing type in cargo
903  if (iRef.get() == nullptr) {
904  Move(pShip->itemID(), flag, true);
905  return;
906  }
907  // fix for elusive error when using IB::Add() to remove loaded modules (charge trying to add to module item)
908  if (iRef->typeID() != m_type.id()) {
909  Move(pShip->itemID(), flag, true);
910  return;
911  }
912 
913  // if either item is assembled, just move item (merge will throw on assembled items)
914  if (iRef->isSingleton() or m_data.singleton) {
915  Move(pShip->itemID(), flag, true);
916  return;
917  }
918  // here we 'merge' with stack already in cargo
919  // if it dont work, just move item.
920  if (!iRef->Merge(InventoryItemRef(this)))
921  Move(pShip->itemID(), flag, true);
922 }
923 
924 bool InventoryItem::AlterQuantity(int32 qty, bool notify/*false*/) {
925  if (qty == 0) {
926  _log(ITEM__WARNING, "II::AlterQuantity() - Sent qty=0 for %s(%u)", m_data.name.c_str(), m_itemID);
927  EvE::traceStack();
928  return false;
929  }
930 
931  int32 new_qty = m_data.quantity + qty;
932  if (new_qty < 0) {
933  codelog(ITEM__ERROR, "II::AlterQuantity() - %s(%u): Tried to remove %i from stack of %i for ownerID %u.", \
934  m_data.name.c_str(), m_itemID, qty, m_data.quantity, m_data.ownerID);
935  // make player error msg here.....
936  Delete();
937  return false;
938  }
939 
940  return SetQuantity(new_qty, notify);
941 }
942 
943 bool InventoryItem::SetQuantity(int32 qty, bool notify/*false*/, bool deleteOnZero/*true*/) {
944  //if an object is singleton, there is only one, and it shouldn't be able to alter qty
945  if (m_data.singleton) {
946  _log(ITEM__ERROR, "II::SetQuantity() - %s(%u): Failed to set quantity %i; the items singleton bit is set", m_data.name.c_str(), m_itemID, qty);
947  // make player error msg here.....
948  return false;
949  }
950  int32 old_qty = m_data.quantity;
951  m_data.quantity = qty;
952 
953  if (m_data.quantity > maxEveItem) {
954  codelog(ITEM__ERROR, "II::SetQuantity() - %s(%u): quantity overflow", m_data.name.c_str(), m_itemID);
957  Client* pClient = sEntityList.FindClientByCharID(m_data.ownerID);
958  if (pClient != nullptr)
959  pClient->SendInfoModalMsg("Your %s has reached quantity limits of this server.<br>If you try to add any more to this stack, you will lose items. This is your only warning.", m_data.name.c_str());
960  }
961  }
962 
964  std::map<int32, PyRep *> changes;
965  changes[Inv::Update::StackSize] = new PyInt(old_qty);
966  SendItemChange(m_data.ownerID, changes); //changes is consumed
967  }
968 
969  // how are we gonna do modules owned by corp here???
971  if (IsCharacterID(m_data.ownerID)/* or IsPlayerCorp(m_data.ownerID)*/) {
972  Client* pClient = sEntityList.FindClientByCharID(m_data.ownerID);
973  SetAttribute(AttrQuantity, m_data.quantity, pClient == nullptr ? notify : pClient->IsInSpace());
974  }
975 
976  if (m_data.quantity < 1) {
977  if (deleteOnZero)
978  Delete();
979  return true;
980  }
981 
982  if (sConfig.world.saveOnUpdate)
983  SaveItem();
984 
985  return true;
986 }
987 
988 bool InventoryItem::SetFlag(EVEItemFlags flag, bool notify/*false*/) {
989  if (m_data.flag == flag)
990  return true;
991 
992  EVEItemFlags old_flag = m_data.flag;
993  m_data.flag = flag;
994 
996 
997  if (notify) {
998  std::map<int32, PyRep *> changes;
999  changes[Inv::Update::Flag] = new PyInt(old_flag);
1000  SendItemChange(m_data.ownerID, changes); //changes is consumed
1001  }
1002 
1003  return true;
1004 }
1005 
1006 bool InventoryItem::ChangeSingleton(bool singleton, bool notify/*false*/) {
1007  if (singleton == m_data.singleton)
1008  return true; //nothing to do...
1009 
1010  bool old_singleton = m_data.singleton;
1011  m_data.singleton = singleton;
1012 
1013  //verify quantity is -1 for singletons
1014  if (m_data.singleton)
1015  if (m_data.quantity > 1) {
1016  _log(ITEM__WARNING, "%s(%u) is changing singleton to %s and qty is currently %u", \
1017  m_data.name.c_str(), m_itemID, singleton?"On":"Off");
1018  m_data.quantity = -1;
1019  }
1020 
1021  if (sConfig.world.saveOnUpdate)
1022  SaveItem();
1023 
1024  if (notify) {
1025  std::map<int32, PyRep *> changes;
1026  changes[Inv::Update::Singleton] = new PyInt(old_singleton);
1027  SendItemChange(m_data.ownerID, changes); //changes is consumed
1028  }
1029 
1030  // must update volume when singleton (packaged state) changes for (mostly) ship items.
1032  return true;
1033 }
1034 
1035 void InventoryItem::ChangeOwner(uint32 new_owner, bool notify/*false*/) {
1036  if (new_owner == m_data.ownerID)
1037  return; //nothing to do...
1038 
1039  uint32 old_owner = m_data.ownerID;
1040  m_data.ownerID = new_owner;
1041 
1042  if (sConfig.world.saveOnUpdate)
1043  SaveItem();
1044 
1045  //notify about the changes.
1046  if (notify) {
1047  std::map<int32, PyRep *> changes;
1048  //send the notify to the new owner.
1049  changes[Inv::Update::Owner] = new PyInt(old_owner);
1050  SendItemChange(new_owner, changes); //changes is consumed
1051  //also send the notify to the old owner.
1052  changes[Inv::Update::Owner] = new PyInt(old_owner);
1053  SendItemChange(old_owner, changes); //changes is consumed
1054  }
1055 }
1056 
1057 //contents of changes are consumed and cleared
1058 void InventoryItem::SendItemChange(uint32 toID, std::map<int32, PyRep *> &changes) {
1059  if (IsNPCCorp(toID) or (toID == 1) or IsFaction(toID)) //IsValidOwner()
1060  return;
1061  if (sConsole.IsShutdown())
1062  return;
1063  if (changes.empty())
1064  return;
1065 
1066  NotifyOnItemChange change;
1067  change.itemRow = GetItemRow();
1068  change.changes = changes;
1069  PyTuple *tmp = change.Encode();
1070 
1071  if (is_log_enabled(ITEM__CHANGE)) {
1072  _log(ITEM__CHANGE, "Sending Item changes for %s(%u)", m_data.name.c_str(), m_itemID);
1073  tmp->Dump(ITEM__CHANGE, " ");
1074  }
1075 
1076  //TODO: figure out the appropriate list of interested people...
1077  if (IsCharacterID(toID)) {
1078  Client* pClient = sEntityList.FindClientByCharID(toID);
1079  if (pClient == nullptr)
1080  return;
1081  if (pClient->IsCharCreation())
1082  return;
1083  //if (IsShipItem()) //(pClient->IsBoard())
1084  // pClient->SendNotification("OnItemsChanged", "charid", &tmp, false); //unsequenced. <<-- this is called for multiple items
1085  //else
1086  pClient->SendNotification("OnItemChange", "clientID", &tmp, false); //unsequenced. <<-- this is for single items
1087  } else if (IsPlayerCorp(toID)) {
1088  if (sDataMgr.IsStation(m_data.locationID)) {
1089  sEntityList.CorpNotify(toID, Notify::Types::ItemUpdateStation, "OnItemChange","*stationid&corpid", tmp);
1090  } else {
1091  sEntityList.CorpNotify(toID, Notify::Types::ItemUpdateSystem, "OnItemChange","corpid", tmp);
1092  }
1093  }
1094 }
1095 
1097 {
1098  ItemData data(m_data.name.c_str(),
1099  m_type.id(),
1100  m_data.ownerID,
1102  m_data.flag,
1104  m_data.singleton,
1105  m_data.quantity,
1106  m_data.position,
1107  customInfo().c_str()
1108  );
1109 
1110  ItemDB::SaveItem(m_itemID, data);
1111  // item attributes are saved in ItemFactory.cpp:96 (save loop on shutdown for loaded items)
1112  // make call here for items saved after *some* change
1113  pAttributeMap->Save();
1114 }
1115 
1118 }
1119 
1121  DBRowDescriptor* header = new DBRowDescriptor();
1122  header->AddColumn("instanceID", DBTYPE_I8);
1123  header->AddColumn("online", DBTYPE_BOOL);
1124  header->AddColumn("damage", DBTYPE_R8);
1125  header->AddColumn("charge", DBTYPE_R8);
1126  header->AddColumn("skillPoints", DBTYPE_I4);
1127  header->AddColumn("armorDamage", DBTYPE_R8);
1128  header->AddColumn("shieldCharge", DBTYPE_R8);
1129  header->AddColumn("incapacitated", DBTYPE_BOOL);
1130  PyPackedRow* row = new PyPackedRow(header);
1131  GetItemStatusRow(row);
1132  return row;
1133 }
1134 
1136  into->SetField("instanceID", new PyLong(m_itemID ));
1137  into->SetField("online", new PyBool((HasAttribute(AttrOnline) ? GetAttribute(AttrOnline).get_bool() : false) ));
1138  into->SetField("damage", new PyFloat((HasAttribute(AttrDamage) ? GetAttribute(AttrDamage).get_float() : 0) ));
1139  into->SetField("charge", new PyFloat((HasAttribute(AttrCapacitorCharge) ? GetAttribute(AttrCapacitorCharge).get_float() : 0) ));
1140  into->SetField("skillPoints", new PyInt((HasAttribute(AttrSkillPoints) ? GetAttribute(AttrSkillPoints).get_uint32() : 0) ));
1141  into->SetField("armorDamage", new PyFloat((HasAttribute(AttrArmorDamageAmount) ? GetAttribute(AttrArmorDamageAmount).get_float() : 0.0) ));
1142  into->SetField("shieldCharge", new PyFloat((HasAttribute(AttrShieldCharge) ? GetAttribute(AttrShieldCharge).get_float() : 0.0) ));
1143  into->SetField("incapacitated", new PyBool((HasAttribute(AttrIsIncapacitated) ? GetAttribute(AttrIsIncapacitated).get_bool() : false) ));
1144 }
1145 
1146 /* charge info for specific module */
1148  DBRowDescriptor* header = new DBRowDescriptor();
1149  header->AddColumn("instanceID", DBTYPE_I8);
1150  header->AddColumn("flagID", DBTYPE_I2);
1151  header->AddColumn("typeID", DBTYPE_I4);
1152  //header->AddColumn("quantity", DBTYPE_I4);
1153  PyPackedRow* row = new PyPackedRow(header);
1154  GetChargeStatusRow(shipID, row);
1155  return row;
1156 }
1157 
1159  into->SetField("instanceID", new PyLong(shipID)); // locationID
1160  into->SetField("flagID", new PyInt(m_data.flag));
1161  into->SetField("typeID", new PyInt(m_type.id()));
1162 }
1163 
1165 {
1166  PyPackedRow* row = new PyPackedRow( sDataMgr.CreateHeader() );
1167  GetItemRow(row);
1168  return row;
1169 }
1170 
1172 {
1173  int32 qty = (m_data.singleton ? -1 : m_data.quantity);
1175  if (sItemFactory.GetBlueprint(m_itemID)->copy())
1176  qty = -2;
1177 
1178  into->SetField("itemID", new PyLong(m_itemID));
1179  into->SetField("typeID", new PyInt(m_type.id()));
1180  into->SetField("ownerID", new PyInt(m_data.ownerID));
1181  into->SetField("locationID", new PyInt(m_data.locationID));
1182  into->SetField("flagID", new PyInt(m_data.flag));
1183  into->SetField("groupID", new PyInt(type().groupID()));
1184  into->SetField("categoryID", new PyInt(type().categoryID()));
1185  into->SetField("quantity", new PyInt(qty));
1186  /*
1187  if (m_type.categoryID() == EVEDB::invCategories::Blueprint) {
1188  if (sItemFactory.GetBlueprint(m_itemID)->copy()) {
1189  into->SetField("stacksize", new PyInt(1));
1190  into->SetField("singleton", new PyInt(2));
1191  } else {
1192  into->SetField("stacksize", new PyInt(m_data.singleton? -1 : m_data.quantity));
1193  into->SetField("singleton", new PyInt(m_data.singleton?1:0));
1194  }
1195  } else {
1196  into->SetField("stacksize", new PyInt(m_data.singleton? -1 : m_data.quantity));
1197  into->SetField("singleton", new PyInt(m_data.singleton?1:0));
1198  }
1199  */
1200  // customInfo is actually used in client (but i dont think it's a string)
1201  //if const.ixLocationID in change and item.customInfo == logConst.eventUndock:
1202  into->SetField("customInfo", new PyString(m_data.customInfo));
1203 }
1204 
1205 bool InventoryItem::Populate(Rsp_CommonGetInfo_Entry& result )
1206 {
1209  //make sure trash data is removed from &result
1210  result.attributes.clear();
1211  PySafeDecRef(result.itemID);
1212  PySafeDecRef(result.invItem);
1213  result.time = GetFileTimeNow();
1214  // this is only to display item name in logs. client ignores it
1215  result.description = m_data.name;
1216 
1217  // updated charge info...again -allan 8Jan20
1219  and IsFittingSlot(m_data.flag)) {
1220  PyTuple* tuple = new PyTuple(3);
1221  tuple->SetItem(0, new PyInt(m_data.locationID));
1222  tuple->SetItem(1, new PyInt(m_data.flag));
1223  tuple->SetItem(2, new PyInt(m_type.id()));
1224  result.itemID = tuple;
1225  result.invItem = PyStatic.NewNone();
1226  for (AttrMapItr itr = pAttributeMap->begin(), end = pAttributeMap->end(); itr != end; ++itr)
1227  result.attributes[(*itr).first] = (*itr).second.GetPyObject();
1228  return true;
1229  }
1230 
1231  result.itemID = new PyInt(m_itemID);
1232  result.invItem = GetItemRow();
1233 
1236  result.attributes[AttrSkillPoints] = GetAttribute(AttrSkillPoints).GetPyObject();
1237  result.attributes[AttrSkillLevel] = GetAttribute(AttrSkillLevel).GetPyObject();
1238  return true;
1239  }
1240  //} else if (m_type.id() == 51) { // for vouchers
1241  // result.description = m_data.name;
1242 
1244  EntityEffectState es;
1245  es.env_itemID = m_itemID;
1246  es.env_charID = m_data.ownerID;
1247  es.env_shipID = m_data.locationID;
1248  es.env_target = m_data.locationID;
1249  es.env_other = PyStatic.NewNone();
1250  es.env_area = PyStatic.NewNone();
1251  es.env_effectID = 16;
1253  // on login, this is current time
1254  if (IsCharacterID(m_itemID)) {
1255  es.startTime = GetFileTimeNow() - EvE::Time::Minute; // m_timestamp
1256  } else {
1257  es.startTime = GetFileTimeNow() - EvE::Time::Minute; // GetAttribute(AttrStartTime).get_int();
1258  }
1259  es.duration = -1;
1260  es.repeat = 0;
1261  es.randomSeed = PyStatic.NewNone();
1262  result.activeEffects[es.env_effectID] = es.Encode();
1263  }
1264 
1265  for (AttrMapItr itr = pAttributeMap->begin(), end = pAttributeMap->end(); itr != end; ++itr) {
1266  //localization.GetByLabel('UI/Fitting/FittingWindow/WarpSpeed', distText=util.FmtDist(max(1.0, bws) * wsm * 3 * const.AU, 2))
1267  if ((*itr).first == AttrWarpSpeedMultiplier) {
1268  result.attributes[AttrWarpSpeedMultiplier] = new PyFloat((*itr).second.get_float() /3);
1269  } else {
1270  result.attributes[(*itr).first] = (*itr).second.GetPyObject();
1271  }
1272  }
1273 
1274  return true;
1275 }
1276 
1278 {
1279  PyList* itemInfo = new PyList();
1280  itemInfo->AddItem(GetItemRow());
1281  return itemInfo;
1282 }
1283 
1285 { // called from dogmaBound at least once (usually 2x) on every weapon update
1286  Rsp_ItemGetInfo result;
1287  if (!Populate(result.entry))
1288  return nullptr;
1289  return result.Encode();
1290 }
1291 
1292 void InventoryItem::SetCustomInfo(const char *ci) {
1293  if (ci != nullptr) {
1294  m_data.customInfo = ci;
1295  } else {
1296  m_data.customInfo = "";
1297  }
1298 
1299  if (sConfig.world.saveOnUpdate)
1300  SaveItem();
1301 }
1302 
1304 {
1305  if (m_data.position == pos)
1306  return;
1307  /*
1308  if (pos.isZero() and sDataMgr.IsSolarSystem(m_data.locationID)) {
1309  _log(DESTINY__TRACE, "II::SetPosition() - %s(%u) point is zero", m_data.name.c_str(), m_itemID);
1310  EvE::traceStack();
1311  } */
1312 
1313  m_data.position = pos;
1314  _log(ITEM__RELOCATE, "%s(%u) Relocating to %.2f, %.2f, %.2f.", m_data.name.c_str(), \
1316 }
1317 
1318 void InventoryItem::SetAttribute(uint16 attrID, float num, bool notify/*true*/)
1319 {
1320  EvilNumber eNum(num);
1321  pAttributeMap->SetAttribute(attrID, eNum, notify);
1322 }
1323 
1324 void InventoryItem::SetAttribute(uint16 attrID, double num, bool notify/*true*/)
1325 {
1326  EvilNumber eNum(num);
1327  pAttributeMap->SetAttribute(attrID, eNum, notify);
1328 }
1329 
1330 void InventoryItem::SetAttribute(uint16 attrID, EvilNumber num, bool notify/*true*/)
1331 {
1332  pAttributeMap->SetAttribute(attrID, num, notify);
1333 }
1334 
1335 void InventoryItem::SetAttribute(uint16 attrID, int num, bool notify/*true*/)
1336 {
1337  EvilNumber eNum(num);
1338  pAttributeMap->SetAttribute(attrID, eNum, notify);
1339 }
1340 
1341 void InventoryItem::SetAttribute(uint16 attrID, int64 num, bool notify/*true*/)
1342 {
1343  EvilNumber eNum(num);
1344  pAttributeMap->SetAttribute(attrID, eNum, notify);
1345 }
1346 
1347 void InventoryItem::SetAttribute(uint16 attrID, uint32 num, bool notify/*true*/)
1348 {
1349  EvilNumber eNum(num);
1350  pAttributeMap->SetAttribute(attrID, eNum, notify);
1351 }
1352 
1353 void InventoryItem::MultiplyAttribute(uint16 attrID, EvilNumber num, bool notify/*false*/)
1354 {
1355  pAttributeMap->MultiplyAttribute(attrID, num, notify);
1356 }
1357 
1359 {
1360  if (m_data.singleton)
1361  return m_type.volume();
1362 
1363  // these volumes are hard-coded in client.
1364  switch (m_type.groupID()) {
1365  case 29: //Capsule
1366  case 31: //Shuttle
1367  case 1022: { //Prototype Exploration Ship
1368  return 500;
1369  }
1370  case 12: //Cargo Container
1371  case 306: //Spawn Container
1372  case 340: //Secure Cargo Container
1373  case 448: //Audit Log Secure Container
1374  case 649: //Freight Container
1375  case 952: { //Mission Container
1376  return 1000;
1377  }
1378  case 25: //Frigate
1379  case 324: //Assault Ship
1380  case 830: //Covert Ops
1381  case 893: //Electronic Attack Ship
1382  case 831: //Interceptor
1383  case 834: { //Stealth Bomber
1384  return 2500;
1385  }
1386  case 543: //Exhumer
1387  case 463: { //Mining Barge
1388  return 3750;
1389  }
1390  case 541: //Interdictor
1391  case 420: //Destroyer
1392  case 963: { //Strategic Cruiser
1393  return 5000;
1394  }
1395  case 906: //Combat Recon Ship
1396  case 26: //Cruiser
1397  case 833: //Force Recon Ship
1398  case 358: //Heavy Assault Ship
1399  case 894: //Heavy Interdictor
1400  case 832: { //Logistics
1401  return 10000;
1402  }
1403  case 419: //Battlecruiser
1404  case 540: { //Command Ship
1405  return 15000;
1406  }
1407  case 28: //Industrial
1408  case 380: { //Transport Ship
1409  return 20000;
1410  }
1411  case 27: //Battleship
1412  case 900: //Marauder
1413  case 898: //Black Ops
1414  case 381: { //Elite Battleship
1415  return 50000;
1416  }
1417  case 941: { //Industrial Command Ship
1418  return 500000;
1419  }
1420  case 883: //Capital Industrial Ship
1421  case 547: //Carrier
1422  case 485: //Dreadnought
1423  case 513: //Freighter
1424  case 902: //Jump Freighter
1425  case 659: { //Supercarrier
1426  return 1000000;
1427  }
1428  case 30: { //Titan
1429  return 10000000;
1430  }
1431  }
1432 
1433  // return basic volume for non-ship or non-container objects
1434  return m_type.volume();
1435 }
1436 
1438 {
1439  EvilNumber need = 0, has = 0;
1440  uint16 attr = 182, skill = 277;
1441  for (int8 i = 0; i < 3; ++i, ++attr, ++skill) {
1442  if (refItem->HasAttribute(attr, need) and HasAttribute(skill, has)) {
1443  if (need > has)
1444  return false;
1445  }
1446  }
1447  if (refItem->HasAttribute(1285, need) and HasAttribute(1286, has)) {
1448  if (need > has)
1449  return false;
1450  }
1451 
1452  attr = 1289; skill = 1287;
1453  for (int8 i = 0; i < 2; ++i, ++attr, ++skill) {
1454  if (refItem->HasAttribute(attr, need) and HasAttribute(skill, has)) {
1455  if (need > has)
1456  return false;
1457  }
1458  }
1459  // all skill requirement checks passed.
1460  return true;
1461 }
1462 
1463 // new effects system -allan 4Feb17
1465 {
1466  m_modifiers.emplace(data.math, data);
1467 }
1468 
1470 {
1471  switch (data.math) {
1472  case FX::Math::PreMul: data.math = FX::Math::PreDiv; break;
1473  case FX::Math::PreDiv: data.math = FX::Math::PreMul; break;
1474  case FX::Math::ModAdd: data.math = FX::Math::ModSub; break;
1475  case FX::Math::ModSub: data.math = FX::Math::ModAdd; break;
1476  case FX::Math::PostMul: data.math = FX::Math::PostDiv; break;
1477  case FX::Math::PostDiv: data.math = FX::Math::PostMul; break;
1481  }
1482  m_modifiers.emplace(data.math, data);
1483 }
1484 
1486 {
1487  _log(EFFECTS__TRACE, "Clearing modifier map for %s", m_data.name.c_str());
1488  m_modifiers.clear();
1489 }
1490 
1492  _log(EFFECTS__TRACE, "Resetting attrib map for %s", m_data.name.c_str());
1493  pAttributeMap->Load(true);
1494 }
bool SetQuantity(int32 qty, bool notify=false, bool deleteOnZero=true)
#define sConfig
A macro for easier access to the singleton.
PyPackedRow * GetItemRow() const
static bool SaveItem(uint32 itemID, const ItemData &data)
Definition: ItemDB.cpp:219
RefPtr< StationItem > StationItemRef
Definition: ItemRef.h:66
virtual InventoryItemRef Split(int32 qty=0, bool notify=true, bool silent=false)
#define IsNPCCorp(itemID)
Definition: EVE_Defines.h:238
void Donate(uint32 new_owner=ownerSystem, uint32 new_location=locTemp, EVEItemFlags new_flag=flagNone, bool notify=true)
void SendNotification(const PyAddress &dest, EVENotificationStream &noti, bool seq=true)
Definition: Client.cpp:2245
static ShipItemRef Spawn(ItemData &data)
Definition: Ship.cpp:51
void SetAttribute(uint16 attrID, EvilNumber &num, bool notify=true)
void SendItemChange(uint32 toID, std::map< int32, PyRep * > &changes)
void SendErrorMsg(const char *fmt,...)
Definition: Client.cpp:2719
PyList * GetItemInfo() const
#define IsAsteroidID(itemID)
Definition: EVE_Defines.h:259
static InventoryItemRef SpawnItem(uint32 itemID, const ItemData &data)
#define _log(type, fmt,...)
Definition: logsys.h:124
uint16 id() const
Definition: ItemType.h:63
#define sConsole
Python string.
Definition: PyRep.h:430
ItemType m_type
uint32 locationID
Definition: ItemType.h:190
virtual bool Merge(InventoryItemRef to_merge, int32 qty=0, bool notify=true)
uint16 groupID() const
Definition: ItemType.h:64
static RefPtr< _Ty > _LoadItem(uint32 itemID, const ItemType &type, const ItemData &data)
bool IsCharCreation()
Definition: Client.h:433
bool HasAttribute(const uint16 attrID) const
bool SetFlag(EVEItemFlags flag, bool notify=false)
PyRep * GetPyObject()
converts the EvilNumber into a python object.
Definition: EvilNumber.cpp:109
#define maxEveItem
Definition: EVE_Defines.h:166
virtual void RemoveItem(InventoryItemRef iRef)
bool get_bool()
Definition: EvilNumber.cpp:157
virtual void AddItem(InventoryItemRef iRef)
static StationItemRef Load(uint32 stationID)
Definition: Station.cpp:82
SystemBubble * SysBubble()
Definition: SystemEntity.h:195
void MultiplyAttribute(uint16 attrID, EvilNumber &num, bool notify=false)
const std::string & customInfo() const
void AddItem(InventoryItemRef iRef)
Definition: Inventory.cpp:203
EVEItemFlags
Definition: EVE_Flags.h:13
A reference-counted object.
Definition: RefPtr.h:48
GPoint position
Definition: ItemType.h:192
void AddColumn(const char *name, DBTYPE type)
Definition: PyDatabase.cpp:96
void SendInfoModalMsg(const char *fmt,...)
Definition: Client.cpp:2756
static CargoContainerRef SpawnTemp(ItemData &data)
Definition: Container.cpp:87
Python floating point number.
Definition: PyRep.h:292
bool SkillCheck(InventoryItemRef refItem)
void BubblecastSendNotification(const char *notifyType, const char *idType, PyTuple **payload, bool seq=true)
static SkillRef Spawn(ItemData &data)
Definition: Skill.cpp:52
static CelestialObjectRef Spawn(ItemData &data)
Definition: Celestial.cpp:72
ItemData m_data
virtual bool _Load()
#define sEntityList
Definition: EntityList.h:208
AttrMapItr begin()
return the begin iterator of the AttributeMap
void RemoveItem(InventoryItemRef iRef)
Definition: Inventory.cpp:243
void SetPosition(const GPoint &pos)
this is a class that kinda mimics how python polymorph's numbers.
Definition: EvilNumber.h:59
PyPackedRow * GetItemStatusRow() const
const char * name()
bool IsInSpace()
Definition: Client.h:228
Python tuple.
Definition: PyRep.h:567
virtual void Relocate(uint32 locID=0, EVEItemFlags flag=flagNone)
GaFloat x
Definition: GaTypes.h:207
Advanced version of UserError that allows to send a full custom message.
Definition: PyExceptions.h:453
void Dump(FILE *into, const char *pfx) const
Dumps object to file.
Definition: PyRep.cpp:84
float capacity() const
Definition: ItemType.h:71
signed __int8 int8
Definition: eve-compat.h:45
void Move(uint32 new_location=locTemp, EVEItemFlags flag=flagNone, bool notify=false)
void GetChargeStatusRow(uint32 shipID, PyPackedRow *into) const
EvilNumber EvilZero
Definition: EvilNumber.cpp:32
uint8 categoryID() const
Definition: ItemType.h:66
void AddItem(PyRep *i)
Definition: PyRep.h:701
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
std::string customInfo
Definition: ItemType.h:194
EvilNumber GetAttribute(const uint16 attrID) const
uint16 groupID() const
signed __int32 int32
Definition: eve-compat.h:49
static ModuleItemRef Spawn(ItemData &data)
Definition: ModuleItem.cpp:27
Python boolean.
Definition: PyRep.h:323
const ItemType & type() const
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
double GetPackagedVolume()
Definition: gpoint.h:33
bool isSingleton() const
Definition: InventoryItem.h:96
Python object.
Definition: PyRep.h:826
Python object "blue.DBRowDescriptor".
Definition: PyDatabase.h:41
Inventory * pInventory
#define codelog(type, fmt,...)
Definition: logsys.h:128
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
bool IsDocked()
Definition: Client.h:229
static uint32 NewItem(const ItemData &data)
Definition: ItemDB.cpp:183
std::multimap< int8, fxData > m_modifiers
float mass() const
Definition: ItemType.h:69
AttrMap::iterator AttrMapItr
Definition: AttributeMap.h:35
uint32 locationID() const
Python integer.
Definition: PyRep.h:231
bool AlterQuantity(int32 qty, bool notify=false)
int8 math
Definition: EffectsData.h:70
static InventoryItemRef SpawnTemp(ItemData &data)
static uint32 CreateTempItemID(ItemData &data)
void SetAttribute(uint16 attrID, int num, bool notify=true)
static CargoContainerRef Spawn(ItemData &data)
Definition: Container.cpp:70
#define PyStatic
Definition: PyRep.h:1209
X * get() const
Definition: RefPtr.h:213
#define IsFittingSlot(flag)
Definition: EVE_Defines.h:355
#define IsPlayerCorp(itemID)
Definition: EVE_Defines.h:241
void ChangeOwner(uint32 new_owner, bool notify=false)
static WreckContainerRef Spawn(ItemData &data)
Definition: Container.cpp:410
uint32 quantity
Definition: ItemType.h:191
const std::string & name() const
Definition: ItemType.h:74
bool Populate(Rsp_CommonGetInfo_Entry &into)
#define IsCharacterID(itemID)
Definition: EVE_Defines.h:206
static ProbeItemRef Spawn(ItemData &data)
Definition: Probes.cpp:52
bool Load(bool reset=false)
Definition: Client.h:66
static InventoryItemRef Load(uint32 itemID)
bool ChangeSingleton(bool singleton, bool notify=false)
static InventoryItemRef Spawn(ItemData &data)
unsigned __int32 uint32
Definition: eve-compat.h:50
uint16 typeID
Definition: ItemType.h:188
void SetCustomInfo(const char *ci)
void MultiplyAttribute(uint16 attrID, EvilNumber num, bool notify=false)
EVEItemFlags flag() const
InventoryItem(uint32 _itemID, const ItemType &_type, const ItemData &_data)
EVEItemFlags flag
Definition: ItemType.h:187
static BlueprintRef Spawn(ItemData &data, EvERam::bpData &bdata)
Definition: Blueprint.cpp:71
#define IsValidOwner(itemID)
Definition: EVE_Defines.h:214
RefPtr< InventoryItem > InventoryItemRef
Definition: ItemRef.h:52
GaFloat y
Definition: GaTypes.h:207
Definition: Ship.h:46
static uint32 CreateItemID(ItemData &data)
Definition: Station.cpp:106
double GetFileTimeNow()
Definition: utils_time.cpp:84
static void UpdateLocation(uint32 itemID, uint32 locationID, EVEItemFlags flag)
Definition: ItemDB.cpp:212
#define IsTempItem(itemID)
Definition: EVE_Defines.h:333
ShipSE * GetShipSE()
Definition: Client.h:168
signed __int64 int64
Definition: eve-compat.h:51
#define IsCargoHoldFlag(flag)
Definition: EVE_Defines.h:336
static uint32 CreateItemID(ItemData &data)
static bool DeleteItem(uint32 itemID)
Definition: ItemDB.cpp:348
EvilNumber GetAttribute(const uint16 attrID) const
void AddModifier(fxData &data)
typeID Spawn an NPC with the specified type text Search for items matching the specified query() type()() itemID() copy() materialLevel()() itemID(attributeID)-Retrieves attribute value." ) COMMAND( setattr
bool SetField(uint32 index, PyRep *value)
Definition: PyRep.cpp:1031
#define IsFaction(itemID)
Definition: EVE_Defines.h:250
virtual void Rename(std::string name)
uint32 ownerID
Definition: ItemType.h:189
void traceStack(void)
Definition: misc.cpp:169
float radius() const
Definition: ItemType.h:68
float volume() const
Definition: ItemType.h:70
virtual ~InventoryItem() noexcept
bool contraband
Definition: ItemType.h:185
virtual void Delete()
PyObject * ItemGetInfo()
#define PySafeDecRef(op)
Definition: PyRep.h:61
#define sItemFactory
Definition: ItemFactory.h:165
Packed row.
Definition: PyRep.h:961
AttributeMap * pAttributeMap
int16 runs
Definition: EVE_RAM.h:110
AttrMapItr end()
return the end iterator of the AttributeMap
void RemoveModifier(fxData &data)
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
unsigned __int16 uint16
Definition: eve-compat.h:48
#define IsNPC(itemID)
Definition: EVE_Defines.h:300
InventoryItemRef GetByTypeFlag(uint32 typeID, EVEItemFlags flag) const
Definition: Inventory.cpp:444
static StructureItemRef Spawn(ItemData &data)
Definition: Structure.cpp:71
void MergeTypesInCargo(ShipItem *pShip, EVEItemFlags flag=flagNone)
std::string name
Definition: ItemType.h:193
uint16 typeID() const
uint8 categoryID() const
bool singleton
Definition: ItemType.h:186
Python list.
Definition: PyRep.h:639
GaFloat z
Definition: GaTypes.h:207
void ToVirtual(uint32 locationID)
uint32 itemID() const
Definition: InventoryItem.h:98
#define IsValidLocation(itemID)
Definition: EVE_Defines.h:209
int32 quantity() const
Definition: InventoryItem.h:97
Python long integer.
Definition: PyRep.h:261
#define sDataMgr