EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
RepairService.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: Reve
24  Rewrite: Allan
25 */
26 
27 #include "eve-server.h"
28 
29 #include "PyBoundObject.h"
30 #include "PyServiceCD.h"
31 #include "Client.h"
32 #include "packets/Repair.h"
33 #include "ship/ShipDB.h"
34 #include "station/RepairService.h"
35 #include "station/Station.h"
36 #include "system/SystemManager.h"
37 
39 : public PyBoundObject
40 {
41 public:
43 
45  : PyBoundObject(mgr),
46  m_dispatch(new Dispatcher(this)),
47  m_locationID(locationID) // this is stationID
48  {
50 
51  m_strBoundObjectName = "RepairSvcBound";
52 
53  PyCallable_REG_CALL(RepairSvcBound, GetDamageReports);
54  PyCallable_REG_CALL(RepairSvcBound, RepairItems);
55  PyCallable_REG_CALL(RepairSvcBound, DamageModules);
56  }
57  virtual ~RepairSvcBound() {
58  delete m_dispatch;
59  }
60  virtual void Release() {
61  //I hate this statement
62  delete this;
63  }
64 
65  PyCallable_DECL_CALL(GetDamageReports);
66  PyCallable_DECL_CALL(RepairItems);
67  PyCallable_DECL_CALL(DamageModules);
68 
69 protected:
70  Dispatcher* const m_dispatch;
71 
73 };
74 
76 
78 : PyService(mgr, "repairSvc"),
79  m_dispatch(new Dispatcher(this))
80 {
81  _SetCallDispatcher(m_dispatch);
82 
83  PyCallable_REG_CALL(RepairService, UnasembleItems);
84 }
85 
87  delete m_dispatch;
88 }
89 
91  _log(CLIENT__MESSAGE, "RepairService bind request for:");
92  bind_args->Dump(CLIENT__MESSAGE, " ");
93 
94  return new RepairSvcBound(m_manager, bind_args->AsInt()->value());
95 }
96 
97 PyResult RepairSvcBound::Handle_DamageModules(PyCallArgs &call) {
98  /* itemIDAndAmountOfDamageList.append((item.itemID, amount)) <-- amount is % of damage
99  * self.repairSvc.DamageModules(itemIDAndAmountOfDamageList)
100  */
101 
102  _log(PHYSICS__INFO, "RepairSvcBound::Handle_DamageModules() size= %u", call.tuple->size() );
103  call.Dump(PHYSICS__INFO);
104 
105  Call_SingleIntList args;
106  if (!args.Decode(&call.tuple)) {
107  codelog(SERVICE__ERROR, "Failed to decode bind args from '%s'", call.client->GetName());
108  return nullptr;
109  }
110 
111  return PyStatic.NewNone();
112 }
113 
114 PyResult RepairSvcBound::Handle_RepairItems(PyCallArgs &call) {
115  // self.repairSvc.RepairItems(itemIDs, amount['qty'])
116  /*
117  * 00:18:28 W RepairSvcBound::Handle_RepairItems(): size= 2
118  * 00:18:28 [PhysicsInfo] Call Arguments:
119  * 00:18:28 [PhysicsInfo] Tuple: 2 elements
120  * 00:18:28 [PhysicsInfo] [ 0] List: 1 elements <-- list of itemIDs to repair
121  * 00:18:28 [PhysicsInfo] [ 0] [ 0] Integer field: 140005905 <-- itemID
122  * 00:18:28 [PhysicsInfo] [ 1] Real field: 112500.000000 <-- isk amount to spend on repairs.
123  */
124 
125  Call_RepairItems args;
126  if (!args.Decode(&call.tuple)) {
127  codelog(SERVICE__ERROR, "Failed to decode bind args from '%s'", call.client->GetName());
128  return nullptr;
129  }
130  if (args.iskAmount < 0.01)
131  return nullptr;
132 
133  /* itemIDs is list of itemIDs to repair.
134  * iskAmount is how much to spend on repairs.
135  * - this needs to be checked against total repair amount, and use fraction to reduce damage for all items in list
136  */
137 
138  InventoryItemRef iRef(nullptr);
139  double cost(0), total(0);
140  uint32 damage(0);
141  std::vector<InventoryItemRef> itemRefVec;
142  PyList::const_iterator itr = args.itemIDs->begin(), end = args.itemIDs->end();
143  for (; itr != end; ++itr) {
144  iRef = sItemFactory.GetItem(PyRep::IntegerValueU32(*itr));
145  if (iRef.get() == nullptr)
146  continue;
147 
148  cost = 0;
149  damage = iRef->GetAttribute(AttrDamage).get_uint32();
150  itemRefVec.push_back(iRef);
151  if (iRef->IsShipItem()) {
152  damage += iRef->GetAttribute(AttrArmorDamage).get_uint32();
153  // ship is (basePrice)*7.5e-10
154  cost = (iRef->type().basePrice() * sConfig.rates.ShipRepairModifier);
155  } else {
156  // modules are (basePrice)*1.25e-6
157  cost = (iRef->type().basePrice() * sConfig.rates.ModuleRepairModifier);
158  }
159  total += damage * cost;
160  }
161 
162  float fraction = 1.0;
163  if (args.iskAmount < total)
164  fraction = total / args.iskAmount;
165 
181  if (fraction > 1.0)
182  fraction = 1;
183  EvilNumber toRepair = EvilZero, curDamage = EvilZero;
184  for (auto cur : itemRefVec) {
185  if (cur->IsShipItem()) {
186  if (fraction == 1) {
187  cur->SetAttribute(AttrDamage, EvilZero);
188  cur->SetAttribute(AttrArmorDamage, EvilZero);
189  continue;
190  }
191 
192  uint32 cHull = cur->GetAttribute(AttrDamage).get_uint32();
193  uint32 cArmor = cur->GetAttribute(AttrArmorDamage).get_uint32();
194  curDamage = cHull + cArmor;
195  toRepair = curDamage * fraction;
196  // this will repair hull first, then armor
197  if (toRepair > cHull) {
198  toRepair -= cHull;
199  cur->SetAttribute(AttrDamage, EvilZero);
200  if (toRepair >= cArmor) {
201  cur->SetAttribute(AttrArmorDamage, EvilZero);
202  } else {
203  toRepair = cArmor - toRepair;
204  cur->SetAttribute(AttrArmorDamage, toRepair);
205  }
206  } else {
207  cur->SetAttribute(AttrDamage, toRepair);
208  }
209  } else {
210  if (fraction == 1) {
211  cur->SetAttribute(AttrDamage, EvilZero);
212  continue;
213  }
214  if ((cur->GetAttribute(AttrDamage).get_float() / cur->GetAttribute(AttrHP).get_float()) > fraction) {
215  toRepair = cur->GetAttribute(AttrHP) * fraction;
216  } else {
217  toRepair = EvilZero;
218  }
219  if (toRepair < EvilZero)
220  toRepair = EvilZero;
221  cur->SetAttribute(AttrDamage, toRepair);
222  }
223  }
224 
225  return PyStatic.NewNone();
226 }
227 
228 PyResult RepairSvcBound::Handle_GetDamageReports(PyCallArgs &call) {
229  /*
230  * 20:39:30 W RepairSvcBound::Handle_GetDamageReports(): size= 1
231  * 20:39:30 [SvcCallDump] Call Arguments:
232  * 20:39:30 [SvcCallDump] Tuple: 1 elements
233  * 20:39:30 [SvcCallDump] [ 0] List: 1 elements
234  * 20:39:30 [SvcCallDump] [ 0] [ 0] Integer field: 140012041
235  */
236  Call_SingleIntList args;
237  if (!args.Decode(&call.tuple)) {
238  codelog(SERVICE__ERROR, "Failed to decode bind args from '%s'", call.client->GetName());
239  return nullptr;
240  }
241 
242  PyDict* dict = new PyDict();
243  Client* pClient = call.client;
245  Inventory* pInv = sRef->GetMyInventory();
246  float standing = 0.0f;
247  // standing system isnt complete, but this is the correct data methods for station standing checks
248  if (IsNPCCorp(sRef->ownerID())) {
249  standing = pClient->GetChar()->GetNPCCorpStanding(sRef->ownerID(), pClient->GetCharacterID());
250  } else {
251  standing = pClient->GetChar()->GetStandingModified(sRef->ownerID(), pClient->GetCharacterID());
252  }
253 
254  for (auto cur : args.ints) {
255  RepairListRsp rlr;
256  rlr.discount = "0%"; // not sure....seen 0% and 100% in packets
257  rlr.serviceCharge = "0%"; // not sure....seen 0% in packets
258  rlr.playerStanding = standing;
259  rlr.lines = new PyList();
260  RepairService::GetDamageReports(cur, pInv, rlr.lines);
261  dict->SetItem(new PyInt(cur), rlr.Encode());
262  }
263 
264  return dict;
265 }
266 
268  std::vector<InventoryItemRef> itemRefVec;
269  InventoryItemRef iRef = pInv->GetByID(itemID);
270  if (iRef.get() == nullptr) {
271  iRef = sItemFactory.GetItem(itemID);
272  if (iRef.get() == nullptr)
273  return;
274  }
275  itemRefVec.push_back(iRef);
276  if (iRef->IsShipItem()) {
277  if (iRef->GetShipItem()->IsActive()) {
278  iRef->GetShipItem()->GetModuleRefVec(itemRefVec);
279  } else {
280  iRef->GetShipItem()->GetModuleItemVec(itemRefVec);
281  }
282  }
283 
284  for (auto cur : itemRefVec) {
285  RepairItemData rid;
286  rid.itemID = cur->itemID();
287  rid.typeID = cur->typeID();
288  rid.groupID = cur->groupID();
289  rid.damage = cur->GetAttribute(AttrDamage).get_int();
290  rid.maxHealth = cur->GetAttribute(AttrHP).get_int();
291  // not sure how to find this data
292  rid.repairable = 1;
293  if (cur->IsShipItem()) { // have to check for drone here, also
294  rid.damage += cur->GetAttribute(AttrArmorDamage).get_int();
295  rid.maxHealth += cur->GetAttribute(AttrArmorHP).get_int();
296  // ship is (basePrice)*7.5e-10
297  rid.costToRepairOneUnitOfDamage = (cur->type().basePrice() * sConfig.rates.ShipRepairModifier);
298  } else {
299  // modules are (basePrice)*1.25e-6
300  rid.costToRepairOneUnitOfDamage = (cur->type().basePrice() * sConfig.rates.ModuleRepairModifier);
301  }
302 
303  list->AddItem(rid.Encode());
304  }
305 }
306 
307 PyResult RepairService::Handle_UnasembleItems(PyCallArgs &call) {
308  // sm.RemoteSvc('repairSvc').UnasembleItems(dict(validIDsByStationID), skipChecks)
309 
310  /*
311  * 15:17:44 [PhysicsInfo] Call_UnasembleItems
312  * 15:17:44 [PhysicsInfo] dict:
313  * 15:17:44 [PhysicsInfo] Dictionary: 2 entries
314  * 15:17:44 [PhysicsInfo] [ 0] Key: Integer field: 60004591 <-- stationID
315  * 15:17:44 [PhysicsInfo] [ 0] Value: List: 3 elements <-- list of 2 element tuples
316  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 0] Tuple: 2 elements <-- tuple of itemID/stationID
317  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 0] [ 0] Integer field: 140006472
318  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 0] [ 1] Integer field: 60004591
319  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 1] Tuple: 2 elements <-- tuple of itemID/stationID
320  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 1] [ 0] Integer field: 140006477
321  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 1] [ 1] Integer field: 60004591
322  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 2] Tuple: 2 elements <-- tuple of itemID/stationID
323  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 2] [ 0] Integer field: 140006476
324  * 15:17:44 [PhysicsInfo] [ 0] Value: [ 2] [ 1] Integer field: 60004591
325  * 15:17:44 [PhysicsInfo] [ 1] Key: Integer field: 60014137 <-- stationID
326  * 15:17:44 [PhysicsInfo] [ 1] Value: List: 1 elements <-- list of 2 element tuples
327  * 15:17:44 [PhysicsInfo] [ 1] Value: [ 0] Tuple: 2 elements <-- tuple of itemID/stationID
328  * 15:17:44 [PhysicsInfo] [ 1] Value: [ 0] [ 0] Integer field: 140000028
329  * 15:17:44 [PhysicsInfo] [ 1] Value: [ 0] [ 1] Integer field: 60014137
330  * 15:17:44 [PhysicsInfo] list:
331  * 15:17:44 [PhysicsInfo] List: Empty <-- skipChecks, not sure what this is for
332  */
333 
334  Call_UnasembleItems args;
335  if (!args.Decode(&call.tuple)) {
336  codelog(SERVICE__ERROR, "Failed to decode bind args from '%s'", call.client->GetName());
337  return nullptr;
338  }
339  args.Dump(PHYSICS__INFO);
340 
341  PyList *pList(nullptr);
342  PyTuple *tuple(nullptr);
343  InventoryItemRef iRef(nullptr);
344  uint32 itemID(0); //,locationID = 0, itemLoc = 0;
345 
346  if (args.list->size() > 0)
347  ; // skipChecks is populated....do something constructive here
348 
350  for (PyDict::const_iterator dictItr = args.dict->begin(); dictItr != args.dict->end(); ++dictItr) {
351  // Dictionary key is LocationID, value is tuple of itemID/item locationID
352  //locationID = dictItr->first->AsInt()->value();
353  pList = dictItr->second->AsList();
354  if (pList != nullptr) {
355  // Iterate through list.
356  for (PyList::const_iterator listItr = pList->begin(); listItr != pList->end(); ++listItr) {
357  // List is tuples of itemID, LocationID pairs.
358  tuple = (*listItr)->AsTuple();
359  if (tuple != nullptr) {
360  // Get the itemID.
361  itemID = PyRep::IntegerValue(tuple->GetItem(0));
362  //itemLoc = PyRep::IntegerValue(tuple->GetItem(1));
363  iRef = sItemFactory.GetItem(itemID);
364  if (iRef.get() != nullptr) {
365  // Add type exceptions here.
366  if (iRef->categoryID() == EVEDB::invCategories::Blueprint
367  or iRef->categoryID() == EVEDB::invCategories::Skill
368  or iRef->categoryID() == EVEDB::invCategories::Material) {
369  // Item cannot be repackaged once used!
370  continue;
371  }
372  // if this is a ship, remove all modules and empty cargo holds before repacking
373  // maybe we should instruct player that ship is fitted/has cargo, and let them do this
374  if (iRef->IsShipItem()) {
375  iRef->GetShipItem()->EmptyCargo();
376  iRef->GetShipItem()->StripFitting();
377  // check insurance and delete if applicable
379  }
380 
381  iRef->ChangeSingleton(false, true);
382  }
383  }
384  }
385  }
386  }
387 
388  // MultipleItemsFailedToRepackage
389 
390  return PyStatic.NewNone();
391 }
Base Python wire object.
Definition: PyRep.h:66
#define sConfig
A macro for easier access to the singleton.
Dispatcher *const m_dispatch
virtual ~RepairService()
#define IsNPCCorp(itemID)
Definition: EVE_Defines.h:238
virtual ~RepairSvcBound()
#define _log(type, fmt,...)
Definition: logsys.h:124
int32 value() const
Definition: PyRep.h:247
virtual PyBoundObject * CreateBoundObject(Client *pClient, const PyRep *bind_args)
void GetModuleItemVec(std::vector< InventoryItemRef > &iRefVec)
Definition: Ship.cpp:568
Python's dictionary.
Definition: PyRep.h:719
size_t size() const
Definition: PyRep.h:591
uint32 ownerID() const
Definition: InventoryItem.h:99
int32 GetCharacterID() const
Definition: Client.h:113
static void GetDamageReports(uint32 itemID, Inventory *pInv, PyList *list)
storage_type::const_iterator const_iterator
Definition: PyRep.h:644
static uint32 IntegerValueU32(PyRep *pRep)
Definition: PyRep.cpp:134
std::string m_strBoundObjectName
Definition: PyBoundObject.h:54
this is a class that kinda mimics how python polymorph's numbers.
Definition: EvilNumber.h:59
CharacterRef GetChar() const
Definition: Client.h:164
Python tuple.
Definition: PyRep.h:567
void Dump(FILE *into, const char *pfx) const
Dumps object to file.
Definition: PyRep.cpp:84
EvilNumber EvilZero
Definition: EvilNumber.cpp:32
void AddItem(PyRep *i)
Definition: PyRep.h:701
void _SetCallDispatcher(CallDispatcher *d)
Definition: PyCallable.h:87
Dispatcher *const m_dispatch
* args
#define codelog(type, fmt,...)
Definition: logsys.h:128
PyCallable_Make_Dispatcher(RepairSvcBound) RepairSvcBound(PyServiceMgr *mgr
SystemManager * SystemMgr() const
Definition: Client.h:92
virtual void Release()
uint32 get_uint32()
Definition: EvilNumber.cpp:173
static void DeleteInsuranceByShipID(uint32 shipID)
Definition: ShipDB.cpp:65
float GetStandingModified(uint32 fromID, uint32 toID=0)
Definition: Character.cpp:1363
Python integer.
Definition: PyRep.h:231
PyServiceMgr *const m_manager
Definition: PyService.h:91
float GetNPCCorpStanding(uint32 fromID, uint32 toID=0)
Definition: Character.cpp:1376
#define PyStatic
Definition: PyRep.h:1209
X * get() const
Definition: RefPtr.h:213
virtual ShipItem * GetShipItem()
Definition: InventoryItem.h:82
const char * GetName() const
Definition: Client.h:94
Client *const client
Definition: PyCallable.h:49
#define PyCallable_REG_CALL(c, m)
Definition: PyServiceCD.h:78
Definition: Client.h:66
Dispatcher *const m_dispatch
Definition: RepairService.h:42
StationItemRef GetStationFromInventory(uint32 stationID)
unsigned __int32 uint32
Definition: eve-compat.h:50
storage_type::const_iterator const_iterator
Definition: PyRep.h:750
PyCallable_Make_InnerDispatcher(RepairService) RepairService
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
void Dump(LogType type) const
Definition: PyCallable.cpp:81
bool IsActive()
Definition: Ship.h:147
PyCallable_DECL_CALL(GetDamageReports)
#define sItemFactory
Definition: ItemFactory.h:165
static int64 IntegerValue(PyRep *pRep)
Definition: PyRep.cpp:118
void GetModuleRefVec(std::vector< InventoryItemRef > &iRefVec)
Definition: Ship.cpp:577
PyInt * AsInt()
Definition: PyRep.h:122
Inventory * GetMyInventory()
Definition: InventoryItem.h:91
void SetItem(PyRep *key, PyRep *value)
SetItem adds or sets a database entry.
Definition: PyRep.cpp:713
virtual bool IsShipItem()
Definition: InventoryItem.h:85
InventoryItemRef GetByID(uint32 id) const
Definition: Inventory.cpp:415
Python list.
Definition: PyRep.h:639
PyTuple * tuple
Definition: PyCallable.h:50