/// <summary> /// Removes an expired sell order from the database and returns the leftover items back to the player's hangar /// </summary> /// <param name="connection">The database connection that acquired the lock</param> /// <param name="order">The order to mark as expired</param> private void SellOrderExpired(MySqlConnection connection, MarketOrder order) { // remove order this.DB.RemoveOrder(connection, order.OrderID); // create the item back into the player's hanger // create the new item that will be used by the player ItemEntity item = this.ItemFactory.CreateSimpleItem( this.TypeManager[order.TypeID], order.CharacterID, order.LocationID, Flags.Hangar, order.UnitsLeft ); // immediately unload it, if it has to be loaded the OnItemUpdate notification will take care of that this.ItemFactory.UnloadItem(item); long stationNode = this.SystemManager.GetNodeStationBelongsTo(order.LocationID); if (stationNode == 0 || this.SystemManager.StationBelongsToUs(order.LocationID) == true) { this.NotificationManager.NotifyCharacter(order.CharacterID, Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, this.ItemFactory.LocationMarket.ID)); } else { this.NotificationManager.NotifyNode(stationNode, OnItemChange.BuildLocationChange(item.ID, this.ItemFactory.LocationMarket.ID, order.LocationID)); } // finally notify the character about the order change this.NotificationManager.NotifyCharacter(order.CharacterID, new OnOwnOrderChanged(order.TypeID, "Expiry")); // TODO: SEND AN EVEMAIL TO THE PLAYER? }
public PyDataType DestroyFitting(PyInteger itemID, CallInformation call) { ItemEntity item = this.mInventory.Items[itemID]; if (item.IsInRigSlot() == false) { throw new CannotDestroyFittedItem(); } if (item is ShipModule module) { // disable passive effects module.StopApplyingPassiveEffects(Client); } int oldLocationID = item.LocationID; Flags oldFlag = item.Flag; // destroy the rig this.ItemFactory.DestroyItem(item); // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldFlag, oldLocationID)); return(null); }
public PyInteger LeaveShip(CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); Character character = this.ItemFactory.GetItem <Character>(callerCharacterID); // get the item type Type capsuleType = this.TypeManager[Types.Capsule]; // create a pod for this character ItemInventory capsule = this.ItemFactory.CreateShip(capsuleType, this.Location, character); // update capsule's name capsule.Name = character.Name + "'s Capsule"; // change character's location to the pod character.LocationID = capsule.ID; // notify the client about the item changes call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(capsule, Flags.Capsule, capsule.LocationID)); call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(character, Flags.Pilot, call.Client.ShipID)); // update session call.Client.ShipID = capsule.ID; // persist changes! capsule.Persist(); character.Persist(); // TODO: CHECKS FOR IN-SPACE LEAVING! return(capsule.ID); }
public PyDataType CharAddImplant(PyInteger itemID, CallInformation call) { if (this.Character.SkillQueue.Count > 0) { throw new FailedPlugInImplant(); } // get the item and plug it into our brain now! ItemEntity item = this.ItemFactory.LoadItem(itemID); // ensure the item is somewhere we can interact with it if (item.LocationID != call.Client.ShipID && item.LocationID != call.Client.StationID) { throw new CustomError("You do not have direct access to this implant"); } // check if the slot is free or not this.Character.EnsureFreeImplantSlot(item); // check ownership and skills required to plug in the implant item.EnsureOwnership(this.Character); item.CheckPrerequisites(this.Character); // separate the item if there's more than one if (item.Quantity > 1) { item.Quantity--; // notify the client of the stack change call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(item, item.Quantity + 1)); // save the item to the database item.Persist(); // create the new item with a default location and flag // this way the item location change notification is only needed once item = this.ItemFactory.CreateSimpleItem(item.Type, item.OwnerID, 0, Flags.None, 1, item.Contraband, item.Singleton); } int oldLocationID = item.LocationID; Flags oldFlag = item.Flag; item.LocationID = this.Character.ID; item.Flag = Flags.Implant; call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldFlag, oldLocationID)); // add the item to the inventory it belongs this.Character.AddItem(item); // persist item changes to database item.Persist(); return(null); }
public PyDataType MultiMerge(PyList merges, CallInformation call) { foreach (PyTuple merge in merges.GetEnumerable <PyTuple>()) { if (merge[0] is PyInteger == false || merge[1] is PyInteger == false || merge[2] is PyInteger == false) { continue; } PyInteger fromItemID = merge[0] as PyInteger; PyInteger toItemID = merge[1] as PyInteger; PyInteger quantity = merge[2] as PyInteger; if (this.mInventory.Items.TryGetValue(toItemID, out ItemEntity toItem) == false) { continue; } ItemEntity fromItem = this.ItemFactory.GetItem(fromItemID); // ignore singleton items if (fromItem.Singleton == true || toItem.Singleton == true) { continue; } // ignore items that are not the same type if (fromItem.Type.ID != toItem.Type.ID) { continue; } // if we're fully merging two stacks, just remove one item if (quantity == fromItem.Quantity) { int oldLocationID = fromItem.LocationID; // remove the item this.ItemFactory.DestroyItem(fromItem); // notify the client about the item too call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(fromItem, oldLocationID)); } else { // change the item's quantity fromItem.Quantity -= quantity; // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(fromItem, fromItem.Quantity + quantity)); fromItem.Persist(); } toItem.Quantity += quantity; call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(toItem, toItem.Quantity - quantity)); toItem.Persist(); } return(null); }
public PyDataType RemoveImplantFromCharacter(PyInteger itemID, CallInformation call) { if (this.Character.Items.TryGetValue(itemID, out ItemEntity item) == false) { throw new CustomError("This implant is not in your brain!"); } // now destroy the item this.ItemFactory.DestroyItem(item); // notify the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, this.Character.ID)); return(null); }
public PyDataType Board(PyInteger itemID, CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); // ensure the item is loaded somewhere in this node // this will usually be taken care by the EVE Client if (this.ItemFactory.TryGetItem(itemID, out Ship newShip) == false) { throw new CustomError("Ships not loaded for player and hangar!"); } Character character = this.ItemFactory.GetItem <Character>(callerCharacterID); Ship currentShip = this.ItemFactory.GetItem <Ship>((int)call.Client.ShipID); if (newShip.Singleton == false) { throw new CustomError("TooFewSubSystemsToUndock"); } // TODO: CHECKS FOR IN-SPACE BOARDING! // check skills required to board the given ship newShip.EnsureOwnership(character); newShip.CheckPrerequisites(character); // move the character into this new ship character.LocationID = newShip.ID; // finally update the session call.Client.ShipID = newShip.ID; // notify the client about the change in location call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(character, Flags.Pilot, currentShip.ID)); character.Persist(); // ensure the character is not removed when the capsule is removed currentShip.RemoveItem(character); if (currentShip.Type.ID == (int)Types.Capsule) { // destroy the pod from the database this.ItemFactory.DestroyItem(currentShip); // notify the player of the item change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(currentShip, this.Location.ID)); } return(null); }
private void OnSkillTrainingCompleted(int itemID) { Skill skill = this.Character.Items[itemID] as Skill; // set the skill to the proper flag and set the correct attributes skill.Flag = Flags.Skill; skill.Level = skill.Level + 1; skill.ExpiryTime = 0; // make sure the client is aware of the new item's status this.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(skill, Flags.SkillInTraining)); // also notify attribute changes this.Client.NotifyAttributeChange(new Attributes[] { Attributes.skillPoints, Attributes.skillLevel }, skill); this.Client.NotifyMultiEvent(new OnSkillTrained(skill)); this.Client.SendPendingNotifications(); skill.Persist(); // create history entry this.DB.CreateSkillHistoryRecord(skill.Type, this.Character, SkillHistoryReason.SkillTrainingComplete, skill.Points); // finally remove it off the skill queue this.Character.SkillQueue.RemoveAll(x => x.Skill.ID == skill.ID && x.TargetLevel == skill.Level); this.Character.CalculateSkillPoints(); // get the next skill from the queue (if any) and send the client proper notifications if (this.Character.SkillQueue.Count == 0) { this.Character.Persist(); return; } skill = this.Character.SkillQueue[0].Skill; // setup the process for training next skill in the queue this.SetupTimerForNextSkillInQueue(); this.Client.SendPendingNotifications(); // create history entry this.DB.CreateSkillHistoryRecord(skill.Type, this.Character, SkillHistoryReason.SkillTrainingStarted, skill.Points); // persist the character changes this.Character.Persist(); }
private void InitializeCharacter() { // perform basic checks on the skill queue // iterate the skill queue and generate a timer for the first skill that must be trained // this also prepares the correct notification for multiple skill training done PyList <PyInteger> skillTypeIDs = new PyList <PyInteger>(); List <Character.SkillQueueEntry> toRemove = new List <Character.SkillQueueEntry>(); foreach (Character.SkillQueueEntry entry in this.Character.SkillQueue) { if (entry.Skill.ExpiryTime < DateTime.Now.ToFileTimeUtc()) { // ensure the skill is marked as trained and that they have the correct values stored entry.Skill.Level = entry.TargetLevel; entry.Skill.Flag = Flags.Skill; entry.Skill.ExpiryTime = 0; // add the skill to the list of trained skills for the big notification skillTypeIDs.Add(entry.Skill.Type.ID); toRemove.Add(entry); // update it's location in the client if needed this.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(entry.Skill, Flags.SkillInTraining)); // also notify attribute changes this.Client.NotifyAttributeChange(new Attributes[] { Attributes.skillPoints, Attributes.skillLevel }, entry.Skill); } } // remove skills that already expired this.Character.SkillQueue.RemoveAll(x => toRemove.Contains(x)); // send notification of multiple skills being finished training (if any) if (skillTypeIDs.Count > 0) { this.Client.NotifyMultiEvent(new OnGodmaMultipleSkillsTrained(skillTypeIDs)); } // persists the skill queue this.Character.Persist(); // setup the process for training next skill in the queue this.SetupTimerForNextSkillInQueue(); }
private void StackAll(Flags locationFlag, CallInformation call) { // TODO: ADD CONSTRAINTS CHECKS FOR THE LOCATIONFLAG foreach ((int firstItemID, ItemEntity firstItem) in this.mInventory.Items) { // singleton items are not even checked if (firstItem.Singleton == true || firstItem.Flag != locationFlag) { continue; } foreach ((int secondItemID, ItemEntity secondItem) in this.mInventory.Items) { // ignore the same itemID as they cannot really be merged if (firstItemID == secondItemID) { continue; } // ignore the item if it's singleton if (secondItem.Singleton == true || secondItem.Flag != locationFlag) { continue; } // ignore the item check if they're not the same type ID if (firstItem.Type.ID != secondItem.Type.ID) { continue; } int oldQuantity = secondItem.Quantity; // add the quantity of the first item to the second secondItem.Quantity += firstItem.Quantity; // also create the notification for the user call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(secondItem, oldQuantity)); this.ItemFactory.DestroyItem(firstItem); // notify the client about the item too call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(firstItem, firstItem.Flag, secondItem.LocationID)); // ensure the second item is saved to database too secondItem.Persist(); // finally break this loop as the merge was already done break; } } }
public PyDataType Reprocess(PyList itemIDs, PyInteger ownerID, PyInteger flag, PyBool unknown, PyList skipChecks, CallInformation call) { Character character = this.ItemFactory.GetItem <Character>(call.Client.EnsureCharacterIsSelected()); // TODO: TAKE INTO ACCOUNT OWNERID AND FLAG, THESE MOST LIKELY WILL BE USED BY CORP STUFF foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { if (this.mInventory.Items.TryGetValue(itemID, out ItemEntity item) == false) { throw new MktNotOwner(); } // reprocess the item this.Reprocess(character, item, call.Client); int oldLocationID = item.LocationID; // finally remove the item from the inventories this.ItemFactory.DestroyItem(item); // notify the client about the item being destroyed call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldLocationID)); } return(null); }
public PyDataType TrashItems(PyList itemIDs, PyInteger stationID, CallInformation call) { foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { // do not trash the active ship if (itemID == call.Client.ShipID) { throw new CantMoveActiveShip(); } ItemEntity item = this.ItemFactory.GetItem(itemID); // store it's location id int oldLocation = item.LocationID; Flags oldFlag = item.Flag; // remove the item off the ItemManager this.ItemFactory.DestroyItem(item); // notify the client of the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldFlag, oldLocation)); // TODO: CHECK IF THE ITEM HAS ANY META INVENTORY AND/OR BOUND SERVICE // TODO: AND FREE THOSE TOO SO THE ITEMS CAN BE REMOVED OFF THE DATABASE } return(null); }
private void MoveItemHere(ItemEntity item, Flags newFlag) { // get the old location stored as it'll be used in the notifications int oldLocation = item.LocationID; Flags oldFlag = item.Flag; // rig slots cannot be moved if (item.IsInRigSlot() == true) { throw new CannotRemoveUpgradeManually(); } // special situation, if the old location is a module slot ensure the item is first offlined if (item.IsInModuleSlot() == true) { if (item is ShipModule module) { if (module.Attributes[Attributes.isOnline] == 1) { module.StopApplyingEffect("online", Client); } // disable passive effects too module.StopApplyingPassiveEffects(Client); } } // extra special situation, is the new flag an autofit one? if (newFlag == Flags.AutoFit) { // capsules cannot fit anything if (this.mInventory.Type.ID == (int)Types.Capsule) { throw new CantFitToCapsule(); } if (this.mInventory is Ship ship) { // determine where to put the item if (item is ShipModule module) { if (module.IsHighSlot() == true) { newFlag = this.GetFreeHighSlot(ship); } else if (module.IsMediumSlot() == true) { newFlag = this.GetFreeMediumSlot(ship); } else if (module.IsLowSlot() == true) { newFlag = this.GetFreeLowSlot(ship); } else if (module.IsRigSlot() == true) { newFlag = this.GetFreeRigSlot(ship); } else { // this item cannot be fitted, move it to cargo, maybe throw a exception about not being able to fit it? newFlag = Flags.Cargo; } } // TODO: HANDLE CHARGES! else { newFlag = Flags.Cargo; } } else { newFlag = Flags.Hangar; } } // special situation, if the new location is a module slot ensure the item is a singleton (TODO: HANDLE CHARGES TOO) if (newFlag.IsModule() == true) { ShipModule module = null; if (item is ShipModule shipModule) { module = shipModule; } if (item.Quantity == 1) { // remove item off the old inventory if required if (this.ItemFactory.TryGetItem(item.LocationID, out ItemInventory inventory) == true) { inventory.RemoveItem(item); } OnItemChange changes = new OnItemChange(item); if (item.Singleton == false) { changes.AddChange(ItemChange.Singleton, item.Singleton); } item.LocationID = this.mInventory.ID; item.Flag = newFlag; item.Singleton = true; changes .AddChange(ItemChange.LocationID, oldLocation) .AddChange(ItemChange.Flag, (int)oldFlag); // notify the character about the change Client.NotifyMultiEvent(changes); // update meta inventories too this.ItemFactory.MetaInventoryManager.OnItemMoved(item, oldLocation, this.mInventory.ID); // finally persist the item changes item.Persist(); } else { // item is not a singleton, create a new item, decrease quantity and send notifications ItemEntity newItem = this.ItemFactory.CreateSimpleItem(item.Type, item.OwnerID, this.mInventory.ID, newFlag, 1, false, true); item.Quantity -= 1; // notify the quantity change and the new item Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(item, item.Quantity + 1)); Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(newItem, Flags.None, 0)); item.Persist(); // replace reference so the following code handle things properly item = newItem; if (item is ShipModule shipModule2) { module = shipModule2; } } try { // apply all the passive effects (this also blocks the item fitting if the initialization fails) module?.ApplyPassiveEffects(Client); // extra check, ensure that the character has the required skills } catch (UserError) { // ensure that the passive effects that got applied already are removed from the item module?.StopApplyingPassiveEffects(Client); int newOldLocation = item.LocationID; Flags newOldFlag = item.Flag; // now undo the whole thing item.LocationID = oldLocation; item.Flag = oldFlag; Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, newOldFlag, newOldLocation)); throw; } // ensure the new inventory knows this.mInventory.AddItem(item); module?.Persist(); // put the module online after fitting it as long as it's a normal module if (module?.IsRigSlot() == false) { module?.ApplyEffect("online", Client); } } else { // remove item off the old inventory if required if (this.ItemFactory.TryGetItem(item.LocationID, out ItemInventory inventory) == true) { inventory.RemoveItem(item); } // set the new location for the item item.LocationID = this.mInventory.ID; item.Flag = newFlag; // notify the character about the change Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldFlag, oldLocation)); // update meta inventories too this.ItemFactory.MetaInventoryManager.OnItemMoved(item, oldLocation, this.mInventory.ID); // ensure the new inventory knows this.mInventory.AddItem(item); // finally persist the item changes item.Persist(); } }
private void PlaceImmediateSellOrderChar(MySqlConnection connection, Wallet wallet, Character character, int itemID, int typeID, int stationID, int quantity, double price, Client client) { int solarSystemID = this.ItemFactory.GetStaticStation(stationID).SolarSystemID; // look for matching buy orders MarketOrder[] orders = this.DB.FindMatchingOrders(connection, price, typeID, character.ID, solarSystemID, TransactionType.Buy); // ensure there's at least some that match this.CheckMatchingBuyOrders(orders, quantity, stationID); // there's at least SOME orders that can be satisfied, let's start satisfying them one by one whenever possible foreach (MarketOrder order in orders) { int quantityToSell = 0; // ensure the order is in the range if (order.Range == -1 && order.LocationID != stationID) { continue; } if (order.Range != -1 && order.Range < order.Jumps) { continue; } if (order.UnitsLeft <= quantity) { // if there's any kind of escrow left ensure that the character receives it back double escrowLeft = order.Escrow - order.UnitsLeft * price; if (escrowLeft > 0.0) { // give back the escrow for the character // TODO: THERE IS A POTENTIAL DEADLOCK HERE IF WE BUY FROM OURSELVES using Wallet escrowWallet = this.WalletManager.AcquireWallet(order.CharacterID, order.AccountID); { escrowWallet.CreateJournalRecord( MarketReference.MarketEscrow, null, null, escrowLeft ); } } // this order is fully satisfiable, so do that // remove the order off the database if it's fully satisfied this.DB.RemoveOrder(connection, order.OrderID); quantityToSell = order.UnitsLeft; quantity -= order.UnitsLeft; } else if (order.MinimumUnits <= quantity) { // we can satisfy SOME of the order this.DB.UpdateOrderRemainingQuantity(connection, order.OrderID, order.UnitsLeft - quantity, quantity * price); // the quantity we're selling is already depleted if the code got here quantityToSell = quantity; quantity = 0; } if (quantityToSell > 0) { // calculate sales tax double profit, tax; this.CalculateSalesTax(character.GetSkillLevel(Types.Accounting), quantity, price, out tax, out profit); // create the required records for the wallet wallet.CreateJournalRecord(MarketReference.MarketTransaction, order.CharacterID, character.ID, null, profit); wallet.CreateJournalRecord(MarketReference.TransactionTax, null, null, -tax); this.WalletManager.CreateTransactionRecord(character.ID, TransactionType.Sell, order.CharacterID, typeID, quantityToSell, price, stationID); this.WalletManager.CreateTransactionRecord(order.CharacterID, TransactionType.Buy, character.ID, typeID, quantityToSell, price, stationID); // create the new item that will be used by the player ItemEntity item = this.ItemFactory.CreateSimpleItem( this.TypeManager[typeID], order.CharacterID, stationID, Flags.Hangar, quantityToSell ); // immediately unload it, if it has to be loaded the OnItemUpdate notification will take care of that this.ItemFactory.UnloadItem(item); // check if the station it's at is loaded and notify the node in question // if not take care of the item notification ourselves long stationNode = this.SystemManager.GetNodeStationBelongsTo(stationID); if (stationNode == 0 || this.SystemManager.StationBelongsToUs(stationID) == true) { this.NotificationManager.NotifyCharacter(item.OwnerID, Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, this.ItemFactory.LocationMarket.ID)); } else { this.NotificationManager.NotifyNode(stationNode, OnItemChange.BuildLocationChange(itemID, this.ItemFactory.LocationMarket.ID, stationID)); } } // ensure we do not sell more than we have if (quantity == 0) { break; } } }
private void PlaceImmediateBuyOrderChar(MySqlConnection connection, Wallet wallet, int typeID, Character character, int stationID, int quantity, double price, int range, CallInformation call) { int solarSystemID = this.ItemFactory.GetStaticStation(stationID).SolarSystemID; // look for matching sell orders MarketOrder[] orders = this.DB.FindMatchingOrders(connection, price, typeID, character.ID, solarSystemID, TransactionType.Sell); // ensure there's at least some that match this.CheckMatchingSellOrders(orders, quantity, solarSystemID); foreach (MarketOrder order in orders) { int quantityToBuy = 0; if (order.Range == -1 && order.LocationID != stationID) { continue; } if (order.Range != -1 && order.Range < order.Jumps) { continue; } if (order.UnitsLeft <= quantity) { // the order was completed, remove it from the database this.DB.RemoveOrder(connection, order.OrderID); // increase the amount of bought items quantityToBuy = order.UnitsLeft; quantity -= order.UnitsLeft; } else { // part of the sell order was satisfied this.DB.UpdateOrderRemainingQuantity(connection, order.OrderID, order.UnitsLeft - quantity, 0); quantityToBuy = quantity; quantity = 0; } if (quantityToBuy > 0) { // acquire wallet journal for seller so we can update their balance to add the funds that he got using Wallet sellerWallet = this.WalletManager.AcquireWallet(order.CharacterID, order.AccountID); { sellerWallet.CreateJournalRecord(MarketReference.MarketTransaction, character.ID, order.CharacterID, null, price * quantityToBuy); } // create the transaction records for both characters this.WalletManager.CreateTransactionRecord(character.ID, TransactionType.Buy, order.CharacterID, typeID, quantityToBuy, price, stationID); this.WalletManager.CreateTransactionRecord(order.CharacterID, TransactionType.Sell, character.ID, typeID, quantityToBuy, price, stationID); long stationNode = this.SystemManager.GetNodeStationBelongsTo(stationID); // create the new item that will be used by the player ItemEntity item = this.ItemFactory.CreateSimpleItem( this.TypeManager[typeID], character.ID, stationID, Flags.Hangar, quantityToBuy ); // immediately unload it, if it has to be loaded the OnItemUpdate notification will take care of that this.ItemFactory.UnloadItem(item); if (stationNode == 0 || this.SystemManager.StationBelongsToUs(stationID) == true) { this.NotificationManager.NotifyCharacter(character.ID, Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, this.ItemFactory.LocationMarket.ID)); } else { this.NotificationManager.NotifyNode(stationNode, OnItemChange.BuildLocationChange(item.ID, this.ItemFactory.LocationMarket.ID, stationID)); } } // ensure we do not buy more than we need if (quantity == 0) { break; } } }
public PyDataType CancelCharOrder(PyInteger orderID, PyInteger regionID, CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); Character character = this.ItemFactory.GetItem <Character>(callerCharacterID); using MySqlConnection connection = this.DB.AcquireMarketLock(); try { MarketOrder order = this.DB.GetOrderById(connection, orderID); if (order.CharacterID != callerCharacterID) { throw new MktOrderDidNotMatch(); } long currentTime = DateTime.UtcNow.ToFileTimeUtc(); // check for timers, no changes in less than 5 minutes if (currentTime < order.Issued + TimeSpan.TicksPerSecond * this.NodeContainer.Constants[Constants.mktModificationDelay]) { throw new MktOrderDelay((order.Issued + TimeSpan.TicksPerSecond * this.NodeContainer.Constants[Constants.mktModificationDelay]) - currentTime); } // check for escrow if (order.Escrow > 0.0 && order.Bid == TransactionType.Buy) { using Wallet wallet = this.WalletManager.AcquireWallet(character.ID, 1000); { wallet.CreateJournalRecord(MarketReference.MarketEscrow, null, null, order.Escrow); } } if (order.Bid == TransactionType.Sell) { // create the new item that will be used by the player ItemEntity item = this.ItemFactory.CreateSimpleItem( this.TypeManager[order.TypeID], character.ID, order.LocationID, Flags.Hangar, order.UnitsLeft ); // immediately unload it, if it has to be loaded the OnItemUpdate notification will take care of that this.ItemFactory.UnloadItem(item); // check what node this item should be loaded at long stationNode = this.SystemManager.GetNodeStationBelongsTo(order.LocationID); if (stationNode == 0 || this.SystemManager.StationBelongsToUs(order.LocationID) == true) { this.NotificationManager.NotifyCharacter(character.ID, Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, this.ItemFactory.LocationMarket.ID)); } else { this.NotificationManager.NotifyNode(stationNode, OnItemChange.BuildLocationChange(item.ID, this.ItemFactory.LocationMarket.ID, order.LocationID)); } } // finally remove the order this.DB.RemoveOrder(connection, order.OrderID); // send a OnOwnOrderChange notification call.Client.NotifyMultiEvent(new OnOwnOrderChanged(order.TypeID, "Removed")); } finally { this.DB.ReleaseMarketLock(connection); } return(null); }
public PyDataType SaveSkillQueue(PyList queue, CallInformation call) { if (this.Character.SkillQueue.Count > 0) { // calculate current skill in training points Skill currentSkill = this.Character.SkillQueue[0].Skill; if (currentSkill.ExpiryTime > 0) { // get the total amount of minutes the skill would have taken to train completely long pointsLeft = (long)(currentSkill.GetSkillPointsForLevel(this.Character.SkillQueue[0].TargetLevel) - currentSkill.Points); TimeSpan timeLeft = TimeSpan.FromMinutes(pointsLeft / this.Character.GetSkillPointsPerMinute(currentSkill)); DateTime endTime = DateTime.FromFileTimeUtc(currentSkill.ExpiryTime); DateTime startTime = endTime.Subtract(timeLeft); TimeSpan timePassed = DateTime.UtcNow - startTime; // calculate the skill points to add double skillPointsToAdd = timePassed.TotalMinutes * this.Character.GetSkillPointsPerMinute(currentSkill); currentSkill.Points += skillPointsToAdd; } // remove the timer associated with the queue this.FreeSkillQueueTimers(); foreach (Character.SkillQueueEntry entry in this.Character.SkillQueue) { entry.Skill.Flag = Flags.Skill; call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(entry.Skill, Flags.SkillInTraining)); // send notification of skill training stopped call.Client.NotifyMultiEvent(new OnSkillTrainingStopped(entry.Skill)); // create history entry this.DB.CreateSkillHistoryRecord(entry.Skill.Type, this.Character, SkillHistoryReason.SkillTrainingCancelled, entry.Skill.Points); entry.Skill.ExpiryTime = 0; entry.Skill.Persist(); } this.Character.SkillQueue.Clear(); } DateTime startDateTime = DateTime.UtcNow; bool first = true; foreach (PyTuple entry in queue.GetEnumerable <PyTuple>()) { // ignore wrong entries if (entry.Count != 2) { continue; } int typeID = entry[0] as PyInteger; int level = entry[1] as PyInteger; // search for an item with the given typeID ItemEntity item = this.Character.Items.First(x => x.Value.Type.ID == typeID && (x.Value.Flag == Flags.Skill || x.Value.Flag == Flags.SkillInTraining)).Value; // ignore items that are not skills if (item is Skill == false) { continue; } Skill skill = item as Skill; double skillPointsLeft = skill.GetSkillPointsForLevel(level) - skill.Points; TimeSpan duration = TimeSpan.FromMinutes(skillPointsLeft / this.Character.GetSkillPointsPerMinute(skill)); DateTime expiryTime = startDateTime + duration; skill.ExpiryTime = expiryTime.ToFileTimeUtc(); skill.Flag = Flags.SkillInTraining; call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(skill, Flags.Skill)); startDateTime = expiryTime; // skill added to the queue, persist the character to ensure all the changes are saved this.Character.SkillQueue.Add(new Character.SkillQueueEntry() { Skill = skill, TargetLevel = level }); if (first == true) { // skill was trained, send the success message call.Client.NotifyMultiEvent(new OnSkillStartTraining(skill)); // create history entry this.DB.CreateSkillHistoryRecord(skill.Type, this.Character, SkillHistoryReason.SkillTrainingStarted, skill.Points); first = false; } skill.Persist(); } // ensure the timer is present for the first skill in the queue this.SetupTimerForNextSkillInQueue(); // finally persist the data to the database this.Character.Persist(); return(null); }
public PyDataType InjectSkillIntoBrain(PyList itemIDs, CallInformation call) { foreach (PyInteger item in itemIDs.GetEnumerable <PyInteger>()) { try { // get the item by it's ID and change the location of it Skill skill = this.ItemFactory.GetItem <Skill>(item); // check if the character already has this skill injected if (this.Character.InjectedSkillsByTypeID.ContainsKey(skill.Type.ID) == true) { throw new CharacterAlreadyKnowsSkill(skill.Type); } // is this a stack of skills? if (skill.Quantity > 1) { // add one of the skill into the character's brain Skill newStack = this.ItemFactory.CreateSkill(skill.Type, this.Character, 0, SkillHistoryReason.None); // subtract one from the quantity skill.Quantity -= 1; // save to database skill.Persist(); // finally notify the client call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(skill, skill.Quantity + 1)); call.Client.NotifyMultiEvent(OnItemChange.BuildNewItemChange(newStack)); } else { // store old values for the notification int oldLocationID = skill.LocationID; Flags oldFlag = skill.Flag; // now set the new values skill.LocationID = this.Character.ID; skill.Flag = Flags.Skill; skill.Level = 0; skill.Singleton = true; // ensure the character has the skill in his/her brain this.Character.AddItem(skill); // ensure the changes are saved skill.Persist(); // notify the character of the change in the item call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(skill, oldFlag, oldLocationID)); call.Client.NotifyMultiEvent(OnItemChange.BuildSingletonChange(skill, false)); } } catch (CharacterAlreadyKnowsSkill) { throw; } catch (Exception) { Log.Error($"Cannot inject itemID {item} into {this.Character.ID}'s brain..."); throw; } } // send the skill injected notification to refresh windows if needed call.Client.NotifyMultiEvent(new OnSkillInjected()); return(null); }
public PyDataType UnasembleItems(PyDictionary validIDsByStationID, PyList skipChecks, CallInformation call) { int characterID = call.Client.EnsureCharacterIsSelected(); List <RepairDB.ItemRepackageEntry> entries = new List <RepairDB.ItemRepackageEntry>(); bool ignoreContractVoiding = false; bool ignoreRepackageWithUpgrades = false; foreach (PyString check in skipChecks.GetEnumerable <PyString>()) { if (check == "RepairUnassembleVoidsContract") { ignoreContractVoiding = true; } if (check == "ConfirmRepackageSomethingWithUpgrades") { ignoreRepackageWithUpgrades = true; } } foreach ((PyInteger stationID, PyList itemIDs) in validIDsByStationID.GetEnumerable <PyInteger, PyList>()) { foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { RepairDB.ItemRepackageEntry entry = this.RepairDB.GetItemToRepackage(itemID, characterID, stationID); if (entry.HasContract == true && ignoreContractVoiding == false) { throw new RepairUnassembleVoidsContract(itemID); } if (entry.HasUpgrades == true && ignoreRepackageWithUpgrades == false) { throw new ConfirmRepackageSomethingWithUpgrades(); } if (entry.Damage != 0.0) { throw new CantRepackageDamagedItem(); } entries.Add(entry); } } foreach (RepairDB.ItemRepackageEntry entry in entries) { if (entry.Singleton == false) { continue; } // extra situation, the repair is happening on a item in our node, the client must know immediately if (entry.NodeID == this.Container.NodeID || this.SystemManager.StationBelongsToUs(entry.LocationID) == true) { ItemEntity item = this.ItemFactory.LoadItem(entry.ItemID, out bool loadRequired); // the item is an inventory, take everything out! if (item is ItemInventory inventory) { foreach ((int _, ItemEntity itemInInventory) in inventory.Items) { // if the item is in a rig slot, destroy it if (itemInInventory.IsInRigSlot() == true) { Flags oldFlag = itemInInventory.Flag; this.ItemFactory.DestroyItem(itemInInventory); // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(itemInInventory, oldFlag, entry.ItemID)); } else { Flags oldFlag = itemInInventory.Flag; // update item's location itemInInventory.LocationID = entry.LocationID; itemInInventory.Flag = Flags.Hangar; // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(itemInInventory, oldFlag, entry.ItemID)); // save the item itemInInventory.Persist(); } } } // update the singleton flag too item.Singleton = false; call.Client.NotifyMultiEvent(OnItemChange.BuildSingletonChange(item, true)); // load was required, the item is not needed anymore if (loadRequired == true) { this.ItemFactory.UnloadItem(item); } } else { long nodeID = this.SystemManager.GetNodeStationBelongsTo(entry.LocationID); if (nodeID > 0) { Notifications.Nodes.Inventory.OnItemChange change = new Notifications.Nodes.Inventory.OnItemChange(); change.AddChange(entry.ItemID, "singleton", true, false); this.NotificationManager.NotifyNode(nodeID, change); } } // finally repackage the item this.RepairDB.RepackageItem(entry.ItemID, entry.LocationID); // remove any insurance contract for the ship this.InsuranceDB.UnInsureShip(entry.ItemID); } return(null); }