Пример #1
0
        /// <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?
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        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();
        }
Пример #9
0
        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();
        }
Пример #10
0
        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;
                }
            }
        }
Пример #11
0
        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);
        }
Пример #12
0
        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);
        }
Пример #13
0
        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();
            }
        }
Пример #14
0
        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;
                }
            }
        }
Пример #15
0
        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;
                }
            }
        }
Пример #16
0
        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);
        }
Пример #17
0
        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);
        }
Пример #18
0
        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);
        }
Пример #19
0
        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);
        }