public void CreateItem(ItemTemplateID id, byte amount) { // Get the item template var template = ItemTemplateManager.Instance[id]; if (template == null) { UserChat("Invalid item template ID: " + id); return; } if (amount <= 0) { UserChat("Invalid amount specified. The amount must be 1 or more."); return; } // Create the item var item = new ItemEntity(template, amount); // Give to user var remainder = User.Inventory.TryAdd(item); // Delete any that failed to be added if (remainder != null) { UserChat(remainder.Amount + " units could not be added to your inventory."); remainder.Destroy(); } }
/// <summary> /// Creates an instance of the <see cref="ItemEntity"/> from the template. /// </summary> /// <returns>The instance of the <see cref="ItemEntity"/>, or null if the creation chance failed.</returns> public static ItemEntity CreateInstance(this CharacterTemplateEquipmentItem v) { if (!v.Chance.Test()) return null; var instance = new ItemEntity(v.ItemTemplate, Vector2.Zero, 1); return instance; }
/// <summary> /// Creates an instance of the <see cref="ItemEntity"/> from the template. /// </summary> /// <returns>The instance of the <see cref="ItemEntity"/>, or null if the creation chance failed.</returns> public static ItemEntity CreateInstance(this CharacterTemplateInventoryItem v) { if (!v.Chance.Test()) return null; var amount = (byte)_random.Next(v.Min, v.Max + 1); if (amount == 0) return null; var instance = new ItemEntity(v.ItemTemplate, Vector2.Zero, amount); return instance; }
public static bool AreValuesEqual(ItemEntity item, ItemValueTracker tracker) { // Treat a null ItemValueTracker just like if it was a tracker with IsNull set if (tracker == null) { // If both are null, they are considered equal return item == null; } // Do a normal call to IsEqualTo since the tracker isn't null return tracker.IsEqualTo(item); }
public InventoryChangeInfo(ItemEntity item, ItemValueTracker oldValues, InventorySlot slot) { _slot = slot; _item = item; if (oldValues == null || oldValues.IsNull) _oldValues = null; else _oldValues = oldValues; Debug.Assert(_item != null || _oldValues != null, "item and oldValues can not both be null. " + "This would imply that the item changed from null to null."); }
public bool IsEqualTo(ItemEntity item) { if (item == null) { // If the item is null, then check if ours is also null return _isNull; } else { // If the item isn't null, but ours is, then return false if (_isNull) return false; // Neither are null, so check that all values are equal return (_amount == item.Amount) && (_value == item.Value) && (_graphicIndex == item.GraphicIndex) && (_name == item.Name) && (_description == item.Description); } }
public bool IsEqualTo(ItemEntity item) { if (item == null) { // If the item is null, then check if ours is also null return(_isNull); } else { // If the item isn't null, but ours is, then return false if (_isNull) { return(false); } // Neither are null, so check that all values are equal return((_amount == item.Amount) && (_value == item.Value) && (_graphicIndex == item.GraphicIndex) && (_name == item.Name) && (_description == item.Description)); } }
public void SetValues(ItemEntity item) { // Check if we are setting the value to a null item if (item == null) { _isNull = true; // Release the name and description strings so the GC can maybe clean them up _name = null; _description = null; return; } // Set the new values _isNull = false; _name = item.Name; _description = item.Description; _amount = item.Amount; _graphicIndex = item.GraphicIndex; _value = item.Value; }
/// <summary> /// Makes the Character drop an existing item. This does NOT remove the ItemEntity from the Character in any /// way. Be sure to remove the ItemEntity from the Character first if needed. /// </summary> /// <param name="item">ItemEntity to drop.</param> public void DropItem(ItemEntity item) { if (item == null) { const string errmsg = "`{0}` tried to drop a null item."; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, this); return; } if (item.IsDisposed) { const string errmsg = "`{0}` tried to drop disposed item `{1}`."; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, this, item); return; } if (!IsAlive) { const string errmsg = "`{0}` tried to drop item `{1}` while dead."; if (log.IsWarnEnabled) log.WarnFormat(errmsg, this, item); return; } var dropPos = GetDropPos(); item.Position = dropPos; // Add the item to the map Map.AddEntity(item); OnDroppedItem(item); if (DroppedItem != null) DroppedItem.Raise(this, EventArgsHelper.Create(item)); }
/// <summary> /// Makes the Character use an equipment item. /// </summary> /// <param name="item">Item to be equipped.</param> /// <param name="inventorySlot">If the item is from the inventory, the inventory slot that the item is in.</param> /// <returns>True if the item was successfully equipped; otherwise false.</returns> bool UseEquipment(ItemEntity item, InventorySlot? inventorySlot) { if (!inventorySlot.HasValue) { // Equip an item not from the inventory return Equipped.Equip(item); } else { // Equip an item from the inventory return Equip(inventorySlot.Value); } }
/// <summary> /// Gets the default amount of money a Character will pay for buying the given <paramref name="item"/> from /// a shop. /// </summary> /// <param name="item">The item to purchase.</param> /// <returns>the default amount of money a Character will pay for buying the given <paramref name="item"/> /// from a shop.</returns> public static int GetItemBuyValue(ItemEntity item) { return item.Value; }
/// <summary> /// Gives an item to the Character to be placed in their Inventory. /// </summary> /// <param name="item">Item to give to the character.</param> /// <returns>The remainder of the item that failed to be added to the inventory, or null if all of the /// item was added.</returns> public virtual ItemEntity TryGiveItem(ItemEntity item) { if (item == null) { Debug.Fail("Item is null."); return null; } Debug.Assert(item.Amount != 0, "Invalid item amount."); // Add as much of the item to the inventory as we can int startAmount = item.Amount; ItemEntity remainder = _inventory.TryAdd(item); // Check how much was added int amountAdded = (startAmount - (remainder != null ? (int)remainder.Amount : 0)); Debug.Assert(amountAdded >= 0 && amountAdded <= byte.MaxValue); amountAdded = amountAdded.Clamp(byte.MinValue, byte.MaxValue); if (amountAdded > 0) AfterGiveItem(item, (byte)amountAdded); // Return the remainder return remainder; }
/// <summary> /// Checks if this ItemEntity can be stacked with another ItemEntity. To stack, both items must contain the same /// stat modifiers, name, description, value, and graphic index. /// </summary> /// <param name="source">Item to check if can stack on this ItemEntity.</param> /// <returns>True if the two items can stack on each other, else false.</returns> public bool CanStack(ItemEntity source) { // Check for equal reference if (ReferenceEquals(this, source)) { // Although it makes sense for an ItemEntity to be able to stack onto itself, // there is no reason this should ever happen intentionally const string errmsg = "Trying to stack an ItemEntity `{0}` onto itself. Although this is not an error, " + "it makes no sense why it would be attempted."; if (log.IsWarnEnabled) log.WarnFormat(errmsg, this); Debug.Fail(string.Format(errmsg, this)); return true; } // Check for non-equal values if (Value != source.Value || GraphicIndex != source.GraphicIndex || Type != source.Type || Name != source.Name || Description != source.Description || Range != source.Range || WeaponType != source.WeaponType) return false; // Check for non-equal stats if (!BaseStats.HasSameValues(source.BaseStats) || !ReqStats.HasSameValues(source.ReqStats)) return false; // Everything important is equal, so they can be stacked return true; }
public static PacketWriter SendEquipmentItemInfo(EquipmentSlot slot, ItemEntity item) { var pw = GetWriter(ServerPacketID.SendEquipmentItemInfo); pw.WriteEnum(slot); new ItemTable(item).WriteState(pw); return pw; }
/// <summary> /// Initializes a new instance of the <see cref="World"/> class. /// </summary> /// <param name="parent">Server this world is part of.</param> /// <exception cref="ArgumentNullException"><paramref name="parent" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">The unarmed weapon ID is not a valid item template ID.</exception> public World(Server parent) { if (parent == null) { throw new ArgumentNullException("parent"); } _respawnTaskList = new RespawnTaskList(this); // Store the parent _server = parent; // Create some objects _guildMemberPerformer = new GuildMemberPerformer(DbController, FindUser); // Create the unarmed weapon var unarmedWeaponID = ServerSettings.Default.UnarmedItemTemplateID; var unarmedWeaponTemplate = _itemTemplateManager[unarmedWeaponID]; if (unarmedWeaponTemplate == null) { const string errmsg = "Unable to create unarmed weapon - couldn't find item template with ID `{0}`."; if (log.IsFatalEnabled) { log.FatalFormat(errmsg, unarmedWeaponID); } throw new ArgumentException(string.Format(errmsg, unarmedWeaponID)); } _unarmedWeapon = new ItemEntity(unarmedWeaponTemplate, 1); // Create the maps var mapFiles = MapBase.GetMapFiles(ContentPaths.Build); _maps = mapFiles.LoadIntoIndexedArray(x => (int)x.ID, x => { MapID mapID; if (!MapBase.TryGetIndexFromPath(x, out mapID)) { const string errmsg = "Failed to get the ID of map file `{0}`."; if (log.IsFatalEnabled) { log.FatalFormat(errmsg, x); } throw new ArgumentException(string.Format(errmsg, x)); } return(new Map(mapID, this)); }); // Load maps in parallel Parallel.ForEach(_maps, map => { if (map != null) { map.Load(); } }); // Add some event hooks BanningManager.Instance.AccountBanned -= BanningManager_AccountBanned; BanningManager.Instance.AccountBanned += BanningManager_AccountBanned; }
/// <summary> /// Gives an item to the Character to be placed in their Inventory. /// </summary> /// <param name="item">Item to give to the character.</param> /// <returns>The amount of the <paramref name="item"/> that was successfully given to the <see cref="Character"/>.</returns> public virtual int GiveItem(ItemEntity item) { if (item == null) { Debug.Fail("Item is null."); return 0; } Debug.Assert(item.Amount != 0, "Invalid item amount."); var amountAdded = _inventory.Add(item); Debug.Assert(amountAdded >= byte.MinValue && amountAdded <= byte.MaxValue); if (amountAdded > 0) AfterGiveItem(item, (byte)amountAdded); return amountAdded; }
/// <summary> /// Handles attacking with a ranged weapon. /// </summary> /// <param name="weapon">The weapon to attack with. Cannot be null.</param> /// <param name="target">The target to attack. Can be null.</param> void AttackRanged(ItemEntity weapon, Character target) { if (weapon == null) { Debug.Fail("Weapon should not be null..."); return; } // We can't do anything with ranged attacks if no target is given if (target == null) { TrySend(GameMessage.CannotAttackNeedTarget, ServerMessageType.GUI); return; } Ray2D ray = new Ray2D(this, Position, target.Position, Map.Spatial); Vector2 rayCollideWall; // FUTURE: Use to create some sort of wasted ammo on a wall or something. e.g. Grenade item explodes on walls. bool hasHitWall = ray.Intersects<WallEntity>(out rayCollideWall); if (hasHitWall) { TrySend(GameMessage.CannotAttackNotInSight, ServerMessageType.GUI); return; } List<ISpatial> rayCollideCharacters; // Use IntersectsMany here if you want to damage all characters in the attack path bool hasHitCharacter = ray.Intersects<Character>(out rayCollideCharacters); if (hasHitCharacter) { var ammoUsed = false; // Check for the needed ammo switch (weapon.WeaponType) { case WeaponType.Projectile: // Grab projectile ammo out of the inventory first if possible to avoid having to constantly reload var invAmmo = Inventory.FirstOrDefault(x => weapon.CanStack(x.Value)); if (invAmmo.Value != null) Inventory.DecreaseItemAmount(invAmmo.Key); else weapon.Destroy(); ammoUsed = true; break; case WeaponType.Ranged: // By default, guns won't use ammo. But if you want to require guns to use ammo, you can do so here ammoUsed = true; break; } if (!ammoUsed) return; foreach (var character in rayCollideCharacters) { var c = character as Character; if (!Alliance.CanAttack(c.Alliance)) continue; // Attack using (var charAttack = ServerPacket.CharAttack(MapEntityIndex, c.MapEntityIndex, weapon.ActionDisplayID)) { Map.SendToArea(this, charAttack, ServerMessageType.MapEffect); } OnAttacked(); if (Attacked != null) Attacked.Raise(this, EventArgs.Empty); AttackApplyReal(c); } } }
/// <summary> /// Attacks the <paramref name="target"/> using the given <paramref name="weapon"/>. /// </summary> /// <param name="target">The <see cref="Character"/> to attack. If null, the target will be selected /// automatically if applicable.</param> /// <param name="weapon">The weapon to use for attacking. If null, will be treated as an unarmed melee attack.</param> public void Attack(Character target, ItemEntity weapon) { if (!IsAlive) { const string errmsg = "`{0}` tried to attack `{1}` with weapon `{2}` while dead."; if (log.IsInfoEnabled) log.InfoFormat(errmsg, this, target, weapon); return; } var currTime = GetTime(); // Don't allow attacking while casting a skill if (_skillCaster.IsCastingSkill) return; // Check that enough time has elapsed since the last attack if (_nextAttackTime > currTime) return; // If no weapon is specified, use the unarmed weapon if (weapon == null) weapon = Weapon; // Abort if using an unknown weapon type if (weapon.WeaponType == WeaponType.Unknown) { TrySend(GameMessage.CannotAttackWithWeapon, ServerMessageType.GUI, weapon.Name); return; } // Check if a target was given to us if (target != null) { // Check for a valid target if (!target.IsAlive) { const string errmsg = "`{0}` tried to attack target `{1}`, but the target is not alive."; if (log.IsWarnEnabled) log.WarnFormat(errmsg, this, target); Debug.Fail(string.Format(errmsg, this, target)); return; } if (target.Map != Map) { const string errmsg = "`{0}` tried to attack target `{1}`, but the target is on a different map."; if (log.IsWarnEnabled) log.WarnFormat(errmsg, this, target); Debug.Fail(string.Format(errmsg, this, target)); return; } if (!Alliance.CanAttack(target.Alliance)) { TrySend(GameMessage.CannotAttackAllianceConflict, ServerMessageType.GUI, target.Name); return; } if (this.GetDistance(target) > weapon.Range) { TrySend(GameMessage.CannotAttackTooFarAway, ServerMessageType.GUI); return; } } // Call the appropriate attack method switch (weapon.WeaponType) { case WeaponType.Melee: AttackMelee(weapon, target); break; case WeaponType.Projectile: case WeaponType.Ranged: AttackRanged(weapon, target); break; default: const string errmsg = "No attack support defined for WeaponType `{0}`."; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, weapon.WeaponType); Debug.Fail(string.Format(errmsg, weapon.WeaponType)); break; } // Update the last attack time to now _nextAttackTime = (TickCount)(currTime + _attackTimeout); }
/// <summary> /// When overridden in the derived class, lets the Character handle being given items through GiveItem(). /// </summary> /// <param name="item">The <see cref="ItemEntity"/> the Character was given.</param> /// <param name="amount">The amount of the <paramref name="item"/> the Character was given. Will be greater /// than 0.</param> protected virtual void AfterGiveItem(ItemEntity item, byte amount) { }
/// <summary> /// Uses a use-once item. /// </summary> /// <param name="item">The item to be used.</param> /// <returns>True if the item was used; otherwise false.</returns> bool UseItemUseOnce(ItemEntity item) { var useBonuses = item.BaseStats.Where(stat => stat.Value != 0); foreach (var stat in useBonuses) { BaseStats[stat.StatType] += stat.Value; } if (item.HP != 0) HP += item.HP; if (item.MP != 0) MP += item.MP; if (item.ActionDisplayID.HasValue) { using (var pw = ServerPacket.CreateActionDisplayAtEntity(item.ActionDisplayID.Value, MapEntityIndex)) { Map.Send(pw, ServerMessageType.MapEffect); } } return true; }
/// <summary> /// Makes the Character use an item. /// </summary> /// <param name="item">Item to use.</param> /// <param name="inventorySlot">Inventory slot of the item being used, or null if not used from the inventory.</param> /// <returns>True if the item was successfully used, else false.</returns> public bool UseItem(ItemEntity item, InventorySlot? inventorySlot) { if (!IsAlive) { const string errmsg = "`{0}` tried to use item `{1}` while dead."; if (log.IsInfoEnabled) log.InfoFormat(errmsg, this, item); return false; } // Check for a valid amount if (item.Amount <= 0) { const string errmsg = "`{0}` attempted to use item `{1}`, but the amount was invalid."; Debug.Fail(string.Format(errmsg, this, item)); if (log.IsErrorEnabled) log.ErrorFormat(errmsg, this, item); return false; } // Use the item based on the item's type bool wasUsed; switch (item.Type) { case ItemType.Unusable: wasUsed = false; break; case ItemType.UseOnce: wasUsed = UseItemUseOnce(item); break; case ItemType.Weapon: case ItemType.Helmet: case ItemType.Body: wasUsed = UseEquipment(item, inventorySlot); break; default: // Unhandled item type const string errmsg = "`{0}` attempted to use item `{1}`, but it contains invalid or unhandled ItemType `{2}`."; Debug.Fail(string.Format(errmsg, this, item, item.Type)); if (log.IsErrorEnabled) log.ErrorFormat(errmsg, this, item, item.Type); wasUsed = false; break; } if (wasUsed) { OnUsedItem(item); if (UsedItem != null) UsedItem.Raise(this, EventArgsHelper.Create(item)); } return wasUsed; }
/// <summary> /// When overridden in the derived class, allows for additional handling of the /// <see cref="Character.DroppedItem"/> event. It is recommended you override this method instead of /// using the corresponding event when possible. /// </summary> /// <param name="item">The item that was dropped.</param> protected virtual void OnDroppedItem(ItemEntity item) { }
/// <summary> /// Gets the default amount of money a Character will pay for buying the given <paramref name="item"/> from /// a shop. /// </summary> /// <param name="item">The item to purchase.</param> /// <returns>the default amount of money a Character will pay for buying the given <paramref name="item"/> /// from a shop.</returns> public static int GetItemBuyValue(ItemEntity item) { return(item.Value); }
/// <summary> /// Initializes a new instance of the <see cref="ItemEntity"/> class. /// </summary> /// <param name="s">The <see cref="ItemEntity"/> to copy the values from.</param> ItemEntity(ItemEntity s) : this( s.Position, s.Size, s.ItemTemplateID, s.Name, s.Description, s.Type, s.WeaponType, s.Range, s.GraphicIndex, s.Value, s.Amount, s.HP, s.MP, s.EquippedBody, s.ActionDisplayID, s.BaseStats, s.ReqStats) { }
/// <summary> /// Gets the default amount of money a Character will get for selling the given <paramref name="item"/> to /// a shop. /// </summary> /// <param name="item">The item to sell.</param> /// <returns>the default amount of money a Character will get for selling the given <paramref name="item"/> /// to a shop.</returns> public static int GetItemSellValue(ItemEntity item) { return(Math.Max(item.Value / 2, 1)); }
/// <summary> /// Splits the ItemEntity into two parts. This ItemEntity's amount will be decreased, and a new /// ItemEntity will be constructed as the product of the method. The original ItemEntity must still have /// an amount of at least one for the split to succeed. /// </summary> /// <param name="amount">Amount of the ItemEntity for the new part to contain. This must be less than /// the Amount of the existing ItemEntity, since both resulting ItemEntities must have an amount of /// at least 1.</param> /// <returns>New ItemEntity of the specified <paramref name="amount"/>, or null if the specified /// amount of the ItemEntity could not be acquired.</returns> public ItemEntity Split(byte amount) { // Check for a valid amount if (Amount <= 0) { Debug.Fail("Tried to Split() an ItemEntity with an Amount <= 0."); return null; } // Check if we can't perform a full split if (amount >= Amount) return null; // Create the new ItemEntity var child = new ItemEntity(this) { Amount = amount }; // Lower the amount of this ItemEntity Amount -= amount; return child; }
public static PacketWriter SendInventoryItemInfo(InventorySlot slot, ItemEntity item) { var pw = GetWriter(ServerPacketID.SendInventoryItemInfo); pw.Write(slot); new ItemTable(item).WriteState(pw); return pw; }
/// <summary> /// When overridden in the derived class, allows for additional handling of /// when the <see cref="Character"/> unequips an item. /// Use this overload instead of adding an event listener to the corresponding event when possible. /// </summary> /// <param name="item">The <see cref="ItemEntity"/> that was unequipped.</param> /// <param name="slot">The slot the <paramref name="item"/> was unequipped in.</param> protected virtual void OnUnequipped(ItemEntity item, EquipmentSlot slot) { }
/// <summary> /// Gets the default amount of money a Character will get for selling the given <paramref name="item"/> to /// a shop. /// </summary> /// <param name="item">The item to sell.</param> /// <returns>the default amount of money a Character will get for selling the given <paramref name="item"/> /// to a shop.</returns> public static int GetItemSellValue(ItemEntity item) { return Math.Max(item.Value / 2, 1); }
/// <summary> /// When overridden in the derived class, allows for additional handling of the /// <see cref="Character.KilledByCharacter"/> event. It is recommended you override this method instead of /// using the corresponding event when possible. /// </summary> /// <param name="item">The item that was used.</param> protected virtual void OnUsedItem(ItemEntity item) { if (item.Type == ItemType.UseOnce && item.ItemTemplateID.HasValue) { WorldStatsTracker.Instance.AddCountConsumeItem((int)item.ItemTemplateID.Value); EventCounterManager.ItemTemplate.Increment(item.ItemTemplateID.Value, ItemTemplateEventCounterType.Consume); } }
/// <summary> /// Creates an ItemEntity on the map. /// </summary> /// <param name="template">ItemTemplate to create the characterID from.</param> /// <param name="pos">Position to create the characterID at.</param> /// <param name="amount">Amount of the characterID to create. Must be greater than 0.</param> /// <returns>Reference to the new ItemEntity created.</returns> public ItemEntity CreateItem(IItemTemplateTable template, Vector2 pos, byte amount) { // Check for a valid amount if (amount < 1) { const string errmsg = "Invalid characterID amount `{0}`! Amount must be > 0."; Debug.Fail(string.Format(errmsg, amount)); if (log.IsErrorEnabled) log.ErrorFormat(errmsg, amount); return null; } // Check for a valid template if (template == null) { const string errmsg = "Parameter `template` may not be null!"; Debug.Fail(errmsg); if (log.IsErrorEnabled) log.Error(errmsg); return null; } // Create the characterID, add it to the map, and return the reference var item = new ItemEntity(template, pos, amount); AddEntity(item); return item; }
/// <summary> /// Uses a use-once item. /// </summary> /// <param name="item">The item to be used.</param> /// <returns>True if the item was used; otherwise false.</returns> bool UseItemUseOnce(ItemEntity item) { var useBonuses = item.BaseStats.Where(stat => stat.Value != 0); foreach (var stat in useBonuses) { BaseStats[stat.StatType] += stat.Value; } if (item.HP != 0) HP += item.HP; if (item.MP != 0) MP += item.MP; return true; }