|
Custom Script Releases This forum is where you can release your custom scripts for other users to use. Please note: By releasing your scripts here you are submitting them to the public and as such agree to publish them under the GPL licensing terms. The RunUO Team has made its software GPL for you to use and enjoy you should do the same for anything based off of RunUO. |
|
Thread Tools | Display Modes |
06-30-2006, 03:36 PM | #1 (permalink) |
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
[RunUO 2.0 RC1] XmlSockets - item/creature enhancement system
XmlSockets
v1.08a RunUO 2.0 version updated 6/18/06 ArteGordon Latest News: Version 1.08a has been updated to run under RunUO 2.0. - Lootpack mods package will be added. Summary: This set of attachments provides a system for adding sockets and socket augmentations to items and mobs. The XmlSockets attachment adds sockets that can be filled with augmentations that will enhance specific abilities. The XmlSocketable attachment enables player-addable sockets that can be added to the target object by players meeting customizable skill and resources requirements. Recent Updates: New to v1.08a updated 4/13/05 - added a new powerscroll transmutation recipe to the BoxOfTransmutation that takes any 3 powerscrolls of the same level, along with a legendary diamond, legendary ruby, and legendary emerald, and creates a new random powerscroll of the same level. - added some recipes for transmuting large and small BODs. Transmuting a small BOD and 2000 gp will give you a new random small BOD of the same type. Transmuting 20000 gp and a large BOD will give you a random large BOD of the same type. - the XmlMobFactions recipes are now properly commented out for those that arent using that system. New to v1.08 updated 3/10/05 - slightly modified the transmutation rules so that a recipe that specifies an ingredient in any quantity (quantity requirement of 0) still requires at least one of that ingredient. - added the option to specify a second skill requirement for socketing (thanks to Greystar for the suggestion). The XmlSocketable attachment now has 2 additional properties, RequiredSkill2 and MinSkillLevel2. By default MinSkillLevel2 is 0 so there is no second skill requirement. To support the requirement for a second skill, several new methods and constructors have been added. You can spawn an object with a second socketable skill requirement like this buckler/ATTACH/xmlsocketable,2,Tinkering,115,ArmsLore,70,gold,200 0 You can also randomly configure a socketed/socketable object with two skill requirements in scripts using the new ConfigureRandom overload method, like this XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0, SkillName.Veterinary, 100.0, SkillName.AnimalLore, 90.0, typeof(Emerald), 30); which will add a random socketable/socketed configuration to the target with a minimum 100 veterinary skill as well as a minimum 90 animal lore skill on socketing. There is also a new XmlSocketable constructor that accepts the additional skill requirements if you wish to add socketing to items in your own scripts. public XmlSocketable(int maxsockets, SkillName skillname, double minskilllevel, SkillName skillname2, double minskilllevel2, Type resource, int quantity) - added the ability to recover augmentations from sockets. Specific augmentations must have the ability to be recovered enabled by defining the CanRecover and OnRecover override methods in their scripts. If these are not defined for a particular augmentation, then they will not be recoverable. Augmentations that can be recovered and are already socketed into an item will be identified as such by clicking on their socket in the socket display gump. If the augmentation can be recovered you will see "Can be recovered" at the end of the description. Note that it is possible for an augmentation to be recoverable in certain items and not others if you wish, or even dependent on the individual trying to do the recovery (based on karma, fame, etc.). Just put the desired conditions in the CanRecover override method for the given augmentation. A version argument has been added to the CanRecover and OnRecover calls to allow you to make changes to augmentations but still control how older versions are handled. If you do make changes to existing augmentations you should override the Version property for that augmentation and return an updated value (the default is zero). If you make changes to the number of sockets used by an augmentation and previous versions of the augmentation been used, and you make the augmentation recoverable, then you should override the RecoverableSockets method and return the proper value based on the version number. - added a new recipe to the BoxOfTransmutation for recovering augmentations from items. Transmuting an augmented item and an AncientRuby will allow a random augmentation from the item to be recovered. - added the new HammerOfRecovery item that can be used to recover augmentations from a target. This can be used to recover augmentations from items or creatures. - added a new recipe to the BoxofTransmutation for creating a single use HammerOfRecovery from a regular hammer and an AncientRuby. This allows players with the proper skills and resources to create a hammer that could be given to other players to allow them to remove augmentations. - updated the crystal, radiant crystal, gem, and skull augments to support recovery. New to v1.07 updated 2/13/05 - fixed a possible crash bug involving the display of Socket properties on items that are on the generic vendor buy lists. - added a new transmutation container item, the BagOfResources that takes the arrow/bolt recipes and the hides/bandage recipe out of the BoxOfTransmutation. The BoxOfTransmutation now just contains augmentation related recipes. New to v1.06 updated 1/31/05 - added an interface specification to some of the existing augments to facilitate their use in the new transmutation system. - added the new items BoxOfTransmutation and MagicBasket. They can be made with limited or unlimited uses (setting the UsesRemaining property to a value less than zero gives unlimited use). - added the new BaseTransmutationContainer class that allows you to make containers that can magically create objects based upon specifiable recipes. The recipes are made up of lists of ingredients that when placed into the container in exactly the right quantity and with the correct properties, can be transformed into other objects. Each recipe can also optionally have multiple minimum skill and stat requirements. Two examples of transmutation devices are included. To test them out just [add magicbasket, or [add boxoftransmutation. They can also be made with limited uses with e.g. [add magicbasket 50 for a 50 use basket. The transmutation gump can be opened either by placing objects into the container, or by selecting the 'Use this device' context menu option. The MagicBasket has one recipe defined that requires cooking skill and makes a meat pie out when the following ingredients are used: 2 raw ribs, 1 garlic, 1 bowl of flour, 1 full pitcher of milk, 4 carrots. It requires 30 cooking skill, 30 dex, and 20 int. The BoxOfTransmutation has recipes for manipulating augments, and socketed weapons/armor, and a few other things. Here are the recipes that it has defined: HidesToBandages: will change any amount of hides into bandages Req - 50 str, 30 dex BoltsToArrows: will change any amount of bolts into half the quantity of arrows Req - 50 ArmsLore, 70 str, 30 dex ArrowsToBolts: will change any amount of arrows into half the quantity of bolts Req - 50 ArmsLore, 70 str, 30 dex UpgradeAncientAugment: will take 6 Ancient augments and convert them to a Legendary augment of the same type. Req - 50 Alchemy, 70 str, 30 dex, 40 int UpgradeLegendaryAugment: will take 3 Legendary augments and convert them to a Mythic augment of the same type. Req - 70 Alchemy, 70 str, 30 dex, 40 int UpgradeCrystalAugment: will take 3 Crystal augments and convert them to a Radiant augment of the same type. Req - 70 Alchemy, 70 str, 30 dex, 40 int SocketWeapon: will take any unsocketed weapon and a Rho rune augment and add a single socket to the weapon. Req - 50 Blacksmith, 70 str, 30 dex, 40 int SocketArmor: will take any unsocketed armor and a Rho rune augment and add a single socket to the armor. Req - 50 Blacksmith, 70 str, 30 dex, 40 int some additional recipes that allow various mob faction bonuses to be granted with recipes that use a combination of glimmering stone augments and gold are included. These require the XmlMobFactions system to be installed. These are commented out by default. If you have XmlMobFactions installed and you wish to use these, just uncomment them. An arbitrary number of additional recipes can be specified. You could also construct different transmutation container classes (derived from the BaseTransmutationContainer class) that implemented different recipe sets. New to v1.05 updated 1/22/05 - made an adjustment to the default gump art hues for augments so that they match the item hues. - added another gem augment (tourmaline) and updated the optional Loot.cs to reflect this. - added a set of glimmering stone augments that grant various skill bonuses. - updated installation instructions for 1.0.0 - updated the optional modified lootpack.cs/loot.cs files to 1.0.0 Description: This system makes use of the XmlSpawner2 attachment system and the XmlSpawner2 package must be installed to support it. You dont need to use the xmlspawners themselves, and it doesnt matter whether you use the standard distribution spawners or any other spawning system but you do need the XmlSpawner2 package installed. Because the attachment system neither requires nor makes any changes to serialization/deserializations of any item or mobile, it can be safely added and removed at any time now or in the future without interfering with any other systems that you might have installed. Note, this is intended as more of a development system for people to add custom content to their shards than just a drop-and-play system with a full complement of socket augmentations. While I have included a number of augmentations in the package that can be used as-is, they are intended to illustrate its features so that users can extend them with additional content. Features: Note that ANY object can in principle be socketed - weapons, armor, jewelry, clothing, tools, trees, doors, fruit, creatures, players, gems - anything. You just have to tag them with the sockets/socketable attachments using one of the methods described below. It is then the responsibility of the augment to determine what it can affect and what effect it has on the socketed object. Sockets Sockets allow target objects to be enhanced by providing slots in which specially designed augmentations can be placed. Different augmentations will confer different abilities to the target. Sockets can either come pre-existing on a target, or can be added by an individual with sufficient skill and resources. Socketability Individual objects can be flagged as being "socketable", by adding the XmlSocketable attachment which allows the maximum number of sockets, the type of skill and minimum skill level required to socket, and the resources used in socketing, to be specified. By default 100 Blacksmithing and 50 Valorite ingots are required to add a socket to a target. Augmenting To add an augmentation to a socket, both the augmentation and the item to be augmented must be in the players pack. To bring up the socket gump you must use the item identification skill on the item. In the gump, click on the socket and target the augmentation. By default, augmenting requires no resources and will always succeed. This behavior can be changed in the ConsumeOnAugment and OnAugment override methods for individual augmentations. After a socket has been filled with an augmentation, clicking on the socket will display the augmentation properties. Item Identification. This skill is used to display the sockets and socketability of target objects. Just use the skill and target an object (either items or creatures). Examples. Examples of socketed/socketable objects including tamable creatures such as a socketable dragon are included in the example .xml file socket1.xml. To try this out, place the file "socket1.xml" in your top level RunUO directory and execute the command "[xmlloadhere socket1.xml" It creates several bags of augmentations and various socketed and socketable items. The dragon can be socketed after it is tamed. Commands/Control Items: [addsocket - To add a socket to a socketable object the player issues the "[addsocket" command and targets an object. If the target is an item, then the item must be in the players backpack. If the target is a creature, then the creature must be under the players control. If socketing is successful, you will see the new socket in the socket gump. On each failure resources will still be consumed and there is a 10% chance that the target will be destroyed (this probability can be set by the static DefaultDestructionProbability variable in XmlSocket.cs). The difficulty of adding a socket depends on the minimum skill required by the object, the skill level of the player, and the existing number of sockets. The chance of succeeding in adding a socket can be displayed by using the Item Identification skill on a socketable object. SocketHammer - an item that provides the same functionality as the [addsocket command. Just double-click to use and target the object to be socketed. It can be given either limited (set UsesRemaining to a value > 0) or unlimited number of uses (set UsesRemaining to a value < 0). Adding sockets to items/mobs Manually to individual item/mobs - the xmlSockets attachment can be added to objects in several ways. It can be manually attached to any existing object with the "[addatt" command. For example "[addatt xmlsockets 4" will add 4 sockets to a targeted object. To scripts - Sockets can also be added to scripted objects. See the included script TestSocketedWeapon.cs for an example of this. To spawned objects - To add sockets to spawned objects use the ATTACH keyword in spawn entries such as this katana/ATTACH/xmlsockets,4 which would spawn a katana with 4 sockets. or orc/ADD/<katana/ATTACH/xmlsockets,4> would spawn an orc carrying the socketd katana. To try out the included example spawner .xml file, place the file "socket1.xml" in your top level RunUO directory and execute the command "[xmlloadhere socket1.xml" Making items/mobs socketable by players Manually to individual item/mobs - the xmlSocketable attachment can be added to objects in several ways. It can be manually attached to any existing object with the "[addatt" command. For example "[addatt xmlsocketable 4" will flag the target object as socketable and supporting a maximum of 4 player-added sockets. To scripts - Socketability can also be added to scripted objects. See the included script TestSocketedWeapon.cs for an example of this. To spawned objects - To add sockets to spawned objects use the ATTACH keyword in spawn entries such as this katana/ATTACH/xmlsocketable,4 which would spawn a katana supporting a maximum of 4 player-added sockets. buckler/ATTACH/xmlsocketable,2,Tinkering,115,gold,2000 which would spawn a buckler supporting a maximum of 2 player-added sockets that require a minimum of 115 Tinkering and 2000 gold to add. Installation: For 1.0.0 STEP 1: Install the latest XmlSpawner2 package. You can find it here XmlSpawner2. Make sure that you perform installation step 8 from that thread. I would also strongly suggest performing step 11 from that thread to allow socket properties to be automatically displayed. STEP 2: Place the scripts from this package into your custom scripts directory, or into the XmlAttachments area of your XmlSpawner2 installation. (anywhere in your Scripts folder is fine, these locations are suggested for organizational purposes only). Place the optional .xml example in the top level of your RunUO installation directory if you would like to try it out. If you have also installed the custom attacks/defenses system (XmlCustomAttacks) then you can extract the runeaugments.cs file. If you do not have that system, then remove runeaugments.cs from the Augments folder. STEP 3: (optional) To automatically add sockets and socketability to dropped weapons and armor and tamable creatures with some probability, you can make these changes to BaseArmor.cs, BaseWeapon.cs, BaseJewel.cs, and BaseCreature.cs. If you make this modification, all weapons/armor/tamable creatures will have some chance of having sockets when they are made. If you would rather put the modifications somewhere else, such as in LootPack.cs, or in scripts for specific weapons/armor/creatures/jewelry/etc. that would work as well. You could have random sockets/socketability automatically added to anything such as jewelry or any custom items that you have by adding in the XmlSockets.ConfigureRandom call into their constructors in the same way it is illustrated below. First, place the following line at the beginning of the files BaseArmor.cs, BaseWeapon.cs, BaseJewel.cs, and BaseCreature.cs Code:
using Server.Engines.XmlSpawner2; at the end of the BaseArmor constructor around line 1131 in Scripts/Items/Armor/BaseArmor.cs change this Code:
m_AosSkillBonuses = new AosSkillBonuses( this ); Code:
m_AosSkillBonuses = new AosSkillBonuses( this ); // mod to randomly add sockets and socketability features to armor. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // uncomment the next line to prevent artifacts from being socketed // if(ArtifactRarity == 0) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0); change this Code:
m_AosElementDamages = new AosElementAttributes( this ); Code:
m_AosElementDamages = new AosElementAttributes( this ); // mod to randomly add sockets and socketability features to weapons. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // uncomment the next line to prevent artifacts from being socketed // if(ArtifactRarity == 0) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0); change this Code:
Layer = layer; Code:
Layer = layer; // mod to randomly add sockets and socketability features to weapons. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // uncomment the next line to prevent artifacts from being socketed // if(ArtifactRarity == 0) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0); Code:
public override void OnBeforeSpawn( Point3D location, Map m ) { if ( Paragon.CheckConvert( this, location, m ) ) IsParagon = true; base.OnBeforeSpawn( location, m ); } Code:
public override void OnBeforeSpawn( Point3D location, Map m ) { if ( Paragon.CheckConvert( this, location, m ) ) IsParagon = true; // mod to randomly add socket features to tamable creatures. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // Adding new sockets will require a minimum of 100 Veterinary skill and 30 emeralds if(Tamable) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0, SkillName.Veterinary, 100.0, typeof(Emerald), 30); base.OnBeforeSpawn( location, m ); } STEP 4: (optional) I have added modified distro 1.0.0 versions of Loot.cs and LootPack.cs that will give random lootpack drops of augmentations. The frequency of drop is low (about the same as instruments), and the definitions include the rune augmentations (if you dont have runeaugments.cs installed then comment out the sections referring to the runes). To use them, either replace the distro Scripts/Misc/LootPack.cs and Scripts/Misc/Loot.cs with the versions in the SocketsLootMod.zip file (always make backups of your original scripts), or just look in those files for the changes marked "ARTEGORDONMOD", and incorporate them into your existing lootpack.cs and loot.cs files. Note, I intentionally renamed the files with ._cs extensions so that they wouldnt cause problems if you extracted them into your scripts area. The extensions need to be changed to .cs if you wish to use them as replacements for the distro loot.cs and lootpack.cs scripts. Changelog: Version 1.04 updated 12/26/04 - added support for adding augments via scripts instead of just via player targeting (thanks to sUpplier1 for the suggestion). - added a new example of this feature in testsocketedweapon.cs (case 3) - minor change in reporting of objects that have already reached their maximum number of allowed sockets. It no longer describes the requirements and socketing chance for such objects. - minor change in property list display of socketable objects. The resource and skill requirements will no longer be displayed on mouseover. That info can still be obtained by using the Item Identification skill on the object. - the random socket/socketable configuration method "ConfigureRandom" will no longer create 0-socketable configurations (thanks to sUpplier1 for pointing this out). - added jewelry support to the crystal and radiant crystal augments. - modified installation step 3 to support randomly socketed jewelry drops and to allow you to exclude the possibility of socketed artifacts if you like. Version 1.03 updated 12/07/04 - added 29 new augmentations in two sets, Crystals (2 socket) and RadiantCrystals (4 socket). - modified the optional Loot.cs file to support lootpack drops of these augments. - updated the socket1.xml example to include a bags of the new augments. Version 1.02 update 12/03/04 - added an additional XmlSockets constructor that allows the default requirement for socketed items to be in the players pack when augmenting to be overridden. "[addatt xmlsockets 1 false" for example would allow you to attach a single socket that did not require the socketed item to be in the players pack when augmenting. This can be useful for allowing items in the world such as doors to be augmented. - added a new augmentation called a "keyaugment". This can be used to open doors and chests by assigning it a KeyValue that matches the KeyValue of the door/chest, and then using it to augment a socket on the door/chest. A skill requirement can also be assigned to it as can the number of uses. This is an example of placing additional constraints on the use of augmentations in the OnAugment method. - added an example of a socketed door and socketed chest that open when a master thief places the proper keyaugment into the socket. This can be tested by loading the keyaugment.xml example. Use item identify on the door named "Sesame" to bring up the socket gump, and then augment it with the special "Sesame" rune (you must have at least 100 lockpicking). After opening, the door resockets itself so that it has to be augmented again to be opened. The chest can be opened in the same way with the "Sesame's treasure" keyaugment that has the form of a skull. That one requires 100 Stealing to use. Notice how the keyaugment to open the door does not get destroyed after it is used, while the one to the chest does. This is due to the UsesRemaining property that has been set on it. Setting this to a value < 0, gives it unlimited uses. Setting it to a value > 0 gives it the specified number of uses (a value of 1 would be the default single-use case). - shifted the SocketAugmentations entries in the optional LootPack.cs mods to the ends of each lootpack list to reduce luck-enhanced chance of dropping augments. The way lootpacks work, entries near the top of the list have the greatest chance of being added due to luck, and the luck-computed drop probability is generally much higher than the drop chance specified for each entry (particularly if the entry chance is supposed to be low which is why you get so many luck-related magic item drops on poor lootpack mobs). - added a range check for socketing creatures (maximum distance 2) Version 1.01 updated 12/1/04 - added a set of Mythic augments that are 3 socket augmentations in addition to the Ancient (1 socket) and Legendary (2 socket) types. Loot.cs in the optional SocketsLootMod-v101.zip was modified to reflect the change. - the augment examples have been organized into a separate folder replacing the previous files otheraugments.cs, gemaugments.cs, and runeaugments.cs (you should get rid of those if you had them from an earlier installation or your will get errors about things being already defined). - the socket1.xml example has been updated with the new augments. - added the SocketHammer item as an alternative to the [addsocket command to add sockets to target objects. Just "[add sockethammer", double click the hammer and target the object to be socketed. Setting the number of uses (UsesRemaining property) to a value greater than zero will give it limited uses. Setting it less than zero makes it unlimited. - having an item with an existing socket number that exceeds the maxsockets indicated in its XmlSocketable attachment will now automatically have that max adjusted to match the current number of existing sockets. It is basically just a cosmetic change to avoid confusion in the way the available sockets are reported. Some players were getting confused by items that indicated the maxsockets allowed as 1, and the available sockets as 2 for example. This just meant that the item already had 2 sockets, but that you could not add any additional sockets beyond 1.
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum |
07-03-2006, 05:11 PM | #2 (permalink) | |
Forum Novice
Join Date: May 2005
Age: 19
Posts: 270
|
Arte sorry to be a bother, but on your post it says
Quote:
__________________
Burning Venganc |
|
07-03-2006, 05:16 PM | #3 (permalink) | ||
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
Quote:
Quote:
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum Last edited by ArteGordon; 07-03-2006 at 05:19 PM. |
||
07-04-2006, 11:47 AM | #4 (permalink) |
Forum Novice
Join Date: May 2005
Age: 19
Posts: 270
|
oops sorry also, shouldnt there be a modified Itemid? because atm itemid doesnt bring up the sockets thing like its meant to...
__________________
Burning Venganc |
07-04-2006, 11:52 AM | #5 (permalink) | |||
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
Quote:
Quote:
Quote:
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum |
|||
07-04-2006, 11:56 AM | #6 (permalink) |
Forum Novice
Join Date: May 2005
Age: 19
Posts: 270
|
Ah thanks alot, feel a bit stupid now :P
__________________
Burning Venganc |
07-06-2006, 10:38 AM | #7 (permalink) |
Newbie
Join Date: Jul 2005
Age: 44
Posts: 31
|
Baseweapon.cs+basejewel.cs
Hi Artgordon
I love this sytem been playibg with it on another so though must have it on my new 2.0rc1 server but am having this error come up HTML Code:
Scripts: One or more scripts failed to compile or no script files were found. - Press return to exit, or R to try again. Scripts: Compiling C# scripts...failed (2 errors, 5 warnings) Errors: + custom/system's and tools/XmlSpawner2_20_v312_2of3/XmlAttach/XmlSockets-20-v1 08a/Base mods for sockets/BaseWeapon.cs: CS0103: Line 2732: The name 'XmlSockets' does not exist in the current conte xt + custom/system's and tools/XmlSpawner2_20_v312_2of3/XmlAttach/XmlSockets-20-v1 08a/Base mods for sockets/BaseJewel.cs: CS0103: Line 103: The name 'XmlSockets' does not exist in the current contex t Scripts: One or more scripts failed to compile or no script files were found. - Press return to exit, or R to try again. Basejewel.cs Code:
using System; using Server.Engines.Craft; using Server.Engines.XmlSpawner2; namespace Server.Items { public enum GemType { None, StarSapphire, Emerald, Sapphire, Ruby, Citrine, Amethyst, Tourmaline, Amber, Diamond } public abstract class BaseJewel : Item, ICraftable { private AosAttributes m_AosAttributes; private AosElementAttributes m_AosResistances; private AosSkillBonuses m_AosSkillBonuses; private CraftResource m_Resource; private GemType m_GemType; [CommandProperty( AccessLevel.GameMaster )] public AosAttributes Attributes { get{ return m_AosAttributes; } set{} } [CommandProperty( AccessLevel.GameMaster )] public AosElementAttributes Resistances { get{ return m_AosResistances; } set{} } [CommandProperty( AccessLevel.GameMaster )] public AosSkillBonuses SkillBonuses { get{ return m_AosSkillBonuses; } set{} } [CommandProperty( AccessLevel.GameMaster )] public CraftResource Resource { get{ return m_Resource; } set{ m_Resource = value; Hue = CraftResources.GetHue( m_Resource ); } } [CommandProperty( AccessLevel.GameMaster )] public GemType GemType { get{ return m_GemType; } set{ m_GemType = value; InvalidateProperties(); } } public override int PhysicalResistance{ get{ return m_AosResistances.Physical; } } public override int FireResistance{ get{ return m_AosResistances.Fire; } } public override int ColdResistance{ get{ return m_AosResistances.Cold; } } public override int PoisonResistance{ get{ return m_AosResistances.Poison; } } public override int EnergyResistance{ get{ return m_AosResistances.Energy; } } public virtual int BaseGemTypeNumber{ get{ return 0; } } public override int LabelNumber { get { if ( m_GemType == GemType.None ) return base.LabelNumber; return BaseGemTypeNumber + (int)m_GemType - 1; } } public virtual int ArtifactRarity{ get{ return 0; } } public BaseJewel( int itemID, Layer layer ) : base( itemID ) { m_AosAttributes = new AosAttributes( this ); m_AosResistances = new AosElementAttributes( this ); m_AosSkillBonuses = new AosSkillBonuses( this ); m_Resource = CraftResource.Iron; m_GemType = GemType.None; Layer = layer; // mod to randomly add sockets and socketablility features to jewels. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5 of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // uncomment the following line to prevent artifacts from being socketed // if(ArtifactRarity == 0) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0); } public override void OnAdded( object parent ) { if ( Core.AOS && parent is Mobile ) { Mobile from = (Mobile)parent; m_AosSkillBonuses.AddTo( from ); int strBonus = m_AosAttributes.BonusStr; int dexBonus = m_AosAttributes.BonusDex; int intBonus = m_AosAttributes.BonusInt; if ( strBonus != 0 || dexBonus != 0 || intBonus != 0 ) { string modName = this.Serial.ToString(); if ( strBonus != 0 ) from.AddStatMod( new StatMod( StatType.Str, modName + "Str", strBonus, TimeSpan.Zero ) ); if ( dexBonus != 0 ) from.AddStatMod( new StatMod( StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero ) ); if ( intBonus != 0 ) from.AddStatMod( new StatMod( StatType.Int, modName + "Int", intBonus, TimeSpan.Zero ) ); } from.CheckStatTimers(); } } public override void OnRemoved( object parent ) { if ( Core.AOS && parent is Mobile ) { Mobile from = (Mobile)parent; m_AosSkillBonuses.Remove(); string modName = this.Serial.ToString(); from.RemoveStatMod( modName + "Str" ); from.RemoveStatMod( modName + "Dex" ); from.RemoveStatMod( modName + "Int" ); from.CheckStatTimers(); } } public BaseJewel( Serial serial ) : base( serial ) { } public override void GetProperties( ObjectPropertyList list ) { base.GetProperties( list ); m_AosSkillBonuses.GetProperties( list ); int prop; if ( (prop = ArtifactRarity) > 0 ) list.Add( 1061078, prop.ToString() ); // artifact rarity ~1_val~ if ( (prop = m_AosAttributes.WeaponDamage) != 0 ) list.Add( 1060401, prop.ToString() ); // damage increase ~1_val~% if ( (prop = m_AosAttributes.DefendChance) != 0 ) list.Add( 1060408, prop.ToString() ); // defense chance increase ~1_val~% if ( (prop = m_AosAttributes.BonusDex) != 0 ) list.Add( 1060409, prop.ToString() ); // dexterity bonus ~1_val~ if ( (prop = m_AosAttributes.EnhancePotions) != 0 ) list.Add( 1060411, prop.ToString() ); // enhance potions ~1_val~% if ( (prop = m_AosAttributes.CastRecovery) != 0 ) list.Add( 1060412, prop.ToString() ); // faster cast recovery ~1_val~ if ( (prop = m_AosAttributes.CastSpeed) != 0 ) list.Add( 1060413, prop.ToString() ); // faster casting ~1_val~ if ( (prop = m_AosAttributes.AttackChance) != 0 ) list.Add( 1060415, prop.ToString() ); // hit chance increase ~1_val~% if ( (prop = m_AosAttributes.BonusHits) != 0 ) list.Add( 1060431, prop.ToString() ); // hit point increase ~1_val~ if ( (prop = m_AosAttributes.BonusInt) != 0 ) list.Add( 1060432, prop.ToString() ); // intelligence bonus ~1_val~ if ( (prop = m_AosAttributes.LowerManaCost) != 0 ) list.Add( 1060433, prop.ToString() ); // lower mana cost ~1_val~% if ( (prop = m_AosAttributes.LowerRegCost) != 0 ) list.Add( 1060434, prop.ToString() ); // lower reagent cost ~1_val~% if ( (prop = m_AosAttributes.Luck) != 0 ) list.Add( 1060436, prop.ToString() ); // luck ~1_val~ if ( (prop = m_AosAttributes.BonusMana) != 0 ) list.Add( 1060439, prop.ToString() ); // mana increase ~1_val~ if ( (prop = m_AosAttributes.RegenMana) != 0 ) list.Add( 1060440, prop.ToString() ); // mana regeneration ~1_val~ if ( (prop = m_AosAttributes.NightSight) != 0 ) list.Add( 1060441 ); // night sight if ( (prop = m_AosAttributes.ReflectPhysical) != 0 ) list.Add( 1060442, prop.ToString() ); // reflect physical damage ~1_val~% if ( (prop = m_AosAttributes.RegenStam) != 0 ) list.Add( 1060443, prop.ToString() ); // stamina regeneration ~1_val~ if ( (prop = m_AosAttributes.RegenHits) != 0 ) list.Add( 1060444, prop.ToString() ); // hit point regeneration ~1_val~ if ( (prop = m_AosAttributes.SpellChanneling) != 0 ) list.Add( 1060482 ); // spell channeling if ( (prop = m_AosAttributes.SpellDamage) != 0 ) list.Add( 1060483, prop.ToString() ); // spell damage increase ~1_val~% if ( (prop = m_AosAttributes.BonusStam) != 0 ) list.Add( 1060484, prop.ToString() ); // stamina increase ~1_val~ if ( (prop = m_AosAttributes.BonusStr) != 0 ) list.Add( 1060485, prop.ToString() ); // strength bonus ~1_val~ if ( (prop = m_AosAttributes.WeaponSpeed) != 0 ) list.Add( 1060486, prop.ToString() ); // swing speed increase ~1_val~% base.AddResistanceProperties( list ); } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); writer.Write( (int) 2 ); // version writer.WriteEncodedInt( (int) m_Resource ); writer.WriteEncodedInt( (int) m_GemType ); m_AosAttributes.Serialize( writer ); m_AosResistances.Serialize( writer ); m_AosSkillBonuses.Serialize( writer ); } public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); switch ( version ) { case 2: { m_Resource = (CraftResource)reader.ReadEncodedInt(); m_GemType = (GemType)reader.ReadEncodedInt(); goto case 1; } case 1: { m_AosAttributes = new AosAttributes( this, reader ); m_AosResistances = new AosElementAttributes( this, reader ); m_AosSkillBonuses = new AosSkillBonuses( this, reader ); if ( Core.AOS && Parent is Mobile ) m_AosSkillBonuses.AddTo( (Mobile)Parent ); int strBonus = m_AosAttributes.BonusStr; int dexBonus = m_AosAttributes.BonusDex; int intBonus = m_AosAttributes.BonusInt; if ( Parent is Mobile && (strBonus != 0 || dexBonus != 0 || intBonus != 0) ) { Mobile m = (Mobile)Parent; string modName = Serial.ToString(); if ( strBonus != 0 ) m.AddStatMod( new StatMod( StatType.Str, modName + "Str", strBonus, TimeSpan.Zero ) ); if ( dexBonus != 0 ) m.AddStatMod( new StatMod( StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero ) ); if ( intBonus != 0 ) m.AddStatMod( new StatMod( StatType.Int, modName + "Int", intBonus, TimeSpan.Zero ) ); } if ( Parent is Mobile ) ((Mobile)Parent).CheckStatTimers(); break; } case 0: { m_AosAttributes = new AosAttributes( this ); m_AosResistances = new AosElementAttributes( this ); m_AosSkillBonuses = new AosSkillBonuses( this ); break; } } if ( version < 2 ) { m_Resource = CraftResource.Iron; m_GemType = GemType.None; } } #region ICraftable Members public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) { Type resourceType = typeRes; if ( resourceType == null ) resourceType = craftItem.Ressources.GetAt( 0 ).ItemType; Resource = CraftResources.GetFromType( resourceType ); CraftContext context = craftSystem.GetContext( from ); if ( context != null && context.DoNotColor ) Hue = 0; if ( 1 < craftItem.Ressources.Count ) { resourceType = craftItem.Ressources.GetAt( 1 ).ItemType; if ( resourceType == typeof( StarSapphire ) ) GemType = GemType.StarSapphire; else if ( resourceType == typeof( Emerald ) ) GemType = GemType.Emerald; else if ( resourceType == typeof( Sapphire ) ) GemType = GemType.Sapphire; else if ( resourceType == typeof( Ruby ) ) GemType = GemType.Ruby; else if ( resourceType == typeof( Citrine ) ) GemType = GemType.Citrine; else if ( resourceType == typeof( Amethyst ) ) GemType = GemType.Amethyst; else if ( resourceType == typeof( Tourmaline ) ) GemType = GemType.Tourmaline; else if ( resourceType == typeof( Amber ) ) GemType = GemType.Amber; else if ( resourceType == typeof( Diamond ) ) GemType = GemType.Diamond; } return 1; } #endregion } } Code:
using System; using System.Text; using System.Collections; using Server.Network; using Server.Targeting; using Server.Mobiles; using Server.Spells; using Server.Spells.Necromancy; using Server.Spells.Bushido; using Server.Spells.Ninjitsu; using Server.Factions; using Server.Engines.Craft; using Server.Engines.XmlSpawner2; using System.Collections.Generic; namespace Server.Items { public interface ISlayer { SlayerName Slayer { get; set; } SlayerName Slayer2 { get; set; } } public abstract class BaseWeapon : Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability { #region Factions private FactionItem m_FactionState; public FactionItem FactionItemState { get{ return m_FactionState; } set { m_FactionState = value; if ( m_FactionState == null ) Hue = CraftResources.GetHue( Resource ); LootType = ( m_FactionState == null ? LootType.Regular : LootType.Blessed ); } } #endregion /* Weapon internals work differently now (Mar 13 2003) * * The attributes defined below default to -1. * If the value is -1, the corresponding virtual 'Aos/Old' property is used. * If not, the attribute value itself is used. Here's the list: * - MinDamage * - MaxDamage * - Speed * - HitSound * - MissSound * - StrRequirement, DexRequirement, IntRequirement * - WeaponType * - WeaponAnimation * - MaxRange */ #region Var declarations // Instance values. These values are unique to each weapon. private WeaponDamageLevel m_DamageLevel; private WeaponAccuracyLevel m_AccuracyLevel; private WeaponDurabilityLevel m_DurabilityLevel; private WeaponQuality m_Quality; private Mobile m_Crafter; private Poison m_Poison; private int m_PoisonCharges; private bool m_Identified; private int m_Hits; private int m_MaxHits; private SlayerName m_Slayer; private SlayerName m_Slayer2; private SkillMod m_SkillMod, m_MageMod; private CraftResource m_Resource; private bool m_PlayerConstructed; private bool m_Cursed; // Is this weapon cursed via Curse Weapon necromancer spell? Temporary; not serialized. private bool m_Consecrated; // Is this weapon blessed via Consecrate Weapon paladin ability? Temporary; not serialized. private AosAttributes m_AosAttributes; private AosWeaponAttributes m_AosWeaponAttributes; private AosSkillBonuses m_AosSkillBonuses; private AosElementAttributes m_AosElementDamages; // Overridable values. These values are provided to override the defaults which get defined in the individual weapon scripts. private int m_StrReq, m_DexReq, m_IntReq; private int m_MinDamage, m_MaxDamage; private int m_HitSound, m_MissSound; private int m_Speed; private int m_MaxRange; private SkillName m_Skill; private WeaponType m_Type; private WeaponAnimation m_Animation; #endregion #region Virtual Properties public virtual WeaponAbility PrimaryAbility{ get{ return null; } } public virtual WeaponAbility SecondaryAbility{ get{ return null; } } public virtual int DefMaxRange{ get{ return 1; } } public virtual int DefHitSound{ get{ return 0; } } public virtual int DefMissSound{ get{ return 0; } } public virtual SkillName DefSkill{ get{ return SkillName.Swords; } } public virtual WeaponType DefType{ get{ return WeaponType.Slashing; } } public virtual WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash1H; } } public virtual int AosStrengthReq{ get{ return 0; } } public virtual int AosDexterityReq{ get{ return 0; } } public virtual int AosIntelligenceReq{ get{ return 0; } } public virtual int AosMinDamage{ get{ return 0; } } public virtual int AosMaxDamage{ get{ return 0; } } public virtual int AosSpeed{ get{ return 0; } } public virtual int AosMaxRange{ get{ return DefMaxRange; } } public virtual int AosHitSound{ get{ return DefHitSound; } } public virtual int AosMissSound{ get{ return DefMissSound; } } public virtual SkillName AosSkill{ get{ return DefSkill; } } public virtual WeaponType AosType{ get{ return DefType; } } public virtual WeaponAnimation AosAnimation{ get{ return DefAnimation; } } public virtual int OldStrengthReq{ get{ return 0; } } public virtual int OldDexterityReq{ get{ return 0; } } public virtual int OldIntelligenceReq{ get{ return 0; } } public virtual int OldMinDamage{ get{ return 0; } } public virtual int OldMaxDamage{ get{ return 0; } } public virtual int OldSpeed{ get{ return 0; } } public virtual int OldMaxRange{ get{ return DefMaxRange; } } public virtual int OldHitSound{ get{ return DefHitSound; } } public virtual int OldMissSound{ get{ return DefMissSound; } } public virtual SkillName OldSkill{ get{ return DefSkill; } } public virtual WeaponType OldType{ get{ return DefType; } } public virtual WeaponAnimation OldAnimation{ get{ return DefAnimation; } } public virtual int InitMinHits{ get{ return 0; } } public virtual int InitMaxHits{ get{ return 0; } } public override int PhysicalResistance{ get{ return m_AosWeaponAttributes.ResistPhysicalBonus; } } public override int FireResistance{ get{ return m_AosWeaponAttributes.ResistFireBonus; } } public override int ColdResistance{ get{ return m_AosWeaponAttributes.ResistColdBonus; } } public override int PoisonResistance{ get{ return m_AosWeaponAttributes.ResistPoisonBonus; } } public override int EnergyResistance{ get{ return m_AosWeaponAttributes.ResistEnergyBonus; } } public virtual SkillName AccuracySkill { get { return SkillName.Tactics; } } #endregion #region Getters & Setters [CommandProperty( AccessLevel.GameMaster )] public AosAttributes Attributes { get{ return m_AosAttributes; } set{} } [CommandProperty( AccessLevel.GameMaster )] public AosWeaponAttributes WeaponAttributes { get{ return m_AosWeaponAttributes; } set{} } [CommandProperty( AccessLevel.GameMaster )] public AosSkillBonuses SkillBonuses { get{ return m_AosSkillBonuses; } set{} } [CommandProperty( AccessLevel.GameMaster )] public AosElementAttributes AosElementDamages { get { return m_AosElementDamages; } set { } } [CommandProperty( AccessLevel.GameMaster )] public bool Cursed { get{ return m_Cursed; } set{ m_Cursed = value; } } [CommandProperty( AccessLevel.GameMaster )] public bool Consecrated { get{ return m_Consecrated; } set{ m_Consecrated = value; } } [CommandProperty( AccessLevel.GameMaster )] public bool Identified { get{ return m_Identified; } set{ m_Identified = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int HitPoints { get{ return m_Hits; } set { if ( m_Hits == value ) return; if ( value > m_MaxHits ) value = m_MaxHits; m_Hits = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int MaxHitPoints { get{ return m_MaxHits; } set{ m_MaxHits = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int PoisonCharges { get{ return m_PoisonCharges; } set{ m_PoisonCharges = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public Poison Poison { get{ return m_Poison; } set{ m_Poison = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public WeaponQuality Quality { get{ return m_Quality; } set{ UnscaleDurability(); m_Quality = value; ScaleDurability(); InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public Mobile Crafter { get{ return m_Crafter; } set{ m_Crafter = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public SlayerName Slayer { get{ return m_Slayer; } set{ m_Slayer = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public SlayerName Slayer2 { get { return m_Slayer2; } set { m_Slayer2 = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public CraftResource Resource { get{ return m_Resource; } set{ UnscaleDurability(); m_Resource = value; Hue = CraftResources.GetHue( m_Resource ); InvalidateProperties(); ScaleDurability(); } } [CommandProperty( AccessLevel.GameMaster )] public WeaponDamageLevel DamageLevel { get{ return m_DamageLevel; } set{ m_DamageLevel = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public WeaponDurabilityLevel DurabilityLevel { get{ return m_DurabilityLevel; } set{ UnscaleDurability(); m_DurabilityLevel = value; InvalidateProperties(); ScaleDurability(); } } [CommandProperty( AccessLevel.GameMaster )] public bool PlayerConstructed { get{ return m_PlayerConstructed; } set{ m_PlayerConstructed = value; } } [CommandProperty( AccessLevel.GameMaster )] public int MaxRange { get{ return ( m_MaxRange == -1 ? Core.AOS ? AosMaxRange : OldMaxRange : m_MaxRange ); } set{ m_MaxRange = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public WeaponAnimation Animation { get{ return ( m_Animation == (WeaponAnimation)(-1) ? Core.AOS ? AosAnimation : OldAnimation : m_Animation ); } set{ m_Animation = value; } } [CommandProperty( AccessLevel.GameMaster )] public WeaponType Type { get{ return ( m_Type == (WeaponType)(-1) ? Core.AOS ? AosType : OldType : m_Type ); } set{ m_Type = value; } } [CommandProperty( AccessLevel.GameMaster )] public SkillName Skill { get{ return ( m_Skill == (SkillName)(-1) ? Core.AOS ? AosSkill : OldSkill : m_Skill ); } set{ m_Skill = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int HitSound { get{ return ( m_HitSound == -1 ? Core.AOS ? AosHitSound : OldHitSound : m_HitSound ); } set{ m_HitSound = value; } } [CommandProperty( AccessLevel.GameMaster )] public int MissSound { get{ return ( m_MissSound == -1 ? Core.AOS ? AosMissSound : OldMissSound : m_MissSound ); } set{ m_MissSound = value; } } [CommandProperty( AccessLevel.GameMaster )] public int MinDamage { get{ return ( m_MinDamage == -1 ? Core.AOS ? AosMinDamage : OldMinDamage : m_MinDamage ); } set{ m_MinDamage = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int MaxDamage { get{ return ( m_MaxDamage == -1 ? Core.AOS ? AosMaxDamage : OldMaxDamage : m_MaxDamage ); } set{ m_MaxDamage = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int Speed { get{ return ( m_Speed == -1 ? Core.AOS ? AosSpeed : OldSpeed : m_Speed ); } set{ m_Speed = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int StrRequirement { get{ return ( m_StrReq == -1 ? Core.AOS ? AosStrengthReq : OldStrengthReq : m_StrReq ); } set{ m_StrReq = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] public int DexRequirement { get{ return ( m_DexReq == -1 ? Core.AOS ? AosDexterityReq : OldDexterityReq : m_DexReq ); } set{ m_DexReq = value; } } [CommandProperty( AccessLevel.GameMaster )] public int IntRequirement { get{ return ( m_IntReq == -1 ? Core.AOS ? AosIntelligenceReq : OldIntelligenceReq : m_IntReq ); } set{ m_IntReq = value; } } [CommandProperty( AccessLevel.GameMaster )] public WeaponAccuracyLevel AccuracyLevel { get { return m_AccuracyLevel; } set { if ( m_AccuracyLevel != value ) { m_AccuracyLevel = value; if ( UseSkillMod ) { if ( m_AccuracyLevel == WeaponAccuracyLevel.Regular ) { if ( m_SkillMod != null ) m_SkillMod.Remove(); m_SkillMod = null; } else if ( m_SkillMod == null && Parent is Mobile ) { m_SkillMod = new DefaultSkillMod( AccuracySkill, true, (int)m_AccuracyLevel * 5 ); ((Mobile)Parent).AddSkillMod( m_SkillMod ); } else if ( m_SkillMod != null ) { m_SkillMod.Value = (int)m_AccuracyLevel * 5; } } InvalidateProperties(); } } } #endregion public virtual void UnscaleDurability() { int scale = 100 + GetDurabilityBonus(); m_Hits = ((m_Hits * 100) + (scale - 1)) / scale; m_MaxHits = ((m_MaxHits * 100) + (scale - 1)) / scale; InvalidateProperties(); } public virtual void ScaleDurability() { int scale = 100 + GetDurabilityBonus(); m_Hits = ((m_Hits * scale) + 99) / 100; m_MaxHits = ((m_MaxHits * scale) + 99) / 100; InvalidateProperties(); } public int GetDurabilityBonus() { int bonus = 0; if ( m_Quality == WeaponQuality.Exceptional ) bonus += 20; switch ( m_DurabilityLevel ) { case WeaponDurabilityLevel.Durable: bonus += 20; break; case WeaponDurabilityLevel.Substantial: bonus += 50; break; case WeaponDurabilityLevel.Massive: bonus += 70; break; case WeaponDurabilityLevel.Fortified: bonus += 100; break; case WeaponDurabilityLevel.Indestructible: bonus += 120; break; } if ( Core.AOS ) { bonus += m_AosWeaponAttributes.DurabilityBonus; CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); CraftAttributeInfo attrInfo = null; if ( resInfo != null ) attrInfo = resInfo.AttributeInfo; if ( attrInfo != null ) bonus += attrInfo.WeaponDurability; } return bonus; } public int GetLowerStatReq() { if ( !Core.AOS ) return 0; int v = m_AosWeaponAttributes.LowerStatReq; CraftResourceInfo info = CraftResources.GetInfo( m_Resource ); if ( info != null ) { CraftAttributeInfo attrInfo = info.AttributeInfo; if ( attrInfo != null ) v += attrInfo.WeaponLowerRequirements; } if ( v > 100 ) v = 100; return v; } public static void BlockEquip( Mobile m, TimeSpan duration ) { if ( m.BeginAction( typeof( BaseWeapon ) ) ) new ResetEquipTimer( m, duration ).Start(); } private class ResetEquipTimer : Timer { private Mobile m_Mobile; public ResetEquipTimer( Mobile m, TimeSpan duration ) : base( duration ) { m_Mobile = m; } protected override void OnTick() { m_Mobile.EndAction( typeof( BaseWeapon ) ); } } public override bool CheckConflictingLayer( Mobile m, Item item, Layer layer ) { if ( base.CheckConflictingLayer( m, item, layer ) ) return true; if ( this.Layer == Layer.TwoHanded && layer == Layer.OneHanded ) { m.SendLocalizedMessage( 500214 ); // You already have something in both hands. return true; } else if ( this.Layer == Layer.OneHanded && layer == Layer.TwoHanded && !(item is BaseShield) && !(item is BaseEquipableLight) ) { m.SendLocalizedMessage( 500215 ); // You can only wield one weapon at a time. return true; } return false; } public override bool AllowSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) { if ( !Ethics.Ethic.CheckTrade( from, to, newOwner, this ) ) return false; return base.AllowSecureTrade( from, to, newOwner, accepted ); } public virtual Race RequiredRace { get { return null; } } //On OSI, there are no weapons with race requirements, this is for custom stuff public override bool CanEquip( Mobile from ) { if ( !Ethics.Ethic.CheckEquip( from, this ) ) return false; if( RequiredRace != null && from.Race != RequiredRace ) { if( RequiredRace == Race.Elf ) from.SendLocalizedMessage( 1072203 ); // Only Elves may use this. else from.SendMessage( "Only {0} may use this.", RequiredRace.PluralName ); return false; } else if ( from.Dex < DexRequirement ) { from.SendMessage( "You are not nimble enough to equip that." ); return false; } else if ( from.Str < AOS.Scale( StrRequirement, 100 - GetLowerStatReq() ) ) { from.SendLocalizedMessage( 500213 ); // You are not strong enough to equip that. return false; } else if ( from.Int < IntRequirement ) { from.SendMessage( "You are not smart enough to equip that." ); return false; } else if ( !from.CanBeginAction( typeof( BaseWeapon ) ) ) { return false; } else { return base.CanEquip( from ); } } public virtual bool UseSkillMod{ get{ return !Core.AOS; } } public override bool OnEquip( Mobile from ) { int strBonus = m_AosAttributes.BonusStr; int dexBonus = m_AosAttributes.BonusDex; int intBonus = m_AosAttributes.BonusInt; if ( (strBonus != 0 || dexBonus != 0 || intBonus != 0) ) { Mobile m = from; string modName = this.Serial.ToString(); if ( strBonus != 0 ) m.AddStatMod( new StatMod( StatType.Str, modName + "Str", strBonus, TimeSpan.Zero ) ); if ( dexBonus != 0 ) m.AddStatMod( new StatMod( StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero ) ); if ( intBonus != 0 ) m.AddStatMod( new StatMod( StatType.Int, modName + "Int", intBonus, TimeSpan.Zero ) ); } from.NextCombatTime = DateTime.Now + GetDelay( from ); if ( UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular ) { if ( m_SkillMod != null ) m_SkillMod.Remove(); m_SkillMod = new DefaultSkillMod( AccuracySkill, true, (int)m_AccuracyLevel * 5 ); from.AddSkillMod( m_SkillMod ); } if ( Core.AOS && m_AosWeaponAttributes.MageWeapon != 0 && m_AosWeaponAttributes.MageWeapon != 30 ) { if ( m_MageMod != null ) m_MageMod.Remove(); m_MageMod = new DefaultSkillMod( SkillName.Magery, true, -30 + m_AosWeaponAttributes.MageWeapon ); from.AddSkillMod( m_MageMod ); } return true; } public override void OnAdded( object parent ) { base.OnAdded( parent ); if ( parent is Mobile ) { Mobile from = (Mobile)parent; if ( Core.AOS ) m_AosSkillBonuses.AddTo( from ); from.CheckStatTimers(); from.Delta( MobileDelta.WeaponDamage ); } } public override void OnRemoved( object parent ) { if ( parent is Mobile ) { Mobile m = (Mobile)parent; BaseWeapon weapon = m.Weapon as BaseWeapon; string modName = this.Serial.ToString(); m.RemoveStatMod( modName + "Str" ); m.RemoveStatMod( modName + "Dex" ); m.RemoveStatMod( modName + "Int" ); if ( weapon != null ) m.NextCombatTime = DateTime.Now + weapon.GetDelay( m ); if ( UseSkillMod && m_SkillMod != null ) { m_SkillMod.Remove(); m_SkillMod = null; } if ( m_MageMod != null ) { m_MageMod.Remove(); m_MageMod = null; } if ( Core.AOS ) m_AosSkillBonuses.Remove(); m.CheckStatTimers(); m.Delta( MobileDelta.WeaponDamage ); } } public virtual SkillName GetUsedSkill( Mobile m, bool checkSkillAttrs ) { SkillName sk; if ( checkSkillAttrs && m_AosWeaponAttributes.UseBestSkill != 0 ) { double swrd = m.Skills[SkillName.Swords].Value; double fenc = m.Skills[SkillName.Fencing].Value; double mcng = m.Skills[SkillName.Macing].Value; double val; sk = SkillName.Swords; val = swrd; if ( fenc > val ){ sk = SkillName.Fencing; val = fenc; } if ( mcng > val ){ sk = SkillName.Macing; val = mcng; } } else if ( m_AosWeaponAttributes.MageWeapon != 0 ) { if ( m.Skills[SkillName.Magery].Value > m.Skills[Skill].Value ) sk = SkillName.Magery; else sk = Skill; } else { sk = Skill; if ( sk != SkillName.Wrestling && !m.Player && !m.Body.IsHuman && m.Skills[SkillName.Wrestling].Value > m.Skills[sk].Value ) sk = SkillName.Wrestling; } return sk; } public virtual double GetAttackSkillValue( Mobile attacker, Mobile defender ) { return attacker.Skills[GetUsedSkill( attacker, true )].Value; } public virtual double GetDefendSkillValue( Mobile attacker, Mobile defender ) { return defender.Skills[GetUsedSkill( defender, true )].Value; } private static bool CheckAnimal( Mobile m, Type type ) { return AnimalForm.UnderTransformation( m, type ); } public virtual bool CheckHit( Mobile attacker, Mobile defender ) { BaseWeapon atkWeapon = attacker.Weapon as BaseWeapon; BaseWeapon defWeapon = defender.Weapon as BaseWeapon; Skill atkSkill = attacker.Skills[atkWeapon.Skill]; Skill defSkill = defender.Skills[defWeapon.Skill]; double atkValue = atkWeapon.GetAttackSkillValue( attacker, defender ); double defValue = defWeapon.GetDefendSkillValue( attacker, defender ); //attacker.CheckSkill( atkSkill.SkillName, defValue - 20.0, 120.0 ); //defender.CheckSkill( defSkill.SkillName, atkValue - 20.0, 120.0 ); double ourValue, theirValue; int bonus = GetHitChanceBonus(); if ( Core.AOS ) { if ( atkValue <= -20.0 ) atkValue = -19.9; if ( defValue <= -20.0 ) defValue = -19.9; // Hit Chance Increase = 45% int atkChance = AosAttributes.GetValue( attacker, AosAttribute.AttackChance ); if ( atkChance > 45 ) atkChance = 45; bonus += atkChance; if ( Spells.Chivalry.DivineFurySpell.UnderEffect( attacker ) ) bonus += 10; // attacker gets 10% bonus when they're under divine fury if ( CheckAnimal( attacker, typeof( GreyWolf ) ) || CheckAnimal( attacker, typeof( BakeKitsune ) ) ) bonus += 20; // attacker gets 20% bonus when under Wolf or Bake Kitsune form if ( HitLower.IsUnderAttackEffect( attacker ) ) bonus -= 25; // Under Hit Lower Attack effect -> 25% malus ourValue = (atkValue + 20.0) * (100 + bonus); // Defense Chance Increase = 45% bonus = AosAttributes.GetValue( defender, AosAttribute.DefendChance ); if ( bonus > 45 ) bonus = 45; if ( Spells.Chivalry.DivineFurySpell.UnderEffect( defender ) ) bonus -= 20; // defender loses 20% bonus when they're under divine fury if ( HitLower.IsUnderDefenseEffect( defender ) ) bonus -= 25; // Under Hit Lower Defense effect -> 25% malus int blockBonus = 0; if ( Block.GetBonus( defender, ref blockBonus ) ) bonus += blockBonus; int surpriseMalus = 0; if ( SurpriseAttack.GetMalus( defender, ref surpriseMalus ) ) bonus -= surpriseMalus; int discordanceEffect = 0; // Defender loses -0/-28% if under the effect of Discordance. if ( SkillHandlers.Discordance.GetEffect( attacker, ref discordanceEffect ) ) bonus -= discordanceEffect; theirValue = (defValue + 20.0) * (100 + bonus); bonus = 0; } else { if ( atkValue <= -50.0 ) atkValue = -49.9; if ( defValue <= -50.0 ) defValue = -49.9; ourValue = (atkValue + 50.0); theirValue = (defValue + 50.0); } double chance = ourValue / (theirValue * 2.0); chance *= 1.0 + ((double)bonus / 100); if ( Core.AOS && chance < 0.02 ) chance = 0.02; WeaponAbility ability = WeaponAbility.GetCurrentAbility( attacker ); if ( ability != null ) chance *= ability.AccuracyScalar; SpecialMove move = SpecialMove.GetCurrentMove( attacker ); if ( move != null ) chance *= move.GetAccuracyScalar( attacker ); return attacker.CheckSkill( atkSkill.SkillName, chance ); //return ( chance >= Utility.RandomDouble() ); } public virtual TimeSpan GetDelay( Mobile m ) { int speed = this.Speed; if ( speed == 0 ) return TimeSpan.FromHours( 1.0 ); double delayInSeconds; if ( Core.SE ) { /* * This is likely true for Core.AOS as well... both guides report the same * formula, and both are wrong. * The old formula left in for AOS for legacy & because we aren't quite 100% * Sure that AOS has THIS formula */ int bonus = AosAttributes.GetValue( m, AosAttribute.WeaponSpeed ); if ( Spells.Chivalry.DivineFurySpell.UnderEffect( m ) ) bonus += 10; // Bonus granted by successful use of Honorable Execution. bonus += HonorableExecution.GetSwingBonus( m ); if( DualWield.Registry.Contains( m ) ) bonus += ((DualWield.DualWieldTimer)DualWield.Registry[m]).BonusSwingSpeed; if( Feint.Registry.Contains( m ) ) bonus -= ((Feint.FeintTimer)Feint.Registry[m]).SwingSpeedReduction; int discordanceEffect = 0; // Discordance gives a malus of -0/-28% to swing speed. if ( SkillHandlers.Discordance.GetEffect( m, ref discordanceEffect ) ) bonus -= discordanceEffect; if ( bonus > 60 ) bonus = 60; speed = (int)Math.Floor( speed * (bonus + 100.0) / 100.0 ); if ( speed <= 0 ) speed = 1; int ticks = (int)Math.Floor( (80000.0 / ((m.Stam + 100) * speed)) - 2 ); // Swing speed currently capped at one swing every 1.25 seconds (5 ticks). if ( ticks < 5 ) ticks = 5; delayInSeconds = ticks * 0.25; } else if ( Core.AOS ) { int v = (m.Stam + 100) * speed; int bonus = AosAttributes.GetValue( m, AosAttribute.WeaponSpeed ); if ( Spells.Chivalry.DivineFurySpell.UnderEffect( m ) ) bonus += 10; int discordanceEffect = 0; // Discordance gives a malus of -0/-28% to swing speed. if ( SkillHandlers.Discordance.GetEffect( m, ref discordanceEffect ) ) bonus -= discordanceEffect; v += AOS.Scale( v, bonus ); if ( v <= 0 ) v = 1; delayInSeconds = Math.Floor( 40000.0 / v ) * 0.5; // Maximum swing rate capped at one swing per second // OSI dev said that it has and is supposed to be 1.25 if ( delayInSeconds < 1.25 ) delayInSeconds = 1.25; } else { int v = (m.Stam + 100) * speed; if ( v <= 0 ) v = 1; delayInSeconds = 15000.0 / v; } return TimeSpan.FromSeconds( delayInSeconds ); } public virtual void OnBeforeSwing( Mobile attacker, Mobile defender ) { WeaponAbility a = WeaponAbility.GetCurrentAbility( attacker ); if( a != null && !a.OnBeforeSwing( attacker, defender ) ) WeaponAbility.ClearCurrentAbility( attacker ); SpecialMove move = SpecialMove.GetCurrentMove( attacker ); if( move != null && !move.OnBeforeSwing( attacker, defender ) ) SpecialMove.ClearCurrentMove( attacker ); } public virtual TimeSpan OnSwing( Mobile attacker, Mobile defender ) { return OnSwing( attacker, defender, 1.0 ); } public virtual TimeSpan OnSwing( Mobile attacker, Mobile defender, double damageBonus ) { bool canSwing = true; if ( Core.AOS ) { canSwing = ( !attacker.Paralyzed && !attacker.Frozen ); if ( canSwing ) { Spell sp = attacker.Spell as Spell; canSwing = ( sp == null || !sp.IsCasting || !sp.BlocksMovement ); } } if ( canSwing && attacker.HarmfulCheck( defender ) ) { attacker.DisruptiveAction(); if ( attacker.NetState != null ) attacker.Send( new Swing( 0, attacker, defender ) ); if ( attacker is BaseCreature ) { BaseCreature bc = (BaseCreature)attacker; WeaponAbility ab = bc.GetWeaponAbility(); if ( ab != null ) { if ( bc.WeaponAbilityChance > Utility.RandomDouble() ) WeaponAbility.SetCurrentAbility( bc, ab ); else WeaponAbility.ClearCurrentAbility( bc ); } } if ( CheckHit( attacker, defender ) ) OnHit( attacker, defender, damageBonus ); else OnMiss( attacker, defender ); } return GetDelay( attacker ); } #region Sounds public virtual int GetHitAttackSound( Mobile attacker, Mobile defender ) { int sound = attacker.GetAttackSound(); if ( sound == -1 ) sound = HitSound; return sound; } public virtual int GetHitDefendSound( Mobile attacker, Mobile defender ) { return defender.GetHurtSound(); } public virtual int GetMissAttackSound( Mobile attacker, Mobile defender ) { if ( attacker.GetAttackSound() == -1 ) return MissSound; else return -1; } public virtual int GetMissDefendSound( Mobile attacker, Mobile defender ) { return -1; } #endregion public static bool CheckParry( Mobile defender ) { if ( defender == null ) return false; BaseShield shield = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseShield; double parry = defender.Skills[SkillName.Parry].Value; double parryNonRacial = defender.Skills[SkillName.Parry].NonRacialValue; double bushido = defender.Skills[SkillName.Bushido].Value; if ( shield != null ) { double chance = (parryNonRacial - bushido) / 400.0; //As per OSI, no negitive effect from the Racial stuffs, ie, 120 bushido and '0' parry with humans // Parry over 100 grants a 5% bonus. if ( parry >= 100.0 ) chance += 0.05; // Evasion grants a 50% bonus. if ( Evasion.IsEvading( defender ) ) chance *= 1.5; // Low dexterity lowers the chance. if ( defender.Dex < 80 ) chance = chance * (20 + defender.Dex) / 100; return defender.CheckSkill( SkillName.Parry, chance ); } else if ( !(defender.Weapon is Fists) && !(defender.Weapon is BaseRanged) ) { BaseWeapon weapon = defender.Weapon as BaseWeapon; double divisor = (weapon.Layer == Layer.OneHanded) ? 48000.0 : 41140.0; double chance = (parry * bushido) / divisor; double aosChance = parry / 800.0; // Parry or Bushido over 100 grant a 5% bonus. if( parry >= 100.0 ) { chance += 0.05; aosChance += 0.05; } else if( bushido >= 100.0 ) { chance += 0.05; } // Evasion grants a 50% bonus. if( Evasion.IsEvading( defender ) ) chance *= 1.5; // Low dexterity lowers the chance. if( defender.Dex < 80 ) chance = chance * (20 + defender.Dex) / 100; if ( chance > aosChance ) return defender.CheckSkill( SkillName.Parry, chance ); else return (aosChance > Utility.RandomDouble()); // Only skillcheck if wielding a shield & there's no effect from Bushido } return false; } public virtual int AbsorbDamageAOS( Mobile attacker, Mobile defender, int damage ) { bool blocked = false; if ( defender.Player || defender.Body.IsHuman ) { blocked = CheckParry( defender ); if ( blocked ) { defender.FixedEffect( 0x37B9, 10, 16 ); damage = 0; // Successful block removes the Honorable Execution penalty. HonorableExecution.RemovePenalty( defender ); if ( CounterAttack.IsCountering( defender ) ) { BaseWeapon weapon = defender.Weapon as BaseWeapon; if ( weapon != null ) weapon.OnSwing( defender, attacker ); CounterAttack.StopCountering( defender ); } if ( Confidence.IsConfident( defender ) ) { defender.SendLocalizedMessage( 1063117 ); // Your confidence reassures you as you successfully block your opponent's blow. double bushido = defender.Skills.Bushido.Value; defender.Hits += Utility.RandomMinMax( 1, (int)(bushido / 12) ); defender.Stam += Utility.RandomMinMax( 1, (int)(bushido / 5) ); } BaseShield shield = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseShield; if ( shield != null ) { shield.OnHit( this, damage ); } } } if ( !blocked ) { double positionChance = Utility.RandomDouble(); Item armorItem; if( positionChance < 0.07 ) armorItem = defender.NeckArmor; else if( positionChance < 0.14 ) armorItem = defender.HandArmor; else if( positionChance < 0.28 ) armorItem = defender.ArmsArmor; else if( positionChance < 0.43 ) armorItem = defender.HeadArmor; else if( positionChance < 0.65 ) armorItem = defender.LegsArmor; else armorItem = defender.ChestArmor; IWearableDurability armor = armorItem as IWearableDurability; if ( armor != null ) armor.OnHit( this, damage ); // call OnHit to lose durability } return damage; } public virtual int AbsorbDamage( Mobile attacker, Mobile defender, int damage ) { if ( Core.AOS ) return AbsorbDamageAOS( attacker, defender, damage ); double chance = Utility.RandomDouble(); Item armorItem; if( chance < 0.07 ) armorItem = defender.NeckArmor; else if( chance < 0.14 ) armorItem = defender.HandArmor; else if( chance < 0.28 ) armorItem = defender.ArmsArmor; else if( chance < 0.43 ) armorItem = defender.HeadArmor; else if( chance < 0.65 ) armorItem = defender.LegsArmor; else armorItem = defender.ChestArmor; IWearableDurability armor = armorItem as IWearableDurability; if ( armor != null ) damage = armor.OnHit( this, damage ); BaseShield shield = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseShield; if ( shield != null ) damage = shield.OnHit( this, damage ); int virtualArmor = defender.VirtualArmor + defender.VirtualArmorMod; if ( virtualArmor > 0 ) { double scalar; if ( chance < 0.14 ) scalar = 0.07; else if ( chance < 0.28 ) scalar = 0.14; else if ( chance < 0.43 ) scalar = 0.15; else if ( chance < 0.65 ) scalar = 0.22; else scalar = 0.35; int from = (int)(virtualArmor * scalar) / 2; int to = (int)(virtualArmor * scalar); damage -= Utility.Random( from, (to - from) + 1 ); } return damage; } public virtual int GetPackInstinctBonus( Mobile attacker, Mobile defender ) { if ( attacker.Player || defender.Player ) return 0; BaseCreature bc = attacker as BaseCreature; if ( bc == null || bc.PackInstinct == PackInstinct.None || (!bc.Controlled && !bc.Summoned) ) return 0; Mobile master = bc.ControlMaster; if ( master == null ) master = bc.SummonMaster; if ( master == null ) return 0; int inPack = 1; foreach ( Mobile m in defender.GetMobilesInRange( 1 ) ) { if ( m != attacker && m is BaseCreature ) { BaseCreature tc = (BaseCreature)m; if ( (tc.PackInstinct & bc.PackInstinct) == 0 || (!tc.Controlled && !tc.Summoned) ) continue; Mobile theirMaster = tc.ControlMaster; if ( theirMaster == null ) theirMaster = tc.SummonMaster; if ( master == theirMaster && tc.Combatant == defender ) ++inPack; } } if ( inPack >= 5 ) return 100; else if ( inPack >= 4 ) return 75; else if ( inPack >= 3 ) return 50; else if ( inPack >= 2 ) return 25; return 0; } private static bool m_InDoubleStrike; public static bool InDoubleStrike { get{ return m_InDoubleStrike; } set{ m_InDoubleStrike = value; } } public virtual void OnHit( Mobile attacker, Mobile defender ) { OnHit( attacker, defender, 1.0 ); } public virtual void OnHit( Mobile attacker, Mobile defender, double damageBonus ) { if ( MirrorImage.HasClone( defender ) && (defender.Skills.Ninjitsu.Value / 150.0) > Utility.RandomDouble() ) { Clone bc; foreach ( Mobile m in defender.GetMobilesInRange( 4 ) ) { bc = m as Clone; if ( bc != null && bc.Summoned && bc.SummonMaster == defender ) { attacker.SendLocalizedMessage( 1063141 ); // Your attack has been diverted to a nearby mirror image of your target! defender.SendLocalizedMessage( 1063140 ); // You manage to divert the attack onto one of your nearby mirror images. /* * TODO: What happens if the Clone parries a blow? * And what about if the attacker is using Honorable Execution * and kills it? */ defender = m; break; } } } PlaySwingAnimation( attacker ); PlayHurtAnimation( defender ); attacker.PlaySound( GetHitAttackSound( attacker, defender ) ); defender.PlaySound( GetHitDefendSound( attacker, defender ) ); int damage = ComputeDamage( attacker, defender ); #region Damage Multipliers /* * The following damage bonuses multiply damage by a factor. * Capped at x3 (300%). */ double factor = 1.0; WeaponAbility a = WeaponAbility.GetCurrentAbility( attacker ); SpecialMove move = SpecialMove.GetCurrentMove( attacker ); if ( a != null ) factor *= a.DamageScalar; if ( move != null ) factor *= move.GetDamageScalar( attacker, defender ); factor *= damageBonus; CheckSlayerResult cs = CheckSlayers( attacker, defender ); if ( cs != CheckSlayerResult.None ) { if ( cs == CheckSlayerResult.Slayer ) defender.FixedEffect( 0x37B9, 10, 5 ); factor *= 2.0; } if ( !attacker.Player ) { if ( defender is PlayerMobile ) { PlayerMobile pm = (PlayerMobile)defender; if ( pm.EnemyOfOneType != null && pm.EnemyOfOneType != attacker.GetType() ) factor *= 2.0; } } else if ( !defender.Player ) { if ( attacker is PlayerMobile ) { PlayerMobile pm = (PlayerMobile)attacker; if ( pm.WaitingForEnemy ) { pm.EnemyOfOneType = defender.GetType(); pm.WaitingForEnemy = false; } if ( pm.EnemyOfOneType == defender.GetType() ) { defender.FixedEffect( 0x37B9, 10, 5, 1160, 0 ); factor *= 1.5; } } } int packInstinctBonus = GetPackInstinctBonus( attacker, defender ); if ( packInstinctBonus != 0 ) factor *= 1.0 + (double)packInstinctBonus / 100.0; if ( m_InDoubleStrike ) factor *= 0.9; // 10% loss when attacking with double-strike TransformContext context = TransformationSpell.GetContext( defender ); if ( (m_Slayer == SlayerName.Silver || m_Slayer2 == SlayerName.Silver) && context != null && context.Type != typeof( HorrificBeastSpell ) ) factor *= 1.25; // Every necromancer transformation other than horrific beast takes an additional 25% damage if ( attacker is PlayerMobile ) { PlayerMobile pmAttacker = (PlayerMobile) attacker; if ( pmAttacker.HonorActive && pmAttacker.InRange( defender, 1 ) ) factor *= 1.25; if ( pmAttacker.SentHonorContext != null && pmAttacker.SentHonorContext.Target == defender ) pmAttacker.SentHonorContext.ApplyPerfectionDamageBonus( ref factor ); } if ( factor > 3.0 ) factor = 3.0; damage = (int)(damage * factor); #endregion if ( attacker is BaseCreature ) ((BaseCreature)attacker).AlterMeleeDamageTo( defender, ref damage ); if ( defender is BaseCreature ) ((BaseCreature)defender).AlterMeleeDamageFrom( attacker, ref damage ); damage = AbsorbDamage( attacker, defender, damage ); if ( !Core.AOS && damage < 1 ) damage = 1; else if ( Core.AOS && damage == 0 ) // parried { if ( a != null && a.Validate( attacker ) /*&& a.CheckMana( attacker, true )*/ ) // Parried special moves have no mana cost { a = null; WeaponAbility.ClearCurrentAbility( attacker ); attacker.SendLocalizedMessage( 1061140 ); // Your attack was parried! } } AddBlood( attacker, defender, damage ); int phys, fire, cold, pois, nrgy; GetDamageTypes( attacker, out phys, out fire, out cold, out pois, out nrgy ); if ( m_Consecrated ) { phys = defender.PhysicalResistance; fire = defender.FireResistance; cold = defender.ColdResistance; pois = defender.PoisonResistance; nrgy = defender.EnergyResistance; int low = phys, type = 0; if ( fire < low ){ low = fire; type = 1; } if ( cold < low ){ low = cold; type = 2; } if ( pois < low ){ low = pois; type = 3; } if ( nrgy < low ){ low = nrgy; type = 4; } phys = fire = cold = pois = nrgy = 0; if ( type == 0 ) phys = 100; else if ( type == 1 ) fire = 100; else if ( type == 2 ) cold = 100; else if ( type == 3 ) pois = 100; else if ( type == 4 ) nrgy = 100; } int damageGiven = damage; if ( a != null && !a.OnBeforeDamage( attacker, defender ) ) { WeaponAbility.ClearCurrentAbility( attacker ); a = null; } if ( move != null && !move.OnBeforeDamage( attacker, defender ) ) { SpecialMove.ClearCurrentMove( attacker ); move = null; } bool ignoreArmor = ( a is ArmorIgnore || (move != null && move.IgnoreArmor( attacker )) ); damageGiven = AOS.Damage( defender, attacker, damage, ignoreArmor, phys, fire, cold, pois, nrgy ); double propertyBonus = ( move == null ) ? 1.0 : move.GetPropertyBonus( attacker ); if ( Core.AOS ) { int lifeLeech = 0; int stamLeech = 0; int manaLeech = 0; int wraithLeech = 0; if ( (int)(m_AosWeaponAttributes.HitLeechHits * propertyBonus) > Utility.Random( 100 ) ) lifeLeech += 30; // HitLeechHits% chance to leech 30% of damage as hit points if ( (int)(m_AosWeaponAttributes.HitLeechStam * propertyBonus) > Utility.Random( 100 ) ) stamLeech += 100; // HitLeechStam% chance to leech 100% of damage as stamina if ( (int)(m_AosWeaponAttributes.HitLeechMana * propertyBonus) > Utility.Random( 100 ) ) manaLeech += 40; // HitLeechMana% chance to leech 40% of damage as mana if ( m_Cursed ) lifeLeech += 50; // Additional 50% life leech for cursed weapons (necro spell) context = TransformationSpell.GetContext( attacker ); if ( context != null && context.Type == typeof( VampiricEmbraceSpell ) ) lifeLeech += 20; // Vampiric embrace gives an additional 20% life leech if ( context != null && context.Type == typeof( WraithFormSpell ) ) { wraithLeech = (5 + (int)((15 * attacker.Skills.SpiritSpeak.Value) / 100)); // Wraith form gives an additional 5-20% mana leech // Mana leeched by the Wraith Form spell is actually stolen, not just leeched. defender.Mana -= AOS.Scale( damageGiven, wraithLeech ); manaLeech += wraithLeech; } if ( lifeLeech != 0 ) attacker.Hits += AOS.Scale( damageGiven, lifeLeech ); if ( stamLeech != 0 ) attacker.Stam += AOS.Scale( damageGiven, stamLeech ); if ( manaLeech != 0 ) attacker.Mana += AOS.Scale( damageGiven, manaLeech ); if ( lifeLeech != 0 || stamLeech != 0 || manaLeech != 0 ) attacker.PlaySound( 0x44D ); } if ( m_MaxHits > 0 && ((MaxRange <= 1 && (defender is Slime || defender is ToxicElemental)) || Utility.Random( 25 ) == 0) ) // Stratics says 50% chance, seems more like 4%.. { if ( MaxRange <= 1 && (defender is Slime || defender is ToxicElemental) ) attacker.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500263 ); // *Acid blood scars your weapon!* if ( Core.AOS && m_AosWeaponAttributes.SelfRepair > Utility.Random( 10 ) ) { HitPoints += 2; } else { if ( m_Hits > 0 ) { --HitPoints; } else if ( m_MaxHits > 1 ) { --MaxHitPoints; if ( Parent is Mobile ) ((Mobile)Parent).LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061121 ); // Your equipment is severely damaged. } else { Delete(); } } } if ( attacker is VampireBatFamiliar ) { BaseCreature bc = (BaseCreature)attacker; Mobile caster = bc.ControlMaster; if ( caster == null ) caster = bc.SummonMaster; if ( caster != null && caster.Map == bc.Map && caster.InRange( bc, 2 ) ) caster.Hits += damage; else bc.Hits += damage; } if ( Core.AOS ) { int physChance = (int)(m_AosWeaponAttributes.HitPhysicalArea * propertyBonus); int fireChance = (int)(m_AosWeaponAttributes.HitFireArea * propertyBonus); int coldChance = (int)(m_AosWeaponAttributes.HitColdArea * propertyBonus); int poisChance = (int)(m_AosWeaponAttributes.HitPoisonArea * propertyBonus); int nrgyChance = (int)(m_AosWeaponAttributes.HitEnergyArea * propertyBonus); if ( physChance != 0 && physChance > Utility.Random( 100 ) ) DoAreaAttack( attacker, defender, 0x10E, 50, 100, 0, 0, 0, 0 ); if ( fireChance != 0 && fireChance > Utility.Random( 100 ) ) DoAreaAttack( attacker, defender, 0x11D, 1160, 0, 100, 0, 0, 0 ); if ( coldChance != 0 && coldChance > Utility.Random( 100 ) ) DoAreaAttack( attacker, defender, 0x0FC, 2100, 0, 0, 100, 0, 0 ); if ( poisChance != 0 && poisChance > Utility.Random( 100 ) ) DoAreaAttack( attacker, defender, 0x205, 1166, 0, 0, 0, 100, 0 ); if ( nrgyChance != 0 && nrgyChance > Utility.Random( 100 ) ) DoAreaAttack( attacker, defender, 0x1F1, 120, 0, 0, 0, 0, 100 ); int maChance = (int)(m_AosWeaponAttributes.HitMagicArrow * propertyBonus); int harmChance = (int)(m_AosWeaponAttributes.HitHarm * propertyBonus); int fireballChance = (int)(m_AosWeaponAttributes.HitFireball * propertyBonus); int lightningChance = (int)(m_AosWeaponAttributes.HitLightning * propertyBonus); int dispelChance = (int)(m_AosWeaponAttributes.HitDispel * propertyBonus); if ( maChance != 0 && maChance > Utility.Random( 100 ) ) DoMagicArrow( attacker, defender ); if ( harmChance != 0 && harmChance > Utility.Random( 100 ) ) DoHarm( attacker, defender ); if ( fireballChance != 0 && fireballChance > Utility.Random( 100 ) ) DoFireball( attacker, defender ); if ( lightningChance != 0 && lightningChance > Utility.Random( 100 ) ) DoLightning( attacker, defender ); if ( dispelChance != 0 && dispelChance > Utility.Random( 100 ) ) DoDispel( attacker, defender ); int laChance = (int)(m_AosWeaponAttributes.HitLowerAttack * propertyBonus); int ldChance = (int)(m_AosWeaponAttributes.HitLowerDefend * propertyBonus); if ( laChance != 0 && laChance > Utility.Random( 100 ) ) DoLowerAttack( attacker, defender ); if ( ldChance != 0 && ldChance > Utility.Random( 100 ) ) DoLowerDefense( attacker, defender ); } if ( attacker is BaseCreature ) ((BaseCreature)attacker).OnGaveMeleeAttack( defender ); if ( defender is BaseCreature ) ((BaseCreature)defender).OnGotMeleeAttack( attacker ); if ( a != null ) a.OnHit( attacker, defender, damage ); if ( move != null ) move.OnHit( attacker, defender, damage ); if ( defender is IHonorTarget && ((IHonorTarget)defender).ReceivedHonorContext != null ) ((IHonorTarget)defender).ReceivedHonorContext.OnTargetHit( attacker ); if ( !(this is BaseRanged) ) { if ( AnimalForm.UnderTransformation( attacker, typeof( GiantSerpent ) ) ) defender.ApplyPoison( attacker, Poison.Lesser ); if ( AnimalForm.UnderTransformation( defender, typeof( BullFrog ) ) ) attacker.ApplyPoison( defender, Poison.Regular ); } } public virtual double GetAosDamage( Mobile attacker, int bonus, int dice, int sides ) { int damage = Utility.Dice( dice, sides, bonus ) * 100; int damageBonus = 0; // Inscription bonus int inscribeSkill = attacker.Skills[SkillName.Inscribe].Fixed; damageBonus += inscribeSkill / 200; if ( inscribeSkill >= 1000 ) damageBonus += 5; if ( attacker.Player ) { // Int bonus damageBonus += (attacker.Int / 10); // SDI bonus damageBonus += AosAttributes.GetValue( attacker, AosAttribute.SpellDamage ); } damage = AOS.Scale( damage, 100 + damageBonus ); return damage / 100; } #region Do<AoSEffect> public virtual void DoMagicArrow( Mobile attacker, Mobile defender ) { if ( !attacker.CanBeHarmful( defender, false ) ) return; attacker.DoHarmful( defender ); double damage = GetAosDamage( attacker, 10, 1, 4 ); attacker.MovingParticles( defender, 0x36E4, 5, 0, false, true, 3006, 4006, 0 ); attacker.PlaySound( 0x1E5 ); SpellHelper.Damage( TimeSpan.FromSeconds( 1.0 ), defender, attacker, damage, 0, 100, 0, 0, 0 ); } public virtual void DoHarm( Mobile attacker, Mobile defender ) { if ( !attacker.CanBeHarmful( defender, false ) ) return; attacker.DoHarmful( defender ); double damage = GetAosDamage( attacker, 17, 1, 5 ); if ( !defender.InRange( attacker, 2 ) ) damage *= 0.25; // 1/4 damage at > 2 tile range else if ( !defender.InRange( attacker, 1 ) ) damage *= 0.50; // 1/2 damage at 2 tile range defender.FixedParticles( 0x374A, 10, 30, 5013, 1153, 2, EffectLayer.Waist ); defender.PlaySound( 0x0FC ); SpellHelper.Damage( TimeSpan.Zero, defender, attacker, damage, 0, 0, 100, 0, 0 ); } public virtual void DoFireball( Mobile attacker, Mobile defender ) { if ( !attacker.CanBeHarmful( defender, false ) ) return; attacker.DoHarmful( defender ); double damage = GetAosDamage( attacker, 19, 1, 5 ); attacker.MovingParticles( defender, 0x36D4, 7, 0, false, true, 9502, 4019, 0x160 ); attacker.PlaySound( 0x15E ); SpellHelper.Damage( TimeSpan.FromSeconds( 1.0 ), defender, attacker, damage, 0, 100, 0, 0, 0 ); } public virtual void DoLightning( Mobile attacker, Mobile defender ) { if ( !attacker.CanBeHarmful( defender, false ) ) return; attacker.DoHarmful( defender ); double damage = GetAosDamage( attacker, 23, 1, 4 ); defender.BoltEffect( 0 ); SpellHelper.Damage( TimeSpan.Zero, defender, attacker, damage, 0, 0, 0, 0, 100 ); } public virtual void DoDispel( Mobile attacker, Mobile defender ) { bool dispellable = false; if ( defender is BaseCreature ) dispellable = ((BaseCreature)defender).Summoned && !((BaseCreature)defender).IsAnimatedDead; if ( !dispellable ) return; if ( !attacker.CanBeHarmful( defender, false ) ) return; attacker.DoHarmful( defender ); Spells.Spell sp = new Spells.Sixth.DispelSpell( attacker, null ); if ( sp.CheckResisted( defender ) ) { defender.FixedEffect( 0x3779, 10, 20 ); } else { Effects.SendLocationParticles( EffectItem.Create( defender.Location, defender.Map, EffectItem.DefaultDuration ), 0x3728, 8, 20, 5042 ); Effects.PlaySound( defender, defender.Map, 0x201 ); defender.Delete(); } } public virtual void DoLowerAttack( Mobile from, Mobile defender ) { if ( HitLower.ApplyAttack( defender ) ) { defender.PlaySound( 0x28E ); Effects.SendTargetEffect( defender, 0x37BE, 1, 4, 0xA, 3 ); } } public virtual void DoLowerDefense( Mobile from, Mobile defender ) { if ( HitLower.ApplyDefense( defender ) ) { defender.PlaySound( 0x28E ); Effects.SendTargetEffect( defender, 0x37BE, 1, 4, 0x23, 3 ); } } public virtual void DoAreaAttack( Mobile from, Mobile defender, int sound, int hue, int phys, int fire, int cold, int pois, int nrgy ) { Map map = from.Map; if ( map == null ) return; List<Mobile> list = new List<Mobile>(); foreach ( Mobile m in from.GetMobilesInRange( 10 ) ) { if ( from != m && defender != m && SpellHelper.ValidIndirectTarget( from, m ) && from.CanBeHarmful( m, false ) && from.InLOS( m ) ) list.Add( m ); } if ( list.Count == 0 ) return; Effects.PlaySound( from.Location, map, sound ); // TODO: What is the damage calculation? for ( int i = 0; i < list.Count; ++i ) { Mobile m = list[i]; double scalar = (11 - from.GetDistanceToSqrt( m )) / 10; if ( scalar > 1.0 ) scalar = 1.0; else if ( scalar < 0.0 ) continue; from.DoHarmful( m, true ); m.FixedEffect( 0x3779, 1, 15, hue, 0 ); AOS.Damage( m, from, (int)(GetBaseDamage( from ) * scalar), phys, fire, cold, pois, nrgy ); } } #endregion public virtual CheckSlayerResult CheckSlayers( Mobile attacker, Mobile defender ) { BaseWeapon atkWeapon = attacker.Weapon as BaseWeapon; SlayerEntry atkSlayer = SlayerGroup.GetEntryByName( atkWeapon.Slayer ); SlayerEntry atkSlayer2 = SlayerGroup.GetEntryByName( atkWeapon.Slayer2 ); if ( atkSlayer != null && atkSlayer.Slays( defender ) || atkSlayer2 != null && atkSlayer2.Slays( defender ) ) return CheckSlayerResult.Slayer; if ( !Core.SE ) { ISlayer defISlayer = Spellbook.FindEquippedSpellbook( defender ); if( defISlayer == null ) defISlayer = defender.Weapon as ISlayer; if( defISlayer != null ) { SlayerEntry defSlayer = SlayerGroup.GetEntryByName( defISlayer.Slayer ); SlayerEntry defSlayer2 = SlayerGroup.GetEntryByName( defISlayer.Slayer2 ); if( defSlayer != null && defSlayer.Group.OppositionSuperSlays( attacker ) || defSlayer2 != null && defSlayer2.Group.OppositionSuperSlays( attacker ) ) return CheckSlayerResult.Opposition; } } return CheckSlayerResult.None; } public virtual void AddBlood( Mobile attacker, Mobile defender, int damage ) { if ( damage > 0 ) { new Blood().MoveToWorld( defender.Location, defender.Map ); int extraBlood = (Core.SE ? Utility.RandomMinMax( 3, 4 ) : Utility.RandomMinMax( 0, 1 ) ); for( int i = 0; i < extraBlood; i++ ) { new Blood().MoveToWorld( new Point3D( defender.X + Utility.RandomMinMax( -1, 1 ), defender.Y + Utility.RandomMinMax( -1, 1 ), defender.Z ), defender.Map ); } } /* if ( damage <= 2 ) return; Direction d = defender.GetDirectionTo( attacker ); int maxCount = damage / 15; if ( maxCount < 1 ) maxCount = 1; else if ( maxCount > 4 ) maxCount = 4; for( int i = 0; i < Utility.Random( 1, maxCount ); ++i ) { int x = defender.X; int y = defender.Y; switch( d ) { case Direction.North: x += Utility.Random( -1, 3 ); y += Utility.Random( 2 ); break; case Direction.East: y += Utility.Random( -1, 3 ); x += Utility.Random( -1, 2 ); break; case Direction.West: y += Utility.Random( -1, 3 ); x += Utility.Random( 2 ); break; case Direction.South: x += Utility.Random( -1, 3 ); y += Utility.Random( -1, 2 ); break; case Direction.Up: x += Utility.Random( 2 ); y += Utility.Random( 2 ); break; case Direction.Down: x += Utility.Random( -1, 2 ); y += Utility.Random( -1, 2 ); break; case Direction.Left: x += Utility.Random( 2 ); y += Utility.Random( -1, 2 ); break; case Direction.Right: x += Utility.Random( -1, 2 ); y += Utility.Random( 2 ); break; } new Blood().MoveToWorld( new Point3D( x, y, defender.Z ), defender.Map ); }*/ } public virtual void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) { if( wielder is BaseCreature ) { BaseCreature bc = (BaseCreature)wielder; phys = bc.PhysicalDamage; fire = bc.FireDamage; cold = bc.ColdDamage; pois = bc.PoisonDamage; nrgy = bc.EnergyDamage; } else { fire = m_AosElementDamages.Fire; cold = m_AosElementDamages.Cold; pois = m_AosElementDamages.Poison; nrgy = m_AosElementDamages.Energy; phys = 100 - fire - cold - pois - nrgy; CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); if( resInfo != null ) { CraftAttributeInfo attrInfo = resInfo.AttributeInfo; if( attrInfo != null ) { int left = phys; left = ApplyCraftAttributeElementDamage( attrInfo.WeaponColdDamage, ref cold, left ); left = ApplyCraftAttributeElementDamage( attrInfo.WeaponEnergyDamage, ref nrgy, left ); left = ApplyCraftAttributeElementDamage( attrInfo.WeaponFireDamage, ref fire, left ); left = ApplyCraftAttributeElementDamage( attrInfo.WeaponPoisonDamage, ref pois, left ); phys = left; } } } } private int ApplyCraftAttributeElementDamage( int attrDamage, ref int element, int totalRemaining ) { if( totalRemaining <= 0 ) return 0; if ( attrDamage <= 0 ) return totalRemaining; int appliedDamage = attrDamage; if ( (appliedDamage + element) > 100 ) appliedDamage = 100 - element; if( appliedDamage > totalRemaining ) appliedDamage = totalRemaining; element += appliedDamage; return totalRemaining - appliedDamage; } public virtual void OnMiss( Mobile attacker, Mobile defender ) { PlaySwingAnimation( attacker ); attacker.PlaySound( GetMissAttackSound( attacker, defender ) ); defender.PlaySound( GetMissDefendSound( attacker, defender ) ); WeaponAbility ability = WeaponAbility.GetCurrentAbility( attacker ); if ( ability != null ) ability.OnMiss( attacker, defender ); SpecialMove move = SpecialMove.GetCurrentMove( attacker ); if ( move != null ) move.OnMiss( attacker, defender ); if ( defender is IHonorTarget && ((IHonorTarget)defender).ReceivedHonorContext != null ) ((IHonorTarget)defender).ReceivedHonorContext.OnTargetMissed( attacker ); } public virtual void GetBaseDamageRange( Mobile attacker, out int min, out int max ) { if ( attacker is BaseCreature ) { BaseCreature c = (BaseCreature)attacker; if ( c.DamageMin >= 0 ) { min = c.DamageMin; max = c.DamageMax; return; } if ( this is Fists && !attacker.Body.IsHuman ) { min = attacker.Str / 28; max = attacker.Str / 28; return; } } min = MinDamage; max = MaxDamage; } public virtual double GetBaseDamage( Mobile attacker ) { int min, max; GetBaseDamageRange( attacker, out min, out max ); return Utility.RandomMinMax( min, max ); } public virtual double GetBonus( double value, double scalar, double threshold, double offset ) { double bonus = value * scalar; if ( value >= threshold ) bonus += offset; return bonus / 100; } public virtual int GetHitChanceBonus() { if ( !Core.AOS ) return 0; int bonus = 0; switch ( m_AccuracyLevel ) { case WeaponAccuracyLevel.Accurate: bonus += 02; break; case WeaponAccuracyLevel.Surpassingly: bonus += 04; break; case WeaponAccuracyLevel.Eminently: bonus += 06; break; case WeaponAccuracyLevel.Exceedingly: bonus += 08; break; case WeaponAccuracyLevel.Supremely: bonus += 10; break; } return bonus; } public virtual int GetDamageBonus() { int bonus = VirtualDamageBonus; switch ( m_Quality ) { case WeaponQuality.Low: bonus -= 20; break; case WeaponQuality.Exceptional: bonus += 20; break; } switch ( m_DamageLevel ) { case WeaponDamageLevel.Ruin: bonus += 15; break; case WeaponDamageLevel.Might: bonus += 20; break; case WeaponDamageLevel.Force: bonus += 25; break; case WeaponDamageLevel.Power: bonus += 30; break; case WeaponDamageLevel.Vanq: bonus += 35; break; } return bonus; } public virtual void GetStatusDamage( Mobile from, out int min, out int max ) { int baseMin, baseMax; GetBaseDamageRange( from, out baseMin, out baseMax ); if ( Core.AOS ) { min = Math.Max( (int)ScaleDamageAOS( from, baseMin, false ), 1 ); max = Math.Max( (int)ScaleDamageAOS( from, baseMax, false ), 1 ); } else { min = Math.Max( (int)ScaleDamageOld( from, baseMin, false ), 1 ); max = Math.Max( (int)ScaleDamageOld( from, baseMax, false ), 1 ); } } public virtual double ScaleDamageAOS( Mobile attacker, double damage, bool checkSkills ) { if ( checkSkills ) { attacker.CheckSkill( SkillName.Tactics, 0.0, 120.0 ); // Passively check tactics for gain attacker.CheckSkill( SkillName.Anatomy, 0.0, 120.0 ); // Passively check Anatomy for gain if ( Type == WeaponType.Axe ) attacker.CheckSkill( SkillName.Lumberjacking, 0.0, 100.0 ); // Passively check Lumberjacking for gain } #region Physical bonuses /* * These are the bonuses given by the physical characteristics of the mobile. * No caps apply. */ double strengthBonus = GetBonus( attacker.Str, 0.300, 100.0, 5.00 ); double anatomyBonus = GetBonus( attacker.Skills[SkillName.Anatomy].Value, 0.500, 100.0, 5.00 ); double tacticsBonus = GetBonus( attacker.Skills[SkillName.Tactics].Value, 0.625, 100.0, 6.25 ); double lumberBonus = GetBonus( attacker.Skills[SkillName.Lumberjacking].Value, 0.200, 100.0, 10.00 ); if ( Type != WeaponType.Axe ) lumberBonus = 0.0; #endregion #region Modifiers /* * The following are damage modifiers whose effect shows on the status bar. * Capped at 100% total. */ int damageBonus = AosAttributes.GetValue( attacker, AosAttribute.WeaponDamage ); // Horrific Beast transformation gives a +25% bonus to damage. if ( TransformationSpell.UnderTransformation( attacker, typeof( HorrificBeastSpell ) ) ) damageBonus += 25; // Divine Fury gives a +10% bonus to damage. if ( Spells.Chivalry.DivineFurySpell.UnderEffect( attacker ) ) damageBonus += 10; int defenseMasteryMalus = 0; // Defense Mastery gives a -50%/-80% malus to damage. if ( Server.Items.DefenseMastery.GetMalus( attacker, ref defenseMasteryMalus ) ) damageBonus -= defenseMasteryMalus; int discordanceEffect = 0; // Discordance gives a -2%/-48% malus to damage. if ( SkillHandlers.Discordance.GetEffect( attacker, ref discordanceEffect ) ) damageBonus -= discordanceEffect * 2; if ( damageBonus > 100 ) damageBonus = 100; #endregion double totalBonus = strengthBonus + anatomyBonus + tacticsBonus + lumberBonus + ((double)(GetDamageBonus() + damageBonus) / 100.0); return damage + (int)(damage * totalBonus); } public virtual int VirtualDamageBonus{ get{ return 0; } } public virtual int ComputeDamageAOS( Mobile attacker, Mobile defender ) { return (int)ScaleDamageAOS( attacker, GetBaseDamage( attacker ), true ); } public virtual double ScaleDamageOld( Mobile attacker, double damage, bool checkSkills ) { if ( checkSkills ) { attacker.CheckSkill( SkillName.Tactics, 0.0, 120.0 ); // Passively check tactics for gain attacker.CheckSkill( SkillName.Anatomy, 0.0, 120.0 ); // Passively check Anatomy for gain if ( Type == WeaponType.Axe ) attacker.CheckSkill( SkillName.Lumberjacking, 0.0, 100.0 ); // Passively check Lumberjacking for gain } /* Compute tactics modifier * : 0.0 = 50% loss * : 50.0 = unchanged * : 100.0 = 50% bonus */ double tacticsBonus = (attacker.Skills[SkillName.Tactics].Value - 50.0) / 100.0; /* Compute strength modifier * : 1% bonus for every 5 strength */ double strBonus = (attacker.Str / 5.0) / 100.0; /* Compute anatomy modifier * : 1% bonus for every 5 points of anatomy * : +10% bonus at Grandmaster or higher */ double anatomyValue = attacker.Skills[SkillName.Anatomy].Value; double anatomyBonus = (anatomyValue / 5.0) / 100.0; if ( anatomyValue >= 100.0 ) anatomyBonus += 0.1; /* Compute lumberjacking bonus * : 1% bonus for every 5 points of lumberjacking * : +10% bonus at Grandmaster or higher */ double lumberBonus; if ( Type == WeaponType.Axe ) { double lumberValue = attacker.Skills[SkillName.Lumberjacking].Value; lumberBonus = (lumberValue / 5.0) / 100.0; if ( lumberValue >= 100.0 ) lumberBonus += 0.1; } else { lumberBonus = 0.0; } // New quality bonus: double qualityBonus = ((int)m_Quality - 1) * 0.2; // Apply bonuses damage += (damage * tacticsBonus) + (damage * strBonus) + (damage * anatomyBonus) + (damage * lumberBonus) + (damage * qualityBonus) + ((damage * VirtualDamageBonus) / 100); // Old quality bonus: #if false /* Apply quality offset * : Low : -4 * : Regular : 0 * : Exceptional : +4 */ damage += ((int)m_Quality - 1) * 4.0; #endif /* Apply damage level offset * : Regular : 0 * : Ruin : 1 * : Might : 3 * : Force : 5 * : Power : 7 * : Vanq : 9 */ if ( m_DamageLevel != WeaponDamageLevel.Regular ) damage += (2.0 * (int)m_DamageLevel) - 1.0; // Halve the computed damage and return damage /= 2.0; return ScaleDamageByDurability( (int)damage ); } public virtual int ScaleDamageByDurability( int damage ) { int scale = 100; if ( m_MaxHits > 0 && m_Hits < m_MaxHits ) scale = 50 + ((50 * m_Hits) / m_MaxHits); return AOS.Scale( damage, scale ); } public virtual int ComputeDamage( Mobile attacker, Mobile defender ) { if ( Core.AOS ) return ComputeDamageAOS( attacker, defender ); return (int)ScaleDamageOld( attacker, GetBaseDamage( attacker ), true ); } public virtual void PlayHurtAnimation( Mobile from ) { int action; int frames; switch ( from.Body.Type ) { case BodyType.Sea: case BodyType.Animal: { action = 7; frames = 5; break; } case BodyType.Monster: { action = 10; frames = 4; break; } case BodyType.Human: { action = 20; frames = 5; break; } default: return; } if ( from.Mounted ) return; from.Animate( action, frames, 1, true, false, 0 ); } public virtual void PlaySwingAnimation( Mobile from ) { int action; switch ( from.Body.Type ) { case BodyType.Sea: case BodyType.Animal: { action = Utility.Random( 5, 2 ); break; } case BodyType.Monster: { switch ( Animation ) { default: case WeaponAnimation.Wrestle: case WeaponAnimation.Bash1H: case WeaponAnimation.Pierce1H: case WeaponAnimation.Slash1H: case WeaponAnimation.Bash2H: case WeaponAnimation.Pierce2H: case WeaponAnimation.Slash2H: action = Utility.Random( 4, 3 ); break; case WeaponAnimation.ShootBow: return; // 7 case WeaponAnimation.ShootXBow: return; // 8 } break; } case BodyType.Human: { if ( !from.Mounted ) { action = (int)Animation; } else { switch ( Animation ) { default: case WeaponAnimation.Wrestle: case WeaponAnimation.Bash1H: case WeaponAnimation.Pierce1H: case WeaponAnimation.Slash1H: action = 26; break; case WeaponAnimation.Bash2H: case WeaponAnimation.Pierce2H: case WeaponAnimation.Slash2H: action = 29; break; case WeaponAnimation.ShootBow: action = 27; break; case WeaponAnimation.ShootXBow: action = 28; break; } } break; } default: return; } from.Animate( action, 7, 1, true, false, 0 ); } #region Serialization/Deserialization private static void SetSaveFlag( ref SaveFlag flags, SaveFlag toSet, bool setIf ) { if ( setIf ) flags |= toSet; } private static bool GetSaveFlag( SaveFlag flags, SaveFlag toGet ) { return ( (flags & toGet) != 0 ); } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); writer.Write( (int) 8 ); // version SaveFlag flags = SaveFlag.None; SetSaveFlag( ref flags, SaveFlag.DamageLevel, m_DamageLevel != WeaponDamageLevel.Regular ); SetSaveFlag( ref flags, SaveFlag.AccuracyLevel, m_AccuracyLevel != WeaponAccuracyLevel.Regular ); SetSaveFlag( ref flags, SaveFlag.DurabilityLevel, m_DurabilityLevel != WeaponDurabilityLevel.Regular ); SetSaveFlag( ref flags, SaveFlag.Quality, m_Quality != WeaponQuality.Regular ); SetSaveFlag( ref flags, SaveFlag.Hits, m_Hits != 0 ); SetSaveFlag( ref flags, SaveFlag.MaxHits, m_MaxHits != 0 ); SetSaveFlag( ref flags, SaveFlag.Slayer, m_Slayer != SlayerName.None ); SetSaveFlag( ref flags, SaveFlag.Poison, m_Poison != null ); SetSaveFlag( ref flags, SaveFlag.PoisonCharges, m_PoisonCharges != 0 ); SetSaveFlag( ref flags, SaveFlag.Crafter, m_Crafter != null ); SetSaveFlag( ref flags, SaveFlag.Identified, m_Identified != false ); SetSaveFlag( ref flags, SaveFlag.StrReq, m_StrReq != -1 ); SetSaveFlag( ref flags, SaveFlag.DexReq, m_DexReq != -1 ); SetSaveFlag( ref flags, SaveFlag.IntReq, m_IntReq != -1 ); SetSaveFlag( ref flags, SaveFlag.MinDamage, m_MinDamage != -1 ); SetSaveFlag( ref flags, SaveFlag.MaxDamage, m_MaxDamage != -1 ); SetSaveFlag( ref flags, SaveFlag.HitSound, m_HitSound != -1 ); SetSaveFlag( ref flags, SaveFlag.MissSound, m_MissSound != -1 ); SetSaveFlag( ref flags, SaveFlag.Speed, m_Speed != -1 ); SetSaveFlag( ref flags, SaveFlag.MaxRange, m_MaxRange != -1 ); SetSaveFlag( ref flags, SaveFlag.Skill, m_Skill != (SkillName)(-1) ); SetSaveFlag( ref flags, SaveFlag.Type, m_Type != (WeaponType)(-1) ); SetSaveFlag( ref flags, SaveFlag.Animation, m_Animation != (WeaponAnimation)(-1) ); SetSaveFlag( ref flags, SaveFlag.Resource, m_Resource != CraftResource.Iron ); SetSaveFlag( ref flags, SaveFlag.xAttributes, !m_AosAttributes.IsEmpty ); SetSaveFlag( ref flags, SaveFlag.xWeaponAttributes, !m_AosWeaponAttributes.IsEmpty ); SetSaveFlag( ref flags, SaveFlag.PlayerConstructed, m_PlayerConstructed ); SetSaveFlag( ref flags, SaveFlag.SkillBonuses, !m_AosSkillBonuses.IsEmpty ); SetSaveFlag( ref flags, SaveFlag.Slayer2, m_Slayer2 != SlayerName.None ); SetSaveFlag( ref flags, SaveFlag.ElementalDamages, !m_AosElementDamages.IsEmpty ); writer.Write( (int) flags ); if ( GetSaveFlag( flags, SaveFlag.DamageLevel ) ) writer.Write( (int) m_DamageLevel ); if ( GetSaveFlag( flags, SaveFlag.AccuracyLevel ) ) writer.Write( (int) m_AccuracyLevel ); if ( GetSaveFlag( flags, SaveFlag.DurabilityLevel ) ) writer.Write( (int) m_DurabilityLevel ); if ( GetSaveFlag( flags, SaveFlag.Quality ) ) writer.Write( (int) m_Quality ); if ( GetSaveFlag( flags, SaveFlag.Hits ) ) writer.Write( (int) m_Hits ); if ( GetSaveFlag( flags, SaveFlag.MaxHits ) ) writer.Write( (int) m_MaxHits ); if ( GetSaveFlag( flags, SaveFlag.Slayer ) ) writer.Write( (int) m_Slayer ); if ( GetSaveFlag( flags, SaveFlag.Poison ) ) Poison.Serialize( m_Poison, writer ); if ( GetSaveFlag( flags, SaveFlag.PoisonCharges ) ) writer.Write( (int) m_PoisonCharges ); if ( GetSaveFlag( flags, SaveFlag.Crafter ) ) writer.Write( (Mobile) m_Crafter ); if ( GetSaveFlag( flags, SaveFlag.StrReq ) ) writer.Write( (int) m_StrReq ); if ( GetSaveFlag( flags, SaveFlag.DexReq ) ) writer.Write( (int) m_DexReq ); if ( GetSaveFlag( flags, SaveFlag.IntReq ) ) writer.Write( (int) m_IntReq ); if ( GetSaveFlag( flags, SaveFlag.MinDamage ) ) writer.Write( (int) m_MinDamage ); if ( GetSaveFlag( flags, SaveFlag.MaxDamage ) ) writer.Write( (int) m_MaxDamage ); if ( GetSaveFlag( flags, SaveFlag.HitSound ) ) writer.Write( (int) m_HitSound ); if ( GetSaveFlag( flags, SaveFlag.MissSound ) ) writer.Write( (int) m_MissSound ); if ( GetSaveFlag( flags, SaveFlag.Speed ) ) writer.Write( (int) m_Speed ); if ( GetSaveFlag( flags, SaveFlag.MaxRange ) ) writer.Write( (int) m_MaxRange ); if ( GetSaveFlag( flags, SaveFlag.Skill ) ) writer.Write( (int) m_Skill ); if ( GetSaveFlag( flags, SaveFlag.Type ) ) writer.Write( (int) m_Type ); if ( GetSaveFlag( flags, SaveFlag.Animation ) ) writer.Write( (int) m_Animation ); if ( GetSaveFlag( flags, SaveFlag.Resource ) ) writer.Write( (int) m_Resource ); if ( GetSaveFlag( flags, SaveFlag.xAttributes ) ) m_AosAttributes.Serialize( writer ); if ( GetSaveFlag( flags, SaveFlag.xWeaponAttributes ) ) m_AosWeaponAttributes.Serialize( writer ); if ( GetSaveFlag( flags, SaveFlag.SkillBonuses ) ) m_AosSkillBonuses.Serialize( writer ); if ( GetSaveFlag( flags, SaveFlag.Slayer2 ) ) writer.Write( (int)m_Slayer2 ); if( GetSaveFlag( flags, SaveFlag.ElementalDamages ) ) m_AosElementDamages.Serialize( writer ); } [Flags] private enum SaveFlag { None = 0x00000000, DamageLevel = 0x00000001, AccuracyLevel = 0x00000002, DurabilityLevel = 0x00000004, Quality = 0x00000008, Hits = 0x00000010, MaxHits = 0x00000020, Slayer = 0x00000040, Poison = 0x00000080, PoisonCharges = 0x00000100, Crafter = 0x00000200, Identified = 0x00000400, StrReq = 0x00000800, DexReq = 0x00001000, IntReq = 0x00002000, MinDamage = 0x00004000, MaxDamage = 0x00008000, HitSound = 0x00010000, MissSound = 0x00020000, Speed = 0x00040000, MaxRange = 0x00080000, Skill = 0x00100000, Type = 0x00200000, Animation = 0x00400000, Resource = 0x00800000, xAttributes = 0x01000000, xWeaponAttributes = 0x02000000, PlayerConstructed = 0x04000000, SkillBonuses = 0x08000000, Slayer2 = 0x10000000, ElementalDamages = 0x20000000 } public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); switch ( version ) { case 8: case 7: case 6: case 5: { SaveFlag flags = (SaveFlag)reader.ReadInt(); if ( GetSaveFlag( flags, SaveFlag.DamageLevel ) ) { m_DamageLevel = (WeaponDamageLevel)reader.ReadInt(); if ( m_DamageLevel > WeaponDamageLevel.Vanq ) m_DamageLevel = WeaponDamageLevel.Ruin; } if ( GetSaveFlag( flags, SaveFlag.AccuracyLevel ) ) { m_AccuracyLevel = (WeaponAccuracyLevel)reader.ReadInt(); if ( m_AccuracyLevel > WeaponAccuracyLevel.Supremely ) m_AccuracyLevel = WeaponAccuracyLevel.Accurate; } if ( GetSaveFlag( flags, SaveFlag.DurabilityLevel ) ) { m_DurabilityLevel = (WeaponDurabilityLevel)reader.ReadInt(); if ( m_DurabilityLevel > WeaponDurabilityLevel.Indestructible ) m_DurabilityLevel = WeaponDurabilityLevel.Durable; } if ( GetSaveFlag( flags, SaveFlag.Quality ) ) m_Quality = (WeaponQuality)reader.ReadInt(); else m_Quality = WeaponQuality.Regular; if ( GetSaveFlag( flags, SaveFlag.Hits ) ) m_Hits = reader.ReadInt(); if ( GetSaveFlag( flags, SaveFlag.MaxHits ) ) m_MaxHits = reader.ReadInt(); if ( GetSaveFlag( flags, SaveFlag.Slayer ) ) m_Slayer = (SlayerName)reader.ReadInt(); if ( GetSaveFlag( flags, SaveFlag.Poison ) ) m_Poison = Poison.Deserialize( reader ); if ( GetSaveFlag( flags, SaveFlag.PoisonCharges ) ) m_PoisonCharges = reader.ReadInt(); if ( GetSaveFlag( flags, SaveFlag.Crafter ) ) m_Crafter = reader.ReadMobile(); if ( GetSaveFlag( flags, SaveFlag.Identified ) ) m_Identified = ( version >= 6 || reader.ReadBool() ); if ( GetSaveFlag( flags, SaveFlag.StrReq ) ) m_StrReq = reader.ReadInt(); else m_StrReq = -1; if ( GetSaveFlag( flags, SaveFlag.DexReq ) ) m_DexReq = reader.ReadInt(); else m_DexReq = -1; if ( GetSaveFlag( flags, SaveFlag.IntReq ) ) m_IntReq = reader.ReadInt(); else m_IntReq = -1; if ( GetSaveFlag( flags, SaveFlag.MinDamage ) ) m_MinDamage = reader.ReadInt(); else m_MinDamage = -1; if ( GetSaveFlag( flags, SaveFlag.MaxDamage ) ) m_MaxDamage = reader.ReadInt(); else m_MaxDamage = -1; if ( GetSaveFlag( flags, SaveFlag.HitSound ) ) m_HitSound = reader.ReadInt(); else m_HitSound = -1; if ( GetSaveFlag( flags, SaveFlag.MissSound ) ) m_MissSound = reader.ReadInt(); else m_MissSound = -1; if ( GetSaveFlag( flags, SaveFlag.Speed ) ) m_Speed = reader.ReadInt(); else m_Speed = -1; if ( GetSaveFlag( flags, SaveFlag.MaxRange ) ) m_MaxRange = reader.ReadInt(); else m_MaxRange = -1; if ( GetSaveFlag( flags, SaveFlag.Skill ) ) m_Skill = (SkillName)reader.ReadInt(); else m_Skill = (SkillName)(-1); if ( GetSaveFlag( flags, SaveFlag.Type ) ) m_Type = (WeaponType)reader.ReadInt(); else m_Type = (WeaponType)(-1); if ( GetSaveFlag( flags, SaveFlag.Animation ) ) m_Animation = (WeaponAnimation)reader.ReadInt(); else m_Animation = (WeaponAnimation)(-1); if ( GetSaveFlag( flags, SaveFlag.Resource ) ) m_Resource = (CraftResource)reader.ReadInt(); else m_Resource = CraftResource.Iron; if ( GetSaveFlag( flags, SaveFlag.xAttributes ) ) m_AosAttributes = new AosAttributes( this, reader ); else m_AosAttributes = new AosAttributes( this ); if ( GetSaveFlag( flags, SaveFlag.xWeaponAttributes ) ) m_AosWeaponAttributes = new AosWeaponAttributes( this, reader ); else m_AosWeaponAttributes = new AosWeaponAttributes( this ); if ( UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular && Parent is Mobile ) { m_SkillMod = new DefaultSkillMod( AccuracySkill, true, (int)m_AccuracyLevel * 5 ); ((Mobile)Parent).AddSkillMod( m_SkillMod ); } if ( version < 7 && m_AosWeaponAttributes.MageWeapon != 0 ) m_AosWeaponAttributes.MageWeapon = 30 - m_AosWeaponAttributes.MageWeapon; if ( Core.AOS && m_AosWeaponAttributes.MageWeapon != 0 && m_AosWeaponAttributes.MageWeapon != 30 && Parent is Mobile ) { m_MageMod = new DefaultSkillMod( SkillName.Magery, true, -30 + m_AosWeaponAttributes.MageWeapon ); ((Mobile)Parent).AddSkillMod( m_MageMod ); } if ( GetSaveFlag( flags, SaveFlag.PlayerConstructed ) ) m_PlayerConstructed = true; if( GetSaveFlag( flags, SaveFlag.SkillBonuses ) ) m_AosSkillBonuses = new AosSkillBonuses( this, reader ); else m_AosSkillBonuses = new AosSkillBonuses( this ); // mod to randomly add sockets and socketablility features to weapons. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5 of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // uncomment the following line to prevent artifacts from being socketed // if(ArtifactRarity == 0) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0); if( GetSaveFlag( flags, SaveFlag.Slayer2 ) ) m_Slayer2 = (SlayerName)reader.ReadInt(); if( GetSaveFlag( flags, SaveFlag.ElementalDamages ) ) m_AosElementDamages = new AosElementAttributes( this, reader ); else m_AosElementDamages = new AosElementAttributes( this ); break; } case 4: { m_Slayer = (SlayerName)reader.ReadInt(); goto case 3; } case 3: { m_StrReq = reader.ReadInt(); m_DexReq = reader.ReadInt(); m_IntReq = reader.ReadInt(); goto case 2; } case 2: { m_Identified = reader.ReadBool(); goto case 1; } case 1: { m_MaxRange = reader.ReadInt(); goto case 0; } case 0: { if ( version == 0 ) m_MaxRange = 1; // default if ( version < 5 ) { m_Resource = CraftResource.Iron; m_AosAttributes = new AosAttributes( this ); m_AosWeaponAttributes = new AosWeaponAttributes( this ); m_AosElementDamages = new AosElementAttributes( this ); m_AosSkillBonuses = new AosSkillBonuses( this ); } m_MinDamage = reader.ReadInt(); m_MaxDamage = reader.ReadInt(); m_Speed = reader.ReadInt(); m_HitSound = reader.ReadInt(); m_MissSound = reader.ReadInt(); m_Skill = (SkillName)reader.ReadInt(); m_Type = (WeaponType)reader.ReadInt(); m_Animation = (WeaponAnimation)reader.ReadInt(); m_DamageLevel = (WeaponDamageLevel)reader.ReadInt(); m_AccuracyLevel = (WeaponAccuracyLevel)reader.ReadInt(); m_DurabilityLevel = (WeaponDurabilityLevel)reader.ReadInt(); m_Quality = (WeaponQuality)reader.ReadInt(); m_Crafter = reader.ReadMobile(); m_Poison = Poison.Deserialize( reader ); m_PoisonCharges = reader.ReadInt(); if ( m_StrReq == OldStrengthReq ) m_StrReq = -1; if ( m_DexReq == OldDexterityReq ) m_DexReq = -1; if ( m_IntReq == OldIntelligenceReq ) m_IntReq = -1; if ( m_MinDamage == OldMinDamage ) m_MinDamage = -1; if ( m_MaxDamage == OldMaxDamage ) m_MaxDamage = -1; if ( m_HitSound == OldHitSound ) m_HitSound = -1; if ( m_MissSound == OldMissSound ) m_MissSound = -1; if ( m_Speed == OldSpeed ) m_Speed = -1; if ( m_MaxRange == OldMaxRange ) m_MaxRange = -1; if ( m_Skill == OldSkill ) m_Skill = (SkillName)(-1); if ( m_Type == OldType ) m_Type = (WeaponType)(-1); if ( m_Animation == OldAnimation ) m_Animation = (WeaponAnimation)(-1); if ( UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular && Parent is Mobile ) { m_SkillMod = new DefaultSkillMod( AccuracySkill, true, (int)m_AccuracyLevel * 5); ((Mobile)Parent).AddSkillMod( m_SkillMod ); } break; } } if ( Core.AOS && Parent is Mobile ) m_AosSkillBonuses.AddTo( (Mobile)Parent ); int strBonus = m_AosAttributes.BonusStr; int dexBonus = m_AosAttributes.BonusDex; int intBonus = m_AosAttributes.BonusInt; if ( this.Parent is Mobile && (strBonus != 0 || dexBonus != 0 || intBonus != 0) ) { Mobile m = (Mobile)this.Parent; string modName = this.Serial.ToString(); if ( strBonus != 0 ) m.AddStatMod( new StatMod( StatType.Str, modName + "Str", strBonus, TimeSpan.Zero ) ); if ( dexBonus != 0 ) m.AddStatMod( new StatMod( StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero ) ); if ( intBonus != 0 ) m.AddStatMod( new StatMod( StatType.Int, modName + "Int", intBonus, TimeSpan.Zero ) ); } if ( Parent is Mobile ) ((Mobile)Parent).CheckStatTimers(); if ( m_Hits <= 0 && m_MaxHits <= 0 ) { m_Hits = m_MaxHits = Utility.RandomMinMax( InitMinHits, InitMaxHits ); } if ( version < 6 ) m_PlayerConstructed = true; // we don't know, so, assume it's crafted } #endregion public BaseWeapon( int itemID ) : base( itemID ) { Layer = (Layer)ItemData.Quality; m_Quality = WeaponQuality.Regular; m_StrReq = -1; m_DexReq = -1; m_IntReq = -1; m_MinDamage = -1; m_MaxDamage = -1; m_HitSound = -1; m_MissSound = -1; m_Speed = -1; m_MaxRange = -1; m_Skill = (SkillName)(-1); m_Type = (WeaponType)(-1); m_Animation = (WeaponAnimation)(-1); m_Hits = m_MaxHits = Utility.RandomMinMax( InitMinHits, InitMaxHits ); m_Resource = CraftResource.Iron; m_AosAttributes = new AosAttributes( this ); m_AosWeaponAttributes = new AosWeaponAttributes( this ); m_AosSkillBonuses = new AosSkillBonuses( this ); m_AosElementDamages = new AosElementAttributes( this ); } public BaseWeapon( Serial serial ) : base( serial ) { } private string GetNameString() { string name = this.Name; if ( name == null ) name = String.Format( "#{0}", LabelNumber ); return name; } [Hue, CommandProperty( AccessLevel.GameMaster )] public override int Hue { get{ return base.Hue; } set{ base.Hue = value; InvalidateProperties(); } } public int GetElementalDamageHue() { int phys, fire, cold, pois, nrgy; GetDamageTypes( null, out phys, out fire, out cold, out pois, out nrgy ); //Order is Cold, Energy, Fire, Poison, Physical left int currentMax = 50; int hue = 0; if( pois >= currentMax ) { hue = 1267 + (pois - 50) / 10; currentMax = pois; } if( fire >= currentMax ) { hue = 1255 + (fire - 50) / 10; currentMax = fire; } if( nrgy >= currentMax ) { hue = 1273 + (nrgy - 50) / 10; currentMax = nrgy; } if( cold >= currentMax ) { hue = 1261 + (cold - 50) / 10; currentMax = cold; } return hue; } public override void AddNameProperty( ObjectPropertyList list ) { int oreType; switch ( m_Resource ) { case CraftResource.DullCopper: oreType = 1053108; break; // dull copper case CraftResource.ShadowIron: oreType = 1053107; break; // shadow iron case CraftResource.Copper: oreType = 1053106; break; // copper case CraftResource.Bronze: oreType = 1053105; break; // bronze case CraftResource.Gold: oreType = 1053104; break; // golden case CraftResource.Agapite: oreType = 1053103; break; // agapite case CraftResource.Verite: oreType = 1053102; break; // verite case CraftResource.Valorite: oreType = 1053101; break; // valorite case CraftResource.SpinedLeather: oreType = 1061118; break; // spined case CraftResource.HornedLeather: oreType = 1061117; break; // horned case CraftResource.BarbedLeather: oreType = 1061116; break; // barbed case CraftResource.RedScales: oreType = 1060814; break; // red case CraftResource.YellowScales: oreType = 1060818; break; // yellow case CraftResource.BlackScales: oreType = 1060820; break; // black case CraftResource.GreenScales: oreType = 1060819; break; // green case CraftResource.WhiteScales: oreType = 1060821; break; // white case CraftResource.BlueScales: oreType = 1060815; break; // blue default: oreType = 0; break; } if ( oreType != 0 ) list.Add( 1053099, "#{0}\t{1}", oreType, GetNameString() ); // ~1_oretype~ ~2_armortype~ else if ( Name == null ) list.Add( LabelNumber ); else list.Add( Name ); } public override bool AllowEquipedCast( Mobile from ) { if ( base.AllowEquipedCast( from ) ) return true; return ( m_AosAttributes.SpellChanneling != 0 ); } public virtual int ArtifactRarity { get{ return 0; } } public virtual int GetLuckBonus() { CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); if ( resInfo == null ) return 0; CraftAttributeInfo attrInfo = resInfo.AttributeInfo; if ( attrInfo == null ) return 0; return attrInfo.WeaponLuck; } public override void GetProperties( ObjectPropertyList list ) { base.GetProperties( list ); if ( m_Crafter != null ) list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ #region Factions if ( m_FactionState != null ) list.Add( 1041350 ); // faction item #endregion if ( m_AosSkillBonuses != null ) m_AosSkillBonuses.GetProperties( list ); if ( m_Quality == WeaponQuality.Exceptional ) list.Add( 1060636 ); // exceptional if( RequiredRace == Race.Elf ) list.Add( 1075086 ); // Elves Only if ( ArtifactRarity > 0 ) list.Add( 1061078, ArtifactRarity.ToString() ); // artifact rarity ~1_val~ if ( this is IUsesRemaining && ((IUsesRemaining)this).ShowUsesRemaining ) list.Add( 1060584, ((IUsesRemaining)this).UsesRemaining.ToString() ); // uses remaining: ~1_val~ if ( m_Poison != null && m_PoisonCharges > 0 ) list.Add( 1062412 + m_Poison.Level, m_PoisonCharges.ToString() ); if( m_Slayer != SlayerName.None ) { SlayerEntry entry = SlayerGroup.GetEntryByName( m_Slayer ); if( entry != null ) list.Add( entry.Title ); } if( m_Slayer2 != SlayerName.None ) { SlayerEntry entry = SlayerGroup.GetEntryByName( m_Slayer2 ); if( entry != null ) list.Add( entry.Title ); } base.AddResistanceProperties( list ); int prop; if ( (prop = m_AosWeaponAttributes.UseBestSkill) != 0 ) list.Add( 1060400 ); // use best weapon skill if ( (prop = (GetDamageBonus() + m_AosAttributes.WeaponDamage)) != 0 ) list.Add( 1060401, prop.ToString() ); // damage increase ~1_val~% if ( (prop = m_AosAttributes.DefendChance) != 0 ) list.Add( 1060408, prop.ToString() ); // defense chance increase ~1_val~% if ( (prop = m_AosAttributes.EnhancePotions) != 0 ) list.Add( 1060411, prop.ToString() ); // enhance potions ~1_val~% if ( (prop = m_AosAttributes.CastRecovery) != 0 ) list.Add( 1060412, prop.ToString() ); // faster cast recovery ~1_val~ if ( (prop = m_AosAttributes.CastSpeed) != 0 ) list.Add( 1060413, prop.ToString() ); // faster casting ~1_val~ if ( (prop = (GetHitChanceBonus() + m_AosAttributes.AttackChance)) != 0 ) list.Add( 1060415, prop.ToString() ); // hit chance increase ~1_val~% if ( (prop = m_AosWeaponAttributes.HitColdArea) != 0 ) list.Add( 1060416, prop.ToString() ); // hit cold area ~1_val~% if ( (prop = m_AosWeaponAttributes.HitDispel) != 0 ) list.Add( 1060417, prop.ToString() ); // hit dispel ~1_val~% if ( (prop = m_AosWeaponAttributes.HitEnergyArea) != 0 ) list.Add( 1060418, prop.ToString() ); // hit energy area ~1_val~% if ( (prop = m_AosWeaponAttributes.HitFireArea) != 0 ) list.Add( 1060419, prop.ToString() ); // hit fire area ~1_val~% if ( (prop = m_AosWeaponAttributes.HitFireball) != 0 ) list.Add( 1060420, prop.ToString() ); // hit fireball ~1_val~% if ( (prop = m_AosWeaponAttributes.HitHarm) != 0 ) list.Add( 1060421, prop.ToString() ); // hit harm ~1_val~% if ( (prop = m_AosWeaponAttributes.HitLeechHits) != 0 ) list.Add( 1060422, prop.ToString() ); // hit life leech ~1_val~% if ( (prop = m_AosWeaponAttributes.HitLightning) != 0 ) list.Add( 1060423, prop.ToString() ); // hit lightning ~1_val~% if ( (prop = m_AosWeaponAttributes.HitLowerAttack) != 0 ) list.Add( 1060424, prop.ToString() ); // hit lower attack ~1_val~% if ( (prop = m_AosWeaponAttributes.HitLowerDefend) != 0 ) list.Add( 1060425, prop.ToString() ); // hit lower defense ~1_val~% if ( (prop = m_AosWeaponAttributes.HitMagicArrow) != 0 ) list.Add( 1060426, prop.ToString() ); // hit magic arrow ~1_val~% if ( (prop = m_AosWeaponAttributes.HitLeechMana) != 0 ) list.Add( 1060427, prop.ToString() ); // hit mana leech ~1_val~% if ( (prop = m_AosWeaponAttributes.HitPhysicalArea) != 0 ) list.Add( 1060428, prop.ToString() ); // hit physical area ~1_val~% if ( (prop = m_AosWeaponAttributes.HitPoisonArea) != 0 ) list.Add( 1060429, prop.ToString() ); // hit poison area ~1_val~% if ( (prop = m_AosWeaponAttributes.HitLeechStam) != 0 ) list.Add( 1060430, prop.ToString() ); // hit stamina leech ~1_val~% if ( (prop = m_AosAttributes.BonusDex) != 0 ) list.Add( 1060409, prop.ToString() ); // dexterity bonus ~1_val~ if ( (prop = m_AosAttributes.BonusHits) != 0 ) list.Add( 1060431, prop.ToString() ); // hit point increase ~1_val~ if ( (prop = m_AosAttributes.BonusInt) != 0 ) list.Add( 1060432, prop.ToString() ); // intelligence bonus ~1_val~ if ( (prop = m_AosAttributes.LowerManaCost) != 0 ) list.Add( 1060433, prop.ToString() ); // lower mana cost ~1_val~% if ( (prop = m_AosAttributes.LowerRegCost) != 0 ) list.Add( 1060434, prop.ToString() ); // lower reagent cost ~1_val~% if ( (prop = GetLowerStatReq()) != 0 ) list.Add( 1060435, prop.ToString() ); // lower requirements ~1_val~% if ( (prop = (GetLuckBonus() + m_AosAttributes.Luck)) != 0 ) list.Add( 1060436, prop.ToString() ); // luck ~1_val~ if ( (prop = m_AosWeaponAttributes.MageWeapon) != 0 ) list.Add( 1060438, (30 - prop).ToString() ); // mage weapon -~1_val~ skill if ( (prop = m_AosAttributes.BonusMana) != 0 ) list.Add( 1060439, prop.ToString() ); // mana increase ~1_val~ if ( (prop = m_AosAttributes.RegenMana) != 0 ) list.Add( 1060440, prop.ToString() ); // mana regeneration ~1_val~ if ( (prop = m_AosAttributes.NightSight) != 0 ) list.Add( 1060441 ); // night sight if ( (prop = m_AosAttributes.ReflectPhysical) != 0 ) list.Add( 1060442, prop.ToString() ); // reflect physical damage ~1_val~% if ( (prop = m_AosAttributes.RegenStam) != 0 ) list.Add( 1060443, prop.ToString() ); // stamina regeneration ~1_val~ if ( (prop = m_AosAttributes.RegenHits) != 0 ) list.Add( 1060444, prop.ToString() ); // hit point regeneration ~1_val~ if ( (prop = m_AosWeaponAttributes.SelfRepair) != 0 ) list.Add( 1060450, prop.ToString() ); // self repair ~1_val~ if ( (prop = m_AosAttributes.SpellChanneling) != 0 ) list.Add( 1060482 ); // spell channeling if ( (prop = m_AosAttributes.SpellDamage) != 0 ) list.Add( 1060483, prop.ToString() ); // spell damage increase ~1_val~% if ( (prop = m_AosAttributes.BonusStam) != 0 ) list.Add( 1060484, prop.ToString() ); // stamina increase ~1_val~ if ( (prop = m_AosAttributes.BonusStr) != 0 ) list.Add( 1060485, prop.ToString() ); // strength bonus ~1_val~ if ( (prop = m_AosAttributes.WeaponSpeed) != 0 ) list.Add( 1060486, prop.ToString() ); // swing speed increase ~1_val~% int phys, fire, cold, pois, nrgy; GetDamageTypes( null, out phys, out fire, out cold, out pois, out nrgy ); if ( phys != 0 ) list.Add( 1060403, phys.ToString() ); // physical damage ~1_val~% if ( fire != 0 ) list.Add( 1060405, fire.ToString() ); // fire damage ~1_val~% if ( cold != 0 ) list.Add( 1060404, cold.ToString() ); // cold damage ~1_val~% if ( pois != 0 ) list.Add( 1060406, pois.ToString() ); // poison damage ~1_val~% if ( nrgy != 0 ) list.Add( 1060407, nrgy.ToString() ); // energy damage ~1_val~% list.Add( 1061168, "{0}\t{1}", MinDamage.ToString(), MaxDamage.ToString() ); // weapon damage ~1_val~ - ~2_val~ list.Add( 1061167, Speed.ToString() ); // weapon speed ~1_val~ if ( MaxRange > 1 ) list.Add( 1061169, MaxRange.ToString() ); // range ~1_val~ int strReq = AOS.Scale( StrRequirement, 100 - GetLowerStatReq() ); if ( strReq > 0 ) list.Add( 1061170, strReq.ToString() ); // strength requirement ~1_val~ if ( Layer == Layer.TwoHanded ) list.Add( 1061171 ); // two-handed weapon else list.Add( 1061824 ); // one-handed weapon if ( Core.SE || m_AosWeaponAttributes.UseBestSkill == 0 ) { switch ( Skill ) { case SkillName.Swords: list.Add( 1061172 ); break; // skill required: swordsmanship case SkillName.Macing: list.Add( 1061173 ); break; // skill required: mace fighting case SkillName.Fencing: list.Add( 1061174 ); break; // skill required: fencing case SkillName.Archery: list.Add( 1061175 ); break; // skill required: archery } } if ( m_Hits >= 0 && m_MaxHits > 0 ) list.Add( 1060639, "{0}\t{1}", m_Hits, m_MaxHits ); // durability ~1_val~ / ~2_val~ } public override void OnSingleClick( Mobile from ) { List<EquipInfoAttribute> attrs = new List<EquipInfoAttribute>(); if ( DisplayLootType ) { if ( LootType == LootType.Blessed ) attrs.Add( new EquipInfoAttribute( 1038021 ) ); // blessed else if ( LootType == LootType.Cursed ) attrs.Add( new EquipInfoAttribute( 1049643 ) ); // cursed } #region Factions if ( m_FactionState != null ) attrs.Add( new EquipInfoAttribute( 1041350 ) ); // faction item #endregion if ( m_Quality == WeaponQuality.Exceptional ) attrs.Add( new EquipInfoAttribute( 1018305 - (int)m_Quality ) ); if ( m_Identified || from.AccessLevel >= AccessLevel.GameMaster ) { if( m_Slayer != SlayerName.None ) { SlayerEntry entry = SlayerGroup.GetEntryByName( m_Slayer ); if( entry != null ) attrs.Add( new EquipInfoAttribute( entry.Title ) ); } if( m_Slayer2 != SlayerName.None ) { SlayerEntry entry = SlayerGroup.GetEntryByName( m_Slayer2 ); if( entry != null ) attrs.Add( new EquipInfoAttribute( entry.Title ) ); } if ( m_DurabilityLevel != WeaponDurabilityLevel.Regular ) attrs.Add( new EquipInfoAttribute( 1038000 + (int)m_DurabilityLevel ) ); if ( m_DamageLevel != WeaponDamageLevel.Regular ) attrs.Add( new EquipInfoAttribute( 1038015 + (int)m_DamageLevel ) ); if ( m_AccuracyLevel != WeaponAccuracyLevel.Regular ) attrs.Add( new EquipInfoAttribute( 1038010 + (int)m_AccuracyLevel ) ); } else if( m_Slayer != SlayerName.None || m_Slayer2 != SlayerName.None || m_DurabilityLevel != WeaponDurabilityLevel.Regular || m_DamageLevel != WeaponDamageLevel.Regular || m_AccuracyLevel != WeaponAccuracyLevel.Regular ) attrs.Add( new EquipInfoAttribute( 1038000 ) ); // Unidentified if ( m_Poison != null && m_PoisonCharges > 0 ) attrs.Add( new EquipInfoAttribute( 1017383, m_PoisonCharges ) ); int number; if ( Name == null ) { number = LabelNumber; } else { this.LabelTo( from, Name ); number = 1041000; } if ( attrs.Count == 0 && Crafter == null && Name != null ) return; EquipmentInfo eqInfo = new EquipmentInfo( number, m_Crafter, false, attrs.ToArray() ); from.Send( new DisplayEquipmentInfo( this, eqInfo ) ); } private static BaseWeapon m_Fists; // This value holds the default--fist--weapon public static BaseWeapon Fists { get{ return m_Fists; } set{ m_Fists = value; } } #region ICraftable Members public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) { Quality = (WeaponQuality)quality; if ( makersMark ) Crafter = from; PlayerConstructed = true; Type resourceType = typeRes; if ( resourceType == null ) resourceType = craftItem.Ressources.GetAt( 0 ).ItemType; if ( Core.AOS ) { Resource = CraftResources.GetFromType( resourceType ); CraftContext context = craftSystem.GetContext( from ); if ( context != null && context.DoNotColor ) Hue = 0; if ( tool is BaseRunicTool ) ((BaseRunicTool)tool).ApplyAttributesTo( this ); if ( quality == 2 ) { if ( Attributes.WeaponDamage > 35 ) Attributes.WeaponDamage -= 20; else Attributes.WeaponDamage = 15; } } else if ( tool is BaseRunicTool ) { CraftResource thisResource = CraftResources.GetFromType( resourceType ); if ( thisResource == ((BaseRunicTool)tool).Resource ) { Resource = thisResource; CraftContext context = craftSystem.GetContext( from ); if ( context != null && context.DoNotColor ) Hue = 0; switch ( thisResource ) { case CraftResource.DullCopper: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Durable; AccuracyLevel = WeaponAccuracyLevel.Accurate; break; } case CraftResource.ShadowIron: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Durable; DamageLevel = WeaponDamageLevel.Ruin; break; } case CraftResource.Copper: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Fortified; DamageLevel = WeaponDamageLevel.Ruin; AccuracyLevel = WeaponAccuracyLevel.Surpassingly; break; } case CraftResource.Bronze: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Fortified; DamageLevel = WeaponDamageLevel.Might; AccuracyLevel = WeaponAccuracyLevel.Surpassingly; break; } case CraftResource.Gold: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Indestructible; DamageLevel = WeaponDamageLevel.Force; AccuracyLevel = WeaponAccuracyLevel.Eminently; break; } case CraftResource.Agapite: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Indestructible; DamageLevel = WeaponDamageLevel.Power; AccuracyLevel = WeaponAccuracyLevel.Eminently; break; } case CraftResource.Verite: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Indestructible; DamageLevel = WeaponDamageLevel.Power; AccuracyLevel = WeaponAccuracyLevel.Exceedingly; break; } case CraftResource.Valorite: { Identified = true; DurabilityLevel = WeaponDurabilityLevel.Indestructible; DamageLevel = WeaponDamageLevel.Vanq; AccuracyLevel = WeaponAccuracyLevel.Supremely; break; } } } } return quality; } #endregion } public enum CheckSlayerResult { None, Slayer, Opposition } } |
07-06-2006, 10:58 AM | #8 (permalink) |
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
are you sure that you extracted all of the files from the xmlsockets installation package?
It looks like you are missing XmlSockets.cs also I wouldnt put this in your deserialization Code:
// 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5 of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // uncomment the following line to prevent artifacts from being socketed // if(ArtifactRarity == 0) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0); I would add it to the constructor (like you did with BaseJewel), so that it is only configured once when you actually create the weapon.
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum |
07-07-2006, 02:21 PM | #9 (permalink) |
Newbie
Join Date: Jul 2005
Age: 44
Posts: 31
|
Base weapon and base jewel
Thank you arte
I don't know what I did but just reloaded the base weapon and jewel script i edited and now it works fine (well it compiled), cross fingers it works in game. Again thanks for your help and realy ace system |
07-20-2006, 10:51 AM | #10 (permalink) |
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
are you sure this is the script that is actually being compiled? What you posted does not have any errors (it is identical to what I am currently using).
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum |
10-20-2006, 06:27 AM | #12 (permalink) |
Newbie
|
a lil help
i get all kinds of errors when i start my server i cant copy the errors cause i dont know how 2 do that on my server page the errors are in base creacher .sc here is how i have base creacher set up
using Server.Engines.XmlSpawner2; using System; using System.Collections; using System.Collections.Generic; using Server; using Server.Regions; using Server.Targeting; using Server.Network; using Server.Spells; using Server.Misc; using Server.Items; using Server.Mobiles; using Server.ContextMenus; using Server.Engines.Quests; using Server.Factions; using Server.Spells.Bushido; namespace Server.Mobiles { #region Enums /// <summary> /// Summary description for MobileAI. /// </summary> /// public enum FightMode { None, // Never focus on others Aggressor, // Only attack aggressors Strongest, // Attack the strongest Weakest, // Attack the weakest Closest, // Attack the closest Evil // Only attack aggressor -or- negative karma } public enum OrderType { None, //When no order, let's roam Come, //"(All/Name) come" Summons all or one pet to your location. Drop, //"(Name) drop" Drops its loot to the ground (if it carries any). Follow, //"(Name) follow" Follows targeted being. //"(All/Name) follow me" Makes all or one pet follow you. Friend, //"(Name) friend" Allows targeted player to confirm resurrection. Unfriend, // Remove a friend Guard, //"(Name) guard" Makes the specified pet guard you. Pets can only guard their owner. //"(All/Name) guard me" Makes all or one pet guard you. Attack, //"(All/Name) kill", //"(All/Name) attack" All or the specified pet(s) currently under your control attack the target. Patrol, //"(Name) patrol" Roves between two or more guarded targets. Release, //"(Name) release" Releases pet back into the wild (removes "tame" status). Stay, //"(All/Name) stay" All or the specified pet(s) will stop and stay in current spot. Stop, //"(All/Name) stop Cancels any current orders to attack, guard or follow. Transfer //"(Name) transfer" Transfers complete ownership to targeted player. } [Flags] public enum FoodType { Meat = 0x0001, FruitsAndVegies = 0x0002, GrainsAndHay = 0x0004, Fish = 0x0008, Eggs = 0x0010, Gold = 0x0020 } [Flags] public enum PackInstinct { None = 0x0000, Canine = 0x0001, Ostard = 0x0002, Feline = 0x0004, Arachnid = 0x0008, Daemon = 0x0010, Bear = 0x0020, Equine = 0x0040, Bull = 0x0080 } public enum ScaleType { Red, Yellow, Black, Green, White, Blue, All } public enum MeatType { Ribs, Bird, LambLeg } public enum HideType { Regular, Spined, Horned, Barbed } #endregion public class DamageStore : IComparable { public Mobile m_Mobile; public int m_Damage; public bool m_HasRight; public DamageStore( Mobile m, int damage ) { m_Mobile = m; m_Damage = damage; } public int CompareTo( object obj ) { DamageStore ds = (DamageStore)obj; return ds.m_Damage - m_Damage; } } [AttributeUsage( AttributeTargets.Class )] public class FriendlyNameAttribute : Attribute { //future use: Talisman 'Protection/Bonus vs. Specific Creature private TextDefinition m_FriendlyName; public TextDefinition FriendlyName { get { return m_FriendlyName; } } public FriendlyNameAttribute( TextDefinition friendlyName ) { m_FriendlyName = friendlyName; } public static TextDefinition GetFriendlyNameFor( Type t ) { if( t.IsDefined( typeof( FriendlyNameAttribute ), false ) ) { object[] objs = t.GetCustomAttributes( typeof( FriendlyNameAttribute ), false ); if( objs != null && objs.Length > 0 ) { FriendlyNameAttribute friendly = objs[0] as FriendlyNameAttribute; return friendly.FriendlyName; } } return t.Name; } } public class BaseCreature : Mobile, IHonorTarget { public const int MaxLoyalty = 100; #region Var declarations private BaseAI m_AI; // THE AI private AIType m_CurrentAI; // The current AI private AIType m_DefaultAI; // The default AI private Mobile m_FocusMob; // Use focus mob instead of combatant, maybe we don't whan to fight private FightMode m_FightMode; // The style the mob uses private int m_iRangePerception; // The view area private int m_iRangeFight; // The fight distance private bool m_bDebugAI; // Show debug AI messages private int m_iTeam; // Monster Team private double m_dActiveSpeed; // Timer speed when active private double m_dPassiveSpeed; // Timer speed when not active private double m_dCurrentSpeed; // The current speed, lets say it could be changed by something; private Point3D m_pHome; // The home position of the creature, used by some AI private int m_iRangeHome = 10; // The home range of the creature List<Type> m_arSpellAttack; // List of attack spell/power List<Type> m_arSpellDefense; // List of defensive spell/power private bool m_bControlled; // Is controlled private Mobile m_ControlMaster; // My master private Mobile m_ControlTarget; // My target mobile private Point3D m_ControlDest; // My target destination (patrol) private OrderType m_ControlOrder; // My order private int m_Loyalty; private double m_dMinTameSkill; private bool m_bTamable; private bool m_bSummoned = false; private DateTime m_SummonEnd; private int m_iControlSlots = 1; private bool m_bBardProvoked = false; private bool m_bBardPacified = false; private Mobile m_bBardMaster = null; private Mobile m_bBardTarget = null; private DateTime m_timeBardEnd; private WayPoint m_CurrentWayPoint = null; private Point2D m_TargetLocation = Point2D.Zero; private Mobile m_SummonMaster; private int m_HitsMax = -1; private int m_StamMax = -1; private int m_ManaMax = -1; private int m_DamageMin = -1; private int m_DamageMax = -1; private int m_PhysicalResistance, m_PhysicalDamage = 100; private int m_FireResistance, m_FireDamage; private int m_ColdResistance, m_ColdDamage; private int m_PoisonResistance, m_PoisonDamage; private int m_EnergyResistance, m_EnergyDamage; private List<Mobile> m_Owners; private List<Mobile> m_Friends; private bool m_IsStabled; private bool m_HasGeneratedLoot; // have we generated our loot yet? private bool m_Paragon; #endregion public virtual InhumanSpeech SpeechType{ get{ return null; } } public bool IsStabled { get{ return m_IsStabled; } set{ m_IsStabled = value; } } protected DateTime SummonEnd { get { return m_SummonEnd; } set { m_SummonEnd = value; } } public virtual Faction FactionAllegiance{ get{ return null; } } public virtual int FactionSilverWorth{ get{ return 30; } } #region Bonding public const bool BondingEnabled = true; public virtual bool IsBondable{ get{ return ( BondingEnabled && !Summoned ); } } public virtual TimeSpan BondingDelay{ get{ return TimeSpan.FromDays( 7.0 ); } } public virtual TimeSpan BondingAbandonDelay{ get{ return TimeSpan.FromDays( 1.0 ); } } public override bool CanRegenHits{ get{ return !m_IsDeadPet && base.CanRegenHits; } } public override bool CanRegenStam{ get{ return !m_IsDeadPet && base.CanRegenStam; } } public override bool CanRegenMana{ get{ return !m_IsDeadPet && base.CanRegenMana; } } public override bool IsDeadBondedPet{ get{ return m_IsDeadPet; } } private bool m_IsBonded; private bool m_IsDeadPet; private DateTime m_BondingBegin; private DateTime m_OwnerAbandonTime; [CommandProperty( AccessLevel.GameMaster )] public Mobile LastOwner { get { if ( m_Owners == null || m_Owners.Count == 0 ) return null; return m_Owners[m_Owners.Count - 1]; } } [CommandProperty( AccessLevel.GameMaster )] public bool IsBonded { get{ return m_IsBonded; } set{ m_IsBonded = value; InvalidateProperties(); } } public bool IsDeadPet { get{ return m_IsDeadPet; } set{ m_IsDeadPet = value; } } [CommandProperty( AccessLevel.GameMaster )] public DateTime BondingBegin { get{ return m_BondingBegin; } set{ m_BondingBegin = value; } } [CommandProperty( AccessLevel.GameMaster )] public DateTime OwnerAbandonTime { get{ return m_OwnerAbandonTime; } set{ m_OwnerAbandonTime = value; } } #endregion public virtual double WeaponAbilityChance{ get{ return 0.4; } } public virtual WeaponAbility GetWeaponAbility() { return null; } #region Elemental Resistance/Damage public override int BasePhysicalResistance{ get{ return m_PhysicalResistance; } } public override int BaseFireResistance{ get{ return m_FireResistance; } } public override int BaseColdResistance{ get{ return m_ColdResistance; } } public override int BasePoisonResistance{ get{ return m_PoisonResistance; } } public override int BaseEnergyResistance{ get{ return m_EnergyResistance; } } [CommandProperty( AccessLevel.GameMaster )] public int PhysicalResistanceSeed{ get{ return m_PhysicalResistance; } set{ m_PhysicalResistance = value; UpdateResistances(); } } [CommandProperty( AccessLevel.GameMaster )] public int FireResistSeed{ get{ return m_FireResistance; } set{ m_FireResistance = value; UpdateResistances(); } } [CommandProperty( AccessLevel.GameMaster )] public int ColdResistSeed{ get{ return m_ColdResistance; } set{ m_ColdResistance = value; UpdateResistances(); } } [CommandProperty( AccessLevel.GameMaster )] public int PoisonResistSeed{ get{ return m_PoisonResistance; } set{ m_PoisonResistance = value; UpdateResistances(); } } [CommandProperty( AccessLevel.GameMaster )] public int EnergyResistSeed{ get{ return m_EnergyResistance; } set{ m_EnergyResistance = value; UpdateResistances(); } } [CommandProperty( AccessLevel.GameMaster )] public int PhysicalDamage{ get{ return m_PhysicalDamage; } set{ m_PhysicalDamage = value; } } [CommandProperty( AccessLevel.GameMaster )] public int FireDamage{ get{ return m_FireDamage; } set{ m_FireDamage = value; } } [CommandProperty( AccessLevel.GameMaster )] public int ColdDamage{ get{ return m_ColdDamage; } set{ m_ColdDamage = value; } } [CommandProperty( AccessLevel.GameMaster )] public int PoisonDamage{ get{ return m_PoisonDamage; } set{ m_PoisonDamage = value; } } [CommandProperty( AccessLevel.GameMaster )] public int EnergyDamage{ get{ return m_EnergyDamage; } set{ m_EnergyDamage = value; } } #endregion [CommandProperty( AccessLevel.GameMaster )] public bool IsParagon { get{ return m_Paragon; } set { if ( m_Paragon == value ) return; else if ( value ) Paragon.Convert( this ); else Paragon.UnConvert( this ); m_Paragon = value; InvalidateProperties(); } } public virtual FoodType FavoriteFood{ get{ return FoodType.Meat; } } public virtual PackInstinct PackInstinct{ get{ return PackInstinct.None; } } public List<Mobile> Owners { get { return m_Owners; } } public virtual bool AllowMaleTamer{ get{ return true; } } public virtual bool AllowFemaleTamer{ get{ return true; } } public virtual bool SubdueBeforeTame{ get{ return false; } } public virtual bool StatLossAfterTame{ get{ return SubdueBeforeTame; } } public virtual bool Commandable{ get{ return true; } } public virtual Poison HitPoison{ get{ return null; } } public virtual double HitPoisonChance{ get{ return 0.5; } } public virtual Poison PoisonImmune{ get{ return null; } } public virtual bool BardImmune{ get{ return false; } } public virtual bool Unprovokable{ get{ return BardImmune || m_IsDeadPet; } } public virtual bool Uncalmable{ get{ return BardImmune || m_IsDeadPet; } } public virtual bool BleedImmune{ get{ return false; } } public virtual double BonusPetDamageScalar{ get{ return 1.0; } } public virtual bool DeathAdderCharmable{ get{ return false; } } //TODO: Find the pub 31 tweaks to the DispelDifficulty and apply them of course. public virtual double DispelDifficulty{ get{ return 0.0; } } // at this skill level we dispel 50% chance public virtual double DispelFocus{ get{ return 20.0; } } // at difficulty - focus we have 0%, at difficulty + focus we have 100% #region Breath ability, like dragon fire breath private DateTime m_NextBreathTime; // Must be overriden in subclass to enable public virtual bool HasBreath{ get{ return false; } } // Base damage given is: CurrentHitPoints * BreathDamageScalar public virtual double BreathDamageScalar{ get{ return (Core.AOS ? 0.16 : 0.05); } } // Min/max seconds until next breath public virtual double BreathMinDelay{ get{ return 10.0; } } public virtual double BreathMaxDelay{ get{ return 15.0; } } // Creature stops moving for 1.0 seconds while breathing public virtual double BreathStallTime{ get{ return 1.0; } } // Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played public virtual double BreathEffectDelay{ get{ return 1.3; } } // Damage is given 1.0 seconds after effect is sent public virtual double BreathDamageDelay{ get{ return 1.0; } } public virtual int BreathRange{ get{ return RangePerception; } } // Damage types public virtual int BreathPhysicalDamage{ get{ return 0; } } public virtual int BreathFireDamage{ get{ return 100; } } public virtual int BreathColdDamage{ get{ return 0; } } public virtual int BreathPoisonDamage{ get{ return 0; } } public virtual int BreathEnergyDamage{ get{ return 0; } } // Effect details and sound public virtual int BreathEffectItemID{ get{ return 0x36D4; } } public virtual int BreathEffectSpeed{ get{ return 5; } } public virtual int BreathEffectDuration{ get{ return 0; } } public virtual bool BreathEffectExplodes{ get{ return false; } } public virtual bool BreathEffectFixedDir{ get{ return false; } } public virtual int BreathEffectHue{ get{ return 0; } } public virtual int BreathEffectRenderMode{ get{ return 0; } } public virtual int BreathEffectSound{ get{ return 0x227; } } // Anger sound/animations public virtual int BreathAngerSound{ get{ return GetAngerSound(); } } public virtual int BreathAngerAnimation{ get{ return 12; } } public virtual void BreathStart( Mobile target ) { BreathStallMovement(); BreathPlayAngerSound(); BreathPlayAngerAnimation(); this.Direction = this.GetDirectionTo( target ); Timer.DelayCall( TimeSpan.FromSeconds( BreathEffectDelay ), new TimerStateCallback( BreathEffect_Callback ), target ); } public virtual void BreathStallMovement() { if ( m_AI != null ) m_AI.NextMove = DateTime.Now + TimeSpan.FromSeconds( BreathStallTime ); } public virtual void BreathPlayAngerSound() { PlaySound( BreathAngerSound ); } public virtual void BreathPlayAngerAnimation() { Animate( BreathAngerAnimation, 5, 1, true, false, 0 ); } public virtual void BreathEffect_Callback( object state ) { Mobile target = (Mobile)state; if ( !target.Alive || !CanBeHarmful( target ) ) return; BreathPlayEffectSound(); BreathPlayEffect( target ); Timer.DelayCall( TimeSpan.FromSeconds( BreathDamageDelay ), new TimerStateCallback( BreathDamage_Callback ), target ); } public virtual void BreathPlayEffectSound() { PlaySound( BreathEffectSound ); } public virtual void BreathPlayEffect( Mobile target ) { Effects.SendMovingEffect( this, target, BreathEffectItemID, BreathEffectSpeed, BreathEffectDuration, BreathEffectFixedDir, BreathEffectExplodes, BreathEffectHue, BreathEffectRenderMode ); } public virtual void BreathDamage_Callback( object state ) { Mobile target = (Mobile)state; if ( CanBeHarmful( target ) ) { DoHarmful( target ); BreathDealDamage( target ); } } public virtual void BreathDealDamage( Mobile target ) { int physDamage = BreathPhysicalDamage; int fireDamage = BreathFireDamage; int coldDamage = BreathColdDamage; int poisDamage = BreathPoisonDamage; int nrgyDamage = BreathEnergyDamage; if ( physDamage == 0 && fireDamage == 0 && coldDamage == 0 && poisDamage == 0 && nrgyDamage == 0 ) { // Unresistable damage even in AOS target.Damage( BreathComputeDamage(), this ); } else { AOS.Damage( target, this, BreathComputeDamage(), physDamage, fireDamage, coldDamage, poisDamage, nrgyDamage ); } } public virtual int BreathComputeDamage() { int damage = (int)(Hits * BreathDamageScalar); if ( IsParagon ) damage = (int)(damage / Paragon.HitsBuff); return damage; } #endregion #region Spill Acid public void SpillAcid( TimeSpan duration, int minDamage, int maxDamage ) { SpillAcid( duration, minDamage, maxDamage, null, 1, 1 ); } public void SpillAcid( TimeSpan duration, int minDamage, int maxDamage, Mobile target ) { SpillAcid( duration, minDamage, maxDamage, target, 1, 1 ); } public void SpillAcid( TimeSpan duration, int minDamage, int maxDamage, int count ) { SpillAcid( duration, minDamage, maxDamage, null, count, count ); } public void SpillAcid( TimeSpan duration, int minDamage, int maxDamage, int minAmount, int maxAmount ) { SpillAcid( duration, minDamage, maxDamage, null, minAmount, maxAmount ); } public void SpillAcid( TimeSpan duration, int minDamage, int maxDamage, Mobile target, int count ) { SpillAcid( duration, minDamage, maxDamage, target, count, count ); } public void SpillAcid( TimeSpan duration, int minDamage, int maxDamage, Mobile target, int minAmount, int maxAmount ) { if ( (target != null && target.Map == null) || this.Map == null ) return; int pools = Utility.RandomMinMax( minAmount, maxAmount ); for ( int i = 0; i < pools; ++i ) { PoolOfAcid acid = new PoolOfAcid( duration, minDamage, maxDamage ); if ( target != null && target.Map != null ) { acid.MoveToWorld( target.Location, target.Map ); continue; } bool validLocation = false; Point3D loc = this.Location; Map map = this.Map; for ( int j = 0; !validLocation && j < 10; ++j ) { int x = X + Utility.Random( 3 ) - 1; int y = Y + Utility.Random( 3 ) - 1; int z = map.GetAverageZ( x, y ); if ( validLocation = map.CanFit( x, y, this.Z, 16, false, false ) ) loc = new Point3D( x, y, Z ); else if ( validLocation = map.CanFit( x, y, z, 16, false, false ) ) loc = new Point3D( x, y, z ); } acid.MoveToWorld( loc, map ); } } #endregion #region Flee!!! private DateTime m_EndFlee; public DateTime EndFleeTime { get{ return m_EndFlee; } set{ m_EndFlee = value; } } public virtual void StopFlee() { m_EndFlee = DateTime.MinValue; } public virtual bool CheckFlee() { if ( m_EndFlee == DateTime.MinValue ) return false; if ( DateTime.Now >= m_EndFlee ) { StopFlee(); return false; } return true; } public virtual void BeginFlee( TimeSpan maxDuration ) { m_EndFlee = DateTime.Now + maxDuration; } #endregion public BaseAI AIObject{ get{ return m_AI; } } public const int MaxOwners = 5; public virtual OppositionGroup OppositionGroup { get{ return null; } } #region Friends public List<Mobile> Friends { get { return m_Friends; } } public virtual bool AllowNewPetFriend { get{ return ( m_Friends == null || m_Friends.Count < 5 ); } } public virtual bool IsPetFriend( Mobile m ) { return ( m_Friends != null && m_Friends.Contains( m ) ); } public virtual void AddPetFriend( Mobile m ) { if ( m_Friends == null ) m_Friends = new List<Mobile>( 8 ); m_Friends.Add( m ); } public virtual void RemovePetFriend( Mobile m ) { if ( m_Friends != null ) m_Friends.Remove( m ); } public virtual bool IsFriend( Mobile m ) { OppositionGroup g = this.OppositionGroup; if ( g != null && g.IsEnemy( this, m ) ) return false; if ( !(m is BaseCreature) ) return false; BaseCreature c = (BaseCreature)m; return ( m_iTeam == c.m_iTeam && ( (m_bSummoned || m_bControlled) == (c.m_bSummoned || c.m_bControlled) )/* && c.Combatant != this */); } #endregion #region Allegiance public virtual Ethics.Ethic EthicAllegiance { get { return null; } } public enum Allegiance { None, Ally, Enemy } public virtual Allegiance GetFactionAllegiance( Mobile mob ) { if ( mob == null || mob.Map != Faction.Facet || FactionAllegiance == null ) return Allegiance.None; Faction fac = Faction.Find( mob, true ); if ( fac == null ) return Allegiance.None; return ( fac == FactionAllegiance ? Allegiance.Ally : Allegiance.Enemy ); } public virtual Allegiance GetEthicAllegiance( Mobile mob ) { if ( mob == null || mob.Map != Faction.Facet || EthicAllegiance == null ) return Allegiance.None; Ethics.Ethic ethic = Ethics.Ethic.Find( mob, true ); if ( ethic == null ) return Allegiance.None; return ( ethic == EthicAllegiance ? Allegiance.Ally : Allegiance.Enemy ); } #endregion public virtual bool IsEnemy( Mobile m ) { OppositionGroup g = this.OppositionGroup; if ( g != null && g.IsEnemy( this, m ) ) return true; if ( m is BaseGuard ) return false; if ( GetFactionAllegiance( m ) == Allegiance.Ally ) return false; Ethics.Ethic ourEthic = EthicAllegiance; Ethics.Player pl = Ethics.Player.Find( m, true ); if ( pl != null && pl.IsShielded && ( ourEthic == null || ourEthic == pl.Ethic ) ) return false; if ( !(m is BaseCreature) || m is Server.Engines.Quests.Haven.MilitiaFighter ) return true; BaseCreature c = (BaseCreature)m; return ( m_iTeam != c.m_iTeam || ( (m_bSummoned || m_bControlled) != (c.m_bSummoned || c.m_bControlled) )/* || c.Combatant == this*/ ); } public override string ApplyNameSuffix( string suffix ) { if ( IsParagon ) { if ( suffix.Length == 0 ) suffix = "(Paragon)"; else suffix = String.Concat( suffix, " (Paragon)" ); } return base.ApplyNameSuffix( suffix ); } public virtual bool CheckControlChance( Mobile m ) { if ( GetControlChance( m ) > Utility.RandomDouble() ) { Loyalty += 1; return true; } PlaySound( GetAngerSound() ); if ( Body.IsAnimal ) Animate( 10, 5, 1, true, false, 0 ); else if ( Body.IsMonster ) Animate( 18, 5, 1, true, false, 0 ); Loyalty -= 3; return false; } public virtual bool CanBeControlledBy( Mobile m ) { return ( GetControlChance( m ) > 0.0 ); } public virtual double GetControlChance( Mobile m ) { if ( m_dMinTameSkill <= 29.1 || m_bSummoned || m.AccessLevel >= AccessLevel.GameMaster ) return 1.0; double dMinTameSkill = m_dMinTameSkill; if ( dMinTameSkill > -24.9 && Server.SkillHandlers.AnimalTaming.CheckMastery( m, this ) ) dMinTameSkill = -24.9; int taming = (int)(m.Skills[SkillName.AnimalTaming].Value * 10); int lore = (int)(m.Skills[SkillName.AnimalLore].Value * 10); int difficulty = (int)(dMinTameSkill * 10); int weighted = ((taming * 4) + lore) / 5; int bonus = weighted - difficulty; int chance; if ( bonus <= 0 ) chance = 700 + (bonus * 14); else chance = 700 + (bonus * 6); if ( chance >= 0 && chance < 200 ) chance = 200; else if ( chance > 990 ) chance = 990; chance -= (MaxLoyalty - m_Loyalty) * 10; return ( (double)chance / 1000 ); } private static Type[] m_AnimateDeadTypes = new Type[] { typeof( MoundOfMaggots ), typeof( HellSteed ), typeof( SkeletalMount ), typeof( WailingBanshee ), typeof( Wraith ), typeof( SkeletalDragon ), typeof( LichLord ), typeof( FleshGolem ), typeof( Lich ), typeof( SkeletalKnight ), typeof( BoneKnight ), typeof( Mummy ), typeof( SkeletalMage ), typeof( BoneMagi ), typeof( PatchworkSkeleton ) }; public virtual bool IsAnimatedDead { get { if ( !Summoned ) return false; Type type = this.GetType(); bool contains = false; for ( int i = 0; !contains && i < m_AnimateDeadTypes.Length; ++i ) contains = ( type == m_AnimateDeadTypes[i] ); return contains; } } public override void Damage( int amount, Mobile from ) { int oldHits = this.Hits; if ( !this.Summoned && this.Controlled && 0.2 > Utility.RandomDouble() ) amount = (int)(amount * BonusPetDamageScalar); if ( Spells.Necromancy.EvilOmenSpell.CheckEffect( this ) ) amount = (int)(amount * 1.25); Mobile oath = Spells.Necromancy.BloodOathSpell.GetBloodOath( from ); if ( oath == this ) { amount = (int)(amount * 1.1); from.Damage( amount, from ); } base.Damage( amount, from ); if ( SubdueBeforeTame && !Controlled ) { if ( (oldHits > (this.HitsMax / 10)) && (this.Hits <= (this.HitsMax / 10)) ) PublicOverheadMessage( MessageType.Regular, 0x3B2, false, "* The creature has been beaten into subjugation! *" ); } } public virtual bool DeleteCorpseOnDeath { get { return !Core.AOS && m_bSummoned; } } public override void SetLocation( Point3D newLocation, bool isTeleport ) { base.SetLocation( newLocation, isTeleport ); if ( isTeleport && m_AI != null ) m_AI.OnTeleported(); } public override void OnBeforeSpawn( Point3D location, Map m ) { if ( Paragon.CheckConvert( this, location, m ) ) IsParagon = true; // mod to randomly add socket features to tamable creatures. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // Adding new sockets will require a minimum of 100 Veterinary skill and 30 emeralds if(Tamable) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0, SkillName.Veterinary, 100.0, typeof(Emerald), 30); base.OnBeforeSpawn( location, m ); } base.OnBeforeSpawn( location, m ); } public override ApplyPoisonResult ApplyPoison( Mobile from, Poison poison ) { if ( !Alive || IsDeadPet ) return ApplyPoisonResult.Immune; if ( Spells.Necromancy.EvilOmenSpell.CheckEffect( this ) ) poison = PoisonImpl.IncreaseLevel( poison ); ApplyPoisonResult result = base.ApplyPoison( from, poison ); if ( from != null && result == ApplyPoisonResult.Poisoned && PoisonTimer is PoisonImpl.PoisonTimer ) (PoisonTimer as PoisonImpl.PoisonTimer).From = from; return result; } public override bool CheckPoisonImmunity( Mobile from, Poison poison ) { if ( base.CheckPoisonImmunity( from, poison ) ) return true; Poison p = this.PoisonImmune; return ( p != null && p.Level >= poison.Level ); } [CommandProperty( AccessLevel.GameMaster )] public int Loyalty { get { return m_Loyalty; } set { m_Loyalty = Math.Min( Math.Max( value, 0 ), MaxLoyalty ); } } [CommandProperty( AccessLevel.GameMaster )] public WayPoint CurrentWayPoint { get { return m_CurrentWayPoint; } set { m_CurrentWayPoint = value; } } [CommandProperty( AccessLevel.GameMaster )] public Point2D TargetLocation { get { return m_TargetLocation; } set { m_TargetLocation = value; } } public virtual Mobile ConstantFocus{ get{ return null; } } public virtual bool DisallowAllMoves { get { return false; } } public virtual bool InitialInnocent { get { return false; } } public virtual bool AlwaysMurderer { get { return false; } } public virtual bool AlwaysAttackable { get { return false; } } [CommandProperty( AccessLevel.GameMaster )] public virtual int DamageMin{ get{ return m_DamageMin; } set{ m_DamageMin = value; } } [CommandProperty( AccessLevel.GameMaster )] public virtual int DamageMax{ get{ return m_DamageMax; } set{ m_DamageMax = value; } } [CommandProperty( AccessLevel.GameMaster )] public override int HitsMax { get { if ( m_HitsMax >= 0 ) return m_HitsMax; return Str; } } [CommandProperty( AccessLevel.GameMaster )] public int HitsMaxSeed { get{ return m_HitsMax; } set{ m_HitsMax = value; } } [CommandProperty( AccessLevel.GameMaster )] public override int StamMax { get { if ( m_StamMax >= 0 ) return m_StamMax; return Dex; } } [CommandProperty( AccessLevel.GameMaster )] public int StamMaxSeed { get{ return m_StamMax; } set{ m_StamMax = value; } } [CommandProperty( AccessLevel.GameMaster )] public override int ManaMax { get { if ( m_ManaMax >= 0 ) return m_ManaMax; return Int; } } [CommandProperty( AccessLevel.GameMaster )] public int ManaMaxSeed { get{ return m_ManaMax; } set{ m_ManaMax = value; } } public virtual bool CanOpenDoors { get { return !this.Body.IsAnimal && !this.Body.IsSea; } } public virtual bool CanMoveOverObstacles { get { return Core.AOS || this.Body.IsMonster; } } public virtual bool CanDestroyObstacles { get { // to enable breaking of furniture, 'return CanMoveOverObstacles;' return false; } } public void Unpacify() { BardEndTime = DateTime.Now; BardPacified = false; } private HonorContext m_ReceivedHonorContext; public HonorContext ReceivedHonorContext{ get{ return m_ReceivedHonorContext; } set{ m_ReceivedHonorContext = value; } } public override void OnDamage( int amount, Mobile from, bool willKill ) { if ( BardPacified && (HitsMax - Hits) * 0.001 > Utility.RandomDouble() ) Unpacify(); int disruptThreshold; //NPCs can use bandages too! if( !Core.AOS ) disruptThreshold = 0; else if( from != null && from.Player ) disruptThreshold = 18; else disruptThreshold = 25; if( amount > disruptThreshold ) { BandageContext c = BandageContext.GetContext( this ); if( c != null ) c.Slip(); } if( Confidence.IsRegenerating( this ) ) Confidence.StopRegenerating( this ); WeightOverloading.FatigueOnDamage( this, amount ); InhumanSpeech speechType = this.SpeechType; if ( speechType != null && !willKill ) speechType.OnDamage( this, amount ); if ( m_ReceivedHonorContext != null ) m_ReceivedHonorContext.OnTargetDamaged( from, amount ); base.OnDamage( amount, from, willKill ); } public virtual void OnDamagedBySpell( Mobile from ) { } #region Alter[...]Damage From/To public virtual void AlterDamageScalarFrom( Mobile caster, ref double scalar ) { } public virtual void AlterDamageScalarTo( Mobile target, ref double scalar ) { } public virtual void AlterSpellDamageFrom( Mobile from, ref int damage ) { } public virtual void AlterSpellDamageTo( Mobile to, ref int damage ) { } public virtual void AlterMeleeDamageFrom( Mobile from, ref int damage ) { } public virtual void AlterMeleeDamageTo( Mobile to, ref int damage ) { } #endregion public virtual void CheckReflect( Mobile caster, ref bool reflect ) { } public virtual void OnCarve( Mobile from, Corpse corpse ) { int feathers = Feathers; int wool = Wool; int meat = Meat; int hides = Hides; int scales = Scales; if ( (feathers == 0 && wool == 0 && meat == 0 && hides == 0 && scales == 0) || Summoned || IsBonded ) { from.SendLocalizedMessage( 500485 ); // You see nothing useful to carve from the corpse. } else { if ( corpse.Map == Map.Felucca ) { feathers *= 2; wool *= 2; hides *= 2; } new Blood( 0x122D ).MoveToWorld( corpse.Location, corpse.Map ); if ( feathers != 0 ) { corpse.DropItem( new Feather( feathers ) ); from.SendLocalizedMessage( 500479 ); // You pluck the bird. The feathers are now on the corpse. } if ( wool != 0 ) { corpse.DropItem( new Wool( wool ) ); from.SendLocalizedMessage( 500483 ); // You shear it, and the wool is now on the corpse. } if ( meat != 0 ) { if ( MeatType == MeatType.Ribs ) corpse.DropItem( new RawRibs( meat ) ); else if ( MeatType == MeatType.Bird ) corpse.DropItem( new RawBird( meat ) ); else if ( MeatType == MeatType.LambLeg ) corpse.DropItem( new RawLambLeg( meat ) ); from.SendLocalizedMessage( 500467 ); // You carve some meat, which remains on the corpse. } if ( hides != 0 ) { if ( HideType == HideType.Regular ) corpse.DropItem( new Hides( hides ) ); else if ( HideType == HideType.Spined ) corpse.DropItem( new SpinedHides( hides ) ); else if ( HideType == HideType.Horned ) corpse.DropItem( new HornedHides( hides ) ); else if ( HideType == HideType.Barbed ) corpse.DropItem( new BarbedHides( hides ) ); from.SendLocalizedMessage( 500471 ); // You skin it, and the hides are now in the corpse. } if ( scales != 0 ) { ScaleType sc = this.ScaleType; switch ( sc ) { case ScaleType.Red: corpse.DropItem( new RedScales( scales ) ); break; case ScaleType.Yellow: corpse.DropItem( new YellowScales( scales ) ); break; case ScaleType.Black: corpse.DropItem( new BlackScales( scales ) ); break; case ScaleType.Green: corpse.DropItem( new GreenScales( scales ) ); break; case ScaleType.White: corpse.DropItem( new WhiteScales( scales ) ); break; case ScaleType.Blue: corpse.DropItem( new BlueScales( scales ) ); break; case ScaleType.All: { corpse.DropItem( new RedScales( scales ) ); corpse.DropItem( new YellowScales( scales ) ); corpse.DropItem( new BlackScales( scales ) ); corpse.DropItem( new GreenScales( scales ) ); corpse.DropItem( new WhiteScales( scales ) ); corpse.DropItem( new BlueScales( scales ) ); break; } } from.SendMessage( "You cut away some scales, but they remain on the corpse." ); } corpse.Carved = true; if ( corpse.IsCriminalAction( from ) ) from.CriminalAction( true ); } } public const int DefaultRangePerception = 16; public const int OldRangePerception = 10; public BaseCreature(AIType ai, FightMode mode, int iRangePerception, int iRangeFight, double dActiveSpeed, double dPassiveSpeed) { if ( iRangePerception == OldRangePerception ) iRangePerception = DefaultRangePerception; m_Loyalty = MaxLoyalty; // Wonderfully Happy m_CurrentAI = ai; m_DefaultAI = ai; m_iRangePerception = iRangePerception; m_iRangeFight = iRangeFight; m_FightMode = mode; m_iTeam = 0; SpeedInfo.GetSpeeds( this, ref dActiveSpeed, ref dPassiveSpeed ); m_dActiveSpeed = dActiveSpeed; m_dPassiveSpeed = dPassiveSpeed; m_dCurrentSpeed = dPassiveSpeed; m_bDebugAI = false; m_arSpellAttack = new List<Type>(); m_arSpellDefense = new List<Type>(); m_bControlled = false; m_ControlMaster = null; m_ControlTarget = null; m_ControlOrder = OrderType.None; m_bTamable = false; m_Owners = new List<Mobile>(); m_NextReacquireTime = DateTime.Now + ReacquireDelay; ChangeAIType(AI); InhumanSpeech speechType = this.SpeechType; if ( speechType != null ) speechType.OnConstruct( this ); GenerateLoot( true ); } public BaseCreature( Serial serial ) : base( serial ) { m_arSpellAttack = new List<Type>(); m_arSpellDefense = new List<Type>(); m_bDebugAI = false; } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); writer.Write( (int) 16 ); // version writer.Write( (int)m_CurrentAI ); writer.Write( (int)m_DefaultAI ); writer.Write( (int)m_iRangePerception ); writer.Write( (int)m_iRangeFight ); writer.Write( (int)m_iTeam ); writer.Write( (double)m_dActiveSpeed ); writer.Write( (double)m_dPassiveSpeed ); writer.Write( (double)m_dCurrentSpeed ); writer.Write( (int) m_pHome.X ); writer.Write( (int) m_pHome.Y ); writer.Write( (int) m_pHome.Z ); // Version 1 writer.Write( (int) m_iRangeHome ); int i=0; writer.Write( (int) m_arSpellAttack.Count ); for ( i=0; i< m_arSpellAttack.Count; i++ ) { writer.Write( m_arSpellAttack[i].ToString() ); } writer.Write( (int) m_arSpellDefense.Count ); for ( i=0; i< m_arSpellDefense.Count; i++ ) { writer.Write( m_arSpellDefense[i].ToString() ); } // Version 2 writer.Write( (int) m_FightMode ); writer.Write( (bool) m_bControlled ); writer.Write( (Mobile) m_ControlMaster ); writer.Write( (Mobile) m_ControlTarget ); writer.Write( (Point3D) m_ControlDest ); writer.Write( (int) m_ControlOrder ); writer.Write( (double) m_dMinTameSkill ); // Removed in version 9 //writer.Write( (double) m_dMaxTameSkill ); writer.Write( (bool) m_bTamable ); writer.Write( (bool) m_bSummoned ); if ( m_bSummoned ) writer.WriteDeltaTime( m_SummonEnd ); writer.Write( (int) m_iControlSlots ); // Version 3 writer.Write( (int)m_Loyalty ); // Version 4 writer.Write( m_CurrentWayPoint ); // Verison 5 writer.Write( m_SummonMaster ); // Version 6 writer.Write( (int) m_HitsMax ); writer.Write( (int) m_StamMax ); writer.Write( (int) m_ManaMax ); writer.Write( (int) m_DamageMin ); writer.Write( (int) m_DamageMax ); // Version 7 writer.Write( (int) m_PhysicalResistance ); writer.Write( (int) m_PhysicalDamage ); writer.Write( (int) m_FireResistance ); writer.Write( (int) m_FireDamage ); writer.Write( (int) m_ColdResistance ); writer.Write( (int) m_ColdDamage ); writer.Write( (int) m_PoisonResistance ); writer.Write( (int) m_PoisonDamage ); writer.Write( (int) m_EnergyResistance ); writer.Write( (int) m_EnergyDamage ); // Version 8 writer.Write( m_Owners, true ); // Version 10 writer.Write( (bool) m_IsDeadPet ); writer.Write( (bool) m_IsBonded ); writer.Write( (DateTime) m_BondingBegin ); writer.Write( (DateTime) m_OwnerAbandonTime ); // Version 11 writer.Write( (bool) m_HasGeneratedLoot ); // Version 12 writer.Write( (bool) m_Paragon ); // Version 13 writer.Write( (bool) ( m_Friends != null && m_Friends.Count > 0 ) ); if ( m_Friends != null && m_Friends.Count > 0 ) writer.Write( m_Friends, true ); // Version 14 writer.Write( (bool)m_RemoveIfUntamed ); writer.Write( (int)m_RemoveStep ); } private static double[] m_StandardActiveSpeeds = new double[] { 0.175, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.8 }; private static double[] m_StandardPassiveSpeeds = new double[] { 0.350, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.6, 2.0 }; public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); m_CurrentAI = (AIType)reader.ReadInt(); m_DefaultAI = (AIType)reader.ReadInt(); m_iRangePerception = reader.ReadInt(); m_iRangeFight = reader.ReadInt(); m_iTeam = reader.ReadInt(); m_dActiveSpeed = reader.ReadDouble(); m_dPassiveSpeed = reader.ReadDouble(); m_dCurrentSpeed = reader.ReadDouble(); if ( m_iRangePerception == OldRangePerception ) m_iRangePerception = DefaultRangePerception; m_pHome.X = reader.ReadInt(); m_pHome.Y = reader.ReadInt(); m_pHome.Z = reader.ReadInt(); if ( version >= 1 ) { m_iRangeHome = reader.ReadInt(); int i, iCount; iCount = reader.ReadInt(); for ( i=0; i< iCount; i++ ) { string str = reader.ReadString(); Type type = Type.GetType( str ); if ( type != null ) { m_arSpellAttack.Add( type ); } } iCount = reader.ReadInt(); for ( i=0; i< iCount; i++ ) { string str = reader.ReadString(); Type type = Type.GetType( str ); if ( type != null ) { m_arSpellDefense.Add( type ); } } } else { m_iRangeHome = 0; } if ( version >= 2 ) { m_FightMode = ( FightMode )reader.ReadInt(); m_bControlled = reader.ReadBool(); m_ControlMaster = reader.ReadMobile(); m_ControlTarget = reader.ReadMobile(); m_ControlDest = reader.ReadPoint3D(); m_ControlOrder = (OrderType) reader.ReadInt(); m_dMinTameSkill = reader.ReadDouble(); if ( version < 9 ) reader.ReadDouble(); m_bTamable = reader.ReadBool(); m_bSummoned = reader.ReadBool(); if ( m_bSummoned ) { m_SummonEnd = reader.ReadDeltaTime(); new UnsummonTimer( m_ControlMaster, this, m_SummonEnd - DateTime.Now ).Start(); } m_iControlSlots = reader.ReadInt(); } else { m_FightMode = FightMode.Closest; m_bControlled = false; m_ControlMaster = null; m_ControlTarget = null; m_ControlOrder = OrderType.None; } if ( version >= 3 ) m_Loyalty = reader.ReadInt(); else m_Loyalty = MaxLoyalty; // Wonderfully Happy if ( version >= 4 ) m_CurrentWayPoint = reader.ReadItem() as WayPoint; if ( version >= 5 ) m_SummonMaster = reader.ReadMobile(); if ( version >= 6 ) { m_HitsMax = reader.ReadInt(); m_StamMax = reader.ReadInt(); m_ManaMax = reader.ReadInt(); m_DamageMin = reader.ReadInt(); m_DamageMax = reader.ReadInt(); } if ( version >= 7 ) { m_PhysicalResistance = reader.ReadInt(); m_PhysicalDamage = reader.ReadInt(); m_FireResistance = reader.ReadInt(); m_FireDamage = reader.ReadInt(); m_ColdResistance = reader.ReadInt(); m_ColdDamage = reader.ReadInt(); m_PoisonResistance = reader.ReadInt(); m_PoisonDamage = reader.ReadInt(); m_EnergyResistance = reader.ReadInt(); m_EnergyDamage = reader.ReadInt(); } if ( version >= 8 ) m_Owners = reader.ReadStrongMobileList(); else m_Owners = new List<Mobile>(); if ( version >= 10 ) { m_IsDeadPet = reader.ReadBool(); m_IsBonded = reader.ReadBool(); m_BondingBegin = reader.ReadDateTime(); m_OwnerAbandonTime = reader.ReadDateTime(); } if ( version >= 11 ) m_HasGeneratedLoot = reader.ReadBool(); else m_HasGeneratedLoot = true; if ( version >= 12 ) m_Paragon = reader.ReadBool(); else m_Paragon = false; if ( version >= 13 && reader.ReadBool() ) m_Friends = reader.ReadStrongMobileList(); else if ( version < 13 && m_ControlOrder >= OrderType.Unfriend ) ++m_ControlOrder; if ( version < 16 ) Loyalty *= 10; double activeSpeed = m_dActiveSpeed; double passiveSpeed = m_dPassiveSpeed; SpeedInfo.GetSpeeds( this, ref activeSpeed, ref passiveSpeed ); bool isStandardActive = false; for ( int i = 0; !isStandardActive && i < m_StandardActiveSpeeds.Length; ++i ) isStandardActive = ( m_dActiveSpeed == m_StandardActiveSpeeds[i] ); bool isStandardPassive = false; for ( int i = 0; !isStandardPassive && i < m_StandardPassiveSpeeds.Length; ++i ) isStandardPassive = ( m_dPassiveSpeed == m_StandardPassiveSpeeds[i] ); if ( isStandardActive && m_dCurrentSpeed == m_dActiveSpeed ) m_dCurrentSpeed = activeSpeed; else if ( isStandardPassive && m_dCurrentSpeed == m_dPassiveSpeed ) m_dCurrentSpeed = passiveSpeed; if ( isStandardActive && !m_Paragon ) m_dActiveSpeed = activeSpeed; if ( isStandardPassive && !m_Paragon ) m_dPassiveSpeed = passiveSpeed; if ( version >= 14 ) { m_RemoveIfUntamed = reader.ReadBool(); m_RemoveStep = reader.ReadInt(); } if( version <= 14 && m_Paragon && Hue == 0x31 ) { Hue = Paragon.Hue; //Paragon hue fixed, should now be 0x501. } CheckStatTimers(); ChangeAIType(m_CurrentAI); AddFollowers(); if ( IsAnimatedDead ) Spells.Necromancy.AnimateDeadSpell.Register( m_SummonMaster, this ); } public virtual bool IsHumanInTown() { return ( Body.IsHuman && Region.IsPartOf( typeof( Regions.GuardedRegion ) ) ); } public virtual bool CheckGold( Mobile from, Item dropped ) { if ( dropped is Gold ) return OnGoldGiven( from, (Gold)dropped ); return false; } public virtual bool OnGoldGiven( Mobile from, Gold dropped ) { if ( CheckTeachingMatch( from ) ) { if ( Teach( m_Teaching, from, dropped.Amount, true ) ) { dropped.Delete(); return true; } } else if ( IsHumanInTown() ) { Direction = GetDirectionTo( from ); int oldSpeechHue = this.SpeechHue; this.SpeechHue = 0x23F; SayTo( from, "Thou art giving me gold?" ); if ( dropped.Amount >= 400 ) SayTo( from, "'Tis a noble gift." ); else SayTo( from, "Money is always welcome." ); this.SpeechHue = 0x3B2; SayTo( from, 501548 ); // I thank thee. this.SpeechHue = oldSpeechHue; dropped.Delete(); return true; } return false; } public override bool ShouldCheckStatTimers{ get{ return false; } } #region Food private static Type[] m_Eggs = new Type[] { typeof( FriedEggs ), typeof( Eggs ) }; private static Type[] m_Fish = new Type[] { typeof( FishSteak ), typeof( RawFishSteak ) }; private static Type[] m_GrainsAndHay = new Type[] { typeof( BreadLoaf ), typeof( FrenchBread ), typeof( SheafOfHay ) }; private static Type[] m_Meat = new Type[] { /* Cooked */ typeof( Bacon ), typeof( CookedBird ), typeof( Sausage ), typeof( Ham ), typeof( Ribs ), typeof( LambLeg ), typeof( ChickenLeg ), /* Uncooked */ typeof( RawBird ), typeof( RawRibs ), typeof( RawLambLeg ), typeof( RawChickenLeg ), /* Body Parts */ typeof( Head ), typeof( LeftArm ), typeof( LeftLeg ), typeof( Torso ), typeof( RightArm ), typeof( RightLeg ) }; private static Type[] m_FruitsAndVegies = new Type[] { typeof( HoneydewMelon ), typeof( YellowGourd ), typeof( GreenGourd ), typeof( Banana ), typeof( Bananas ), typeof( Lemon ), typeof( Lime ), typeof( Dates ), typeof( Grapes ), typeof( Peach ), typeof( Pear ), typeof( Apple ), typeof( Watermelon ), typeof( Squash ), typeof( Cantaloupe ), typeof( Carrot ), typeof( Cabbage ), typeof( Onion ), typeof( Lettuce ), typeof( Pumpkin ) }; private static Type[] m_Gold = new Type[] { // white wyrms eat gold.. typeof( Gold ) }; public virtual bool CheckFoodPreference( Item f ) { if ( CheckFoodPreference( f, FoodType.Eggs, m_Eggs ) ) return true; if ( CheckFoodPreference( f, FoodType.Fish, m_Fish ) ) return true; if ( CheckFoodPreference( f, FoodType.GrainsAndHay, m_GrainsAndHay ) ) return true; if ( CheckFoodPreference( f, FoodType.Meat, m_Meat ) ) return true; if ( CheckFoodPreference( f, FoodType.FruitsAndVegies, m_FruitsAndVegies ) ) return true; if ( CheckFoodPreference( f, FoodType.Gold, m_Gold ) ) return true; return false; } public virtual bool CheckFoodPreference( Item fed, FoodType type, Type[] types ) { if ( (FavoriteFood & type) == 0 ) return false; Type fedType = fed.GetType(); bool contains = false; for ( int i = 0; !contains && i < types.Length; ++i ) contains = ( fedType == types[i] ); return contains; } public virtual bool CheckFeed( Mobile from, Item dropped ) { if ( !IsDeadPet && Controlled && (ControlMaster == from || IsPetFriend( from )) && (dropped is Food || dropped is Gold || dropped is CookableFood || dropped is Head || dropped is LeftArm || dropped is LeftLeg || dropped is Torso || dropped is RightArm || dropped is RightLeg) ) { Item f = dropped; if ( CheckFoodPreference( f ) ) { int amount = f.Amount; if ( amount > 0 ) { bool happier = false; int stamGain; if ( f is Gold ) stamGain = amount - 50; else stamGain = (amount * 15) - 50; if ( stamGain > 0 ) Stam += stamGain; if ( Core.SE ) { if ( m_Loyalty < MaxLoyalty ) { m_Loyalty = MaxLoyalty; happier = true; } } else { for ( int i = 0; i < amount; ++i ) { if ( m_Loyalty < MaxLoyalty && 0.5 >= Utility.RandomDouble() ) { m_Loyalty += 10; happier = true; } } } if ( happier ) SayTo( from, 502060 ); // Your pet looks happier. if ( Body.IsAnimal ) Animate( 3, 5, 1, true, false, 0 ); else if ( Body.IsMonster ) Animate( 17, 5, 1, true, false, 0 ); if ( IsBondable && !IsBonded ) { Mobile master = m_ControlMaster; if ( master != null && master == from ) //So friends can't start the bonding process { if ( m_dMinTameSkill <= 29.1 || master.Skills[SkillName.AnimalTaming].Value >= m_dMinTameSkill || GetControlChance( master ) >= 1.0 ) { if ( BondingBegin == DateTime.MinValue ) { BondingBegin = DateTime.Now; } else if ( (BondingBegin + BondingDelay) <= DateTime.Now ) { IsBonded = true; BondingBegin = DateTime.MinValue; from.SendLocalizedMessage( 1049666 ); // Your pet has bonded with you! } } else if( Core.ML ) { from.SendLocalizedMessage( 1075268 ); // Your pet cannot form a bond with you until your animal taming ability has risen. } } } dropped.Delete(); return true; } } } return false; } #endregion public virtual bool CanAngerOnTame{ get{ return false; } } #region OnAction[...] public virtual void OnActionWander() { } public virtual void OnActionCombat() { } public virtual void OnActionGuard() { } public virtual void OnActionFlee() { } public virtual void OnActionInteract() { } public virtual void OnActionBackoff() { } #endregion public override bool OnDragDrop( Mobile from, Item dropped ) { if ( CheckFeed( from, dropped ) ) return true; else if ( CheckGold( from, dropped ) ) return true; return base.OnDragDrop( from, dropped ); } protected virtual BaseAI ForcedAI { get { return null; } } public void ChangeAIType( AIType NewAI ) { if ( m_AI != null ) m_AI.m_Timer.Stop(); if( ForcedAI != null ) { m_AI = ForcedAI; return; } m_AI = null; switch ( NewAI ) { case AIType.AI_Melee: m_AI = new MeleeAI(this); break; case AIType.AI_Animal: m_AI = new AnimalAI(this); break; case AIType.AI_Berserk: m_AI = new BerserkAI(this); break; case AIType.AI_Archer: m_AI = new ArcherAI(this); break; case AIType.AI_Healer: m_AI = new HealerAI(this); break; case AIType.AI_Vendor: m_AI = new VendorAI(this); break; case AIType.AI_Mage: m_AI = new MageAI(this); break; case AIType.AI_Predator: //m_AI = new PredatorAI(this); m_AI = new MeleeAI(this); break; case AIType.AI_Thief: m_AI = new ThiefAI(this); break; } } public void ChangeAIToDefault() { ChangeAIType(m_DefaultAI); } [CommandProperty( AccessLevel.GameMaster )] public AIType AI { get { return m_CurrentAI; } set { m_CurrentAI = value; if (m_CurrentAI == AIType.AI_Use_Default) { m_CurrentAI = m_DefaultAI; } ChangeAIType(m_CurrentAI); } } [CommandProperty( AccessLevel.Administrator )] public bool Debug { get { return m_bDebugAI; } set { m_bDebugAI = value; } } [CommandProperty( AccessLevel.GameMaster )] public int Team { get { return m_iTeam; } set { m_iTeam = value; OnTeamChange(); } } public virtual void OnTeamChange() { } [CommandProperty( AccessLevel.GameMaster )] public Mobile FocusMob { get { return m_FocusMob; } set { m_FocusMob = value; } } [CommandProperty( AccessLevel.GameMaster )] public FightMode FightMode { get { return m_FightMode; } set { m_FightMode = value; } } [CommandProperty( AccessLevel.GameMaster )] public int RangePerception { get { return m_iRangePerception; } set { m_iRangePerception = value; } } [CommandProperty( AccessLevel.GameMaster )] public int RangeFight { get { return m_iRangeFight; } set { m_iRangeFight = value; } } [CommandProperty( AccessLevel.GameMaster )] public int RangeHome { get { return m_iRangeHome; } set { m_iRangeHome = value; } } [CommandProperty( AccessLevel.GameMaster )] public double ActiveSpeed { get { return m_dActiveSpeed; } set { m_dActiveSpeed = value; } } [CommandProperty( AccessLevel.GameMaster )] public double PassiveSpeed { get { return m_dPassiveSpeed; } set { m_dPassiveSpeed = value; } } [CommandProperty( AccessLevel.GameMaster )] public double CurrentSpeed { get { return m_dCurrentSpeed; } set { if ( m_dCurrentSpeed != value ) { m_dCurrentSpeed = value; if (m_AI != null) m_AI.OnCurrentSpeedChanged(); } } } [CommandProperty( AccessLevel.GameMaster )] public Point3D Home { get { return m_pHome; } set { m_pHome = value; } } [CommandProperty( AccessLevel.GameMaster )] public bool Controlled { get { return m_bControlled; } set { if ( m_bControlled == value ) return; m_bControlled = value; Delta( MobileDelta.Noto ); InvalidateProperties(); } } public override void RevealingAction() { Spells.Sixth.InvisibilitySpell.RemoveTimer( this ); base.RevealingAction(); } public void RemoveFollowers() { if ( m_ControlMaster != null ) m_ControlMaster.Followers -= ControlSlots; else if ( m_SummonMaster != null ) m_SummonMaster.Followers -= ControlSlots; if ( m_ControlMaster != null && m_ControlMaster.Followers < 0 ) m_ControlMaster.Followers = 0; if ( m_SummonMaster != null && m_SummonMaster.Followers < 0 ) m_SummonMaster.Followers = 0; } public void AddFollowers() { if ( m_ControlMaster != null ) m_ControlMaster.Followers += ControlSlots; else if ( m_SummonMaster != null ) m_SummonMaster.Followers += ControlSlots; } [CommandProperty( AccessLevel.GameMaster )] public Mobile ControlMaster { get { return m_ControlMaster; } set { if ( m_ControlMaster == value ) return; RemoveFollowers(); m_ControlMaster = value; AddFollowers(); Delta( MobileDelta.Noto ); } } [CommandProperty( AccessLevel.GameMaster )] public Mobile SummonMaster { get { return m_SummonMaster; } set { if ( m_SummonMaster == value ) return; RemoveFollowers(); m_SummonMaster = value; AddFollowers(); Delta( MobileDelta.Noto ); } } [CommandProperty( AccessLevel.GameMaster )] public Mobile ControlTarget { get { return m_ControlTarget; } set { m_ControlTarget = value; } } [CommandProperty( AccessLevel.GameMaster )] public Point3D ControlDest { get { return m_ControlDest; } set { m_ControlDest = value; } } [CommandProperty( AccessLevel.GameMaster )] public OrderType ControlOrder { get { return m_ControlOrder; } set { m_ControlOrder = value; if ( m_AI != null ) m_AI.OnCurrentOrderChanged(); } } [CommandProperty( AccessLevel.GameMaster )] public bool BardProvoked { get { return m_bBardProvoked; } set { m_bBardProvoked = value; } } [CommandProperty( AccessLevel.GameMaster )] public bool BardPacified { get { return m_bBardPacified; } set { m_bBardPacified = value; } } [CommandProperty( AccessLevel.GameMaster )] public Mobile BardMaster { get { return m_bBardMaster; } set { m_bBardMaster = value; } } [CommandProperty( AccessLevel.GameMaster )] public Mobile BardTarget { get { return m_bBardTarget; } set { m_bBardTarget = value; } } [CommandProperty( AccessLevel.GameMaster )] public DateTime BardEndTime { get { return m_timeBardEnd; } set { m_timeBardEnd = value; } } [CommandProperty( AccessLevel.GameMaster )] public double MinTameSkill { get { return m_dMinTameSkill; } set { m_dMinTameSkill = value; } } [CommandProperty( AccessLevel.GameMaster )] public bool Tamable { get { return m_bTamable && !m_Paragon; } set { m_bTamable = value; } } [CommandProperty( AccessLevel.Administrator )] public bool Summoned { get { return m_bSummoned; } set { if ( m_bSummoned == value ) return; m_NextReacquireTime = DateTime.Now; m_bSummoned = value; Delta( MobileDelta.Noto ); InvalidateProperties(); } } [CommandProperty( AccessLevel.Administrator )] public int ControlSlots { get { return m_iControlSlots; } set { m_iControlSlots = value; } } public virtual bool NoHouseRestrictions{ get{ return false; } } public virtual bool IsHouseSummonable{ get{ return false; } } #region Corpse Resources public virtual int Feathers{ get{ return 0; } } public virtual int Wool{ get{ return 0; } } public virtual MeatType MeatType{ get{ return MeatType.Ribs; } } public virtual int Meat{ get{ return 0; } } public virtual int Hides{ get{ return 0; } } public virtual HideType HideType{ get{ return HideType.Regular; } } public virtual int Scales{ get{ return 0; } } public virtual ScaleType ScaleType{ get{ return ScaleType.Red; } } #endregion public virtual bool AutoDispel{ get{ return false; } } public virtual double AutoDispelChance{ get { return ((Core.SE) ? .10 : 1.0); } } public virtual bool IsScaryToPets{ get{ return false; } } public virtual bool IsScaredOfScaryThings{ get{ return true; } } public virtual bool CanRummageCorpses{ get{ return false; } } public virtual void OnGotMeleeAttack( Mobile attacker ) { if ( AutoDispel && attacker is BaseCreature && ((BaseCreature)attacker).IsDispellable && AutoDispelChance > Utility.RandomDouble() ) Dispel( attacker ); } public virtual void Dispel( Mobile m ) { Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x3728, 8, 20, 5042 ); Effects.PlaySound( m, m.Map, 0x201 ); m.Delete(); } public virtual bool DeleteOnRelease{ get{ return m_bSummoned; } } public virtual void OnGaveMeleeAttack( Mobile defender ) { Poison p = HitPoison; if ( m_Paragon ) p = PoisonImpl.IncreaseLevel( p ); if ( p != null && HitPoisonChance >= Utility.RandomDouble() ) defender.ApplyPoison( this, p ); if( AutoDispel && defender is BaseCreature && ((BaseCreature)defender).IsDispellable && AutoDispelChance > Utility.RandomDouble() ) Dispel( defender ); } public override void OnAfterDelete() { if ( m_AI != null ) { if ( m_AI.m_Timer != null ) m_AI.m_Timer.Stop(); m_AI = null; } FocusMob = null; if ( IsAnimatedDead ) Spells.Necromancy.AnimateDeadSpell.Unregister( m_SummonMaster, this ); base.OnAfterDelete(); } public void DebugSay( string text ) { if ( m_bDebugAI ) this.PublicOverheadMessage( MessageType.Regular, 41, false, text ); } public void DebugSay( string format, params object[] args ) { if ( m_bDebugAI ) this.PublicOverheadMessage( MessageType.Regular, 41, false, String.Format( format, args ) ); } /* * Will need to be givent a better name * * This function can be overriden.. so a "Strongest" mobile, can have a different definition depending * on who check for value * -Could add a FightMode.Prefered * */ public virtual double GetValueFrom( Mobile m, FightMode acqType, bool bPlayerOnly ) { if ( ( bPlayerOnly && m.Player ) || !bPlayerOnly ) { switch( acqType ) { case FightMode.Strongest : return (m.Skills[SkillName.Tactics].Value + m.Str); //returns strongest mobile case FightMode.Weakest : return -m.Hits; // returns weakest mobile default : return -GetDistanceToSqrt( m ); // returns closest mobile } } else { return double.MinValue; } } // Turn, - for let, + for right // Basic for now, needs work public virtual void Turn(int iTurnSteps) { int v = (int)Direction; Direction = (Direction)((((v & 0x7) + iTurnSteps) & 0x7) | (v & 0x80)); } public virtual void TurnInternal(int iTurnSteps) { int v = (int)Direction; SetDirection( (Direction)((((v & 0x7) + iTurnSteps) & 0x7) | (v & 0x80)) ); } public bool IsHurt() { return ( Hits != HitsMax ); } public double GetHomeDistance() { return GetDistanceToSqrt( m_pHome ); } public virtual int GetTeamSize(int iRange) { int iCount = 0; foreach ( Mobile m in this.GetMobilesInRange( iRange ) ) { if (m is BaseCreature) { if ( ((BaseCreature)m).Team == Team ) { if ( !m.Deleted ) { if ( m != this ) { if ( CanSee( m ) ) { iCount++; } } } } } } return iCount; } private class TameEntry : ContextMenuEntry { private BaseCreature m_Mobile; public TameEntry( Mobile from, BaseCreature creature ) : base( 6130, 6 ) { m_Mobile = creature; Enabled = Enabled && ( from.Female ? creature.AllowFemaleTamer : creature.AllowMaleTamer ); } public override void OnClick() { if ( !Owner.From.CheckAlive() ) return; Owner.From.TargetLocked = true; SkillHandlers.AnimalTaming.DisableMessage = true; if ( Owner.From.UseSkill( SkillName.AnimalTaming ) ) Owner.From.Target.Invoke( Owner.From, m_Mobile ); SkillHandlers.AnimalTaming.DisableMessage = false; Owner.From.TargetLocked = false; } } #region Teaching public virtual bool CanTeach{ get{ return false; } } public virtual bool CheckTeach( SkillName skill, Mobile from ) { if ( !CanTeach ) return false; if( skill == SkillName.Stealth && from.Skills[SkillName.Hiding].Base < ((Core.SE) ? 50.0 : 80.0) ) return false; if ( skill == SkillName.RemoveTrap && (from.Skills[SkillName.Lockpicking].Base < 50.0 || from.Skills[SkillName.DetectHidden].Base < 50.0) ) return false; if ( !Core.AOS && (skill == SkillName.Focus || skill == SkillName.Chivalry || skill == SkillName.Necromancy) ) return false; return true; } public enum TeachResult { Success, Failure, KnowsMoreThanMe, KnowsWhatIKnow, SkillNotRaisable, NotEnoughFreePoints } public virtual TeachResult CheckTeachSkills( SkillName skill, Mobile m, int maxPointsToLearn, ref int pointsToLearn, bool doTeach ) { if ( !CheckTeach( skill, m ) || !m.CheckAlive() ) return TeachResult.Failure; Skill ourSkill = Skills[skill]; Skill theirSkill = m.Skills[skill]; if ( ourSkill == null || theirSkill == null ) return TeachResult.Failure; int baseToSet = ourSkill.BaseFixedPoint / 3; if ( baseToSet > 420 ) baseToSet = 420; else if ( baseToSet < 200 ) return TeachResult.Failure; if ( baseToSet > theirSkill.CapFixedPoint ) baseToSet = theirSkill.CapFixedPoint; pointsToLearn = baseToSet - theirSkill.BaseFixedPoint; if ( maxPointsToLearn > 0 && pointsToLearn > maxPointsToLearn ) { pointsToLearn = maxPointsToLearn; baseToSet = theirSkill.BaseFixedPoint + pointsToLearn; } if ( pointsToLearn < 0 ) return TeachResult.KnowsMoreThanMe; if ( pointsToLearn == 0 ) return TeachResult.KnowsWhatIKnow; if ( theirSkill.Lock != SkillLock.Up ) return TeachResult.SkillNotRaisable; int freePoints = m.Skills.Cap - m.Skills.Total; int freeablePoints = 0; if ( freePoints < 0 ) freePoints = 0; for ( int i = 0; (freePoints + freeablePoints) < pointsToLearn && i < m.Skills.Length; ++i ) { Skill sk = m.Skills[i]; if ( sk == theirSkill || sk.Lock != SkillLock.Down ) continue; freeablePoints += sk.BaseFixedPoint; } if ( (freePoints + freeablePoints) == 0 ) return TeachResult.NotEnoughFreePoints; if ( (freePoints + freeablePoints) < pointsToLearn ) { pointsToLearn = freePoints + freeablePoints; baseToSet = theirSkill.BaseFixedPoint + pointsToLearn; } if ( doTeach ) { int need = pointsToLearn - freePoints; for ( int i = 0; need > 0 && i < m.Skills.Length; ++i ) { Skill sk = m.Skills[i]; if ( sk == theirSkill || sk.Lock != SkillLock.Down ) continue; if ( sk.BaseFixedPoint < need ) { need -= sk.BaseFixedPoint; sk.BaseFixedPoint = 0; } else { sk.BaseFixedPoint -= need; need = 0; } } /* Sanity check */ if ( baseToSet > theirSkill.CapFixedPoint || (m.Skills.Total - theirSkill.BaseFixedPoint + baseToSet) > m.Skills.Cap ) return TeachResult.NotEnoughFreePoints; theirSkill.BaseFixedPoint = baseToSet; } return TeachResult.Success; } public virtual bool CheckTeachingMatch( Mobile m ) { if ( m_Teaching == (SkillName)(-1) ) return false; if ( m is PlayerMobile ) return ( ((PlayerMobile)m).Learning == m_Teaching ); return true; } private SkillName m_Teaching = (SkillName)(-1); public virtual bool Teach( SkillName skill, Mobile m, int maxPointsToLearn, bool doTeach ) { int pointsToLearn = 0; TeachResult res = CheckTeachSkills( skill, m, maxPointsToLearn, ref pointsToLearn, doTeach ); switch ( res ) { case TeachResult.KnowsMoreThanMe: { Say( 501508 ); // I cannot teach thee, for thou knowest more than I! break; } case TeachResult.KnowsWhatIKnow: { Say( 501509 ); // I cannot teach thee, for thou knowest all I can teach! break; } case TeachResult.NotEnoughFreePoints: case TeachResult.SkillNotRaisable: { // Make sure this skill is marked to raise. If you are near the skill cap (700 points) you may need to lose some points in another skill first. m.SendLocalizedMessage( 501510, "", 0x22 ); break; } case TeachResult.Success: { if ( doTeach ) { Say( 501539 ); // Let me show thee something of how this is done. m.SendLocalizedMessage( 501540 ); // Your skill level increases. m_Teaching = (SkillName)(-1); if ( m is PlayerMobile ) ((PlayerMobile)m).Learning = (SkillName)(-1); } else { // I will teach thee all I know, if paid the amount in full. The price is: Say( 1019077, AffixType.Append, String.Format( " {0}", pointsToLearn ), "" ); Say( 1043108 ); // For less I shall teach thee less. m_Teaching = skill; if ( m is PlayerMobile ) ((PlayerMobile)m).Learning = skill; } return true; } } return false; } #endregion public override void AggressiveAction( Mobile aggressor, bool criminal ) { base.AggressiveAction( aggressor, criminal ); if ( m_AI != null ) m_AI.OnAggressiveAction( aggressor ); StopFlee(); ForceReacquire(); if ( !IsEnemy( aggressor ) ) { Ethics.Player pl = Ethics.Player.Find( aggressor, true ); if ( pl != null && pl.IsShielded ) pl.FinishShield(); } OrderType ct = m_ControlOrder; if ( aggressor.ChangingCombatant && (m_bControlled || m_bSummoned) && (ct == OrderType.Come || ct == OrderType.Stay || ct == OrderType.Stop || ct == OrderType.None || ct == OrderType.Follow) ) { ControlTarget = aggressor; ControlOrder = OrderType.Attack; } else if ( Combatant == null && !m_bBardPacified ) { Warmode = true; Combatant = aggressor; } } public override bool OnMoveOver( Mobile m ) { if ( m is BaseCreature && !((BaseCreature)m).Controlled ) return false; return base.OnMoveOver( m ); } public virtual void AddCustomContextEntries( Mobile from, List<ContextMenuEntry> list ) { } public virtual bool CanDrop { get { return !Summoned; } } public override void GetContextMenuEntries( Mobile from, List<ContextMenuEntry> list ) { base.GetContextMenuEntries( from, list ); if ( m_AI != null && Commandable ) m_AI.GetContextMenuEntries( from, list ); if ( m_bTamable && !m_bControlled && from.Alive ) list.Add( new TameEntry( from, this ) ); AddCustomContextEntries( from, list ); if ( CanTeach && from.Alive ) { Skills ourSkills = this.Skills; Skills theirSkills = from.Skills; for ( int i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i ) { Skill skill = ourSkills[i]; Skill theirSkill = theirSkills[i]; if ( skill != null && theirSkill != null && skill.Base >= 60.0 && CheckTeach( skill.SkillName, from ) ) { double toTeach = skill.Base / 3.0; if ( toTeach > 42.0 ) toTeach = 42.0; list.Add( new TeachEntry( (SkillName)i, this, from, ( toTeach > theirSkill.Base ) ) ); } } } } public override bool HandlesOnSpeech( Mobile from ) { InhumanSpeech speechType = this.SpeechType; if ( speechType != null && (speechType.Flags & IHSFlags.OnSpeech) != 0 && from.InRange( this, 3 ) ) return true; return ( m_AI != null && m_AI.HandlesOnSpeech( from ) && from.InRange( this, m_iRangePerception ) ); } public override void OnSpeech( SpeechEventArgs e ) { InhumanSpeech speechType = this.SpeechType; if ( speechType != null && speechType.OnSpeech( this, e.Mobile, e.Speech ) ) e.Handled = true; else if ( !e.Handled && m_AI != null && e.Mobile.InRange( this, m_iRangePerception ) ) m_AI.OnSpeech( e ); } public override bool IsHarmfulCriminal( Mobile target ) { if ( (Controlled && target == m_ControlMaster) || (Summoned && target == m_SummonMaster) ) return false; if ( target is BaseCreature && ((BaseCreature)target).InitialInnocent && !((BaseCreature)target).Controlled ) return false; if ( target is PlayerMobile && ((PlayerMobile)target).PermaFlags.Count > 0 ) return false; return base.IsHarmfulCriminal( target ); } public override void CriminalAction( bool message ) { base.CriminalAction( message ); if ( Controlled || Summoned ) { if ( m_ControlMaster != null && m_ControlMaster.Player ) m_ControlMaster.CriminalAction( false ); else if ( m_SummonMaster != null && m_SummonMaster.Player ) m_SummonMaster.CriminalAction( false ); } } public override void DoHarmful( Mobile target, bool indirect ) { base.DoHarmful( target, indirect ); if ( target == this || target == m_ControlMaster || target == m_SummonMaster || (!Controlled && !Summoned) ) return; List<AggressorInfo> list = this.Aggressors; for ( int i = 0; i < list.Count; ++i ) { AggressorInfo ai = list[i]; if ( ai.Attacker == target ) return; } list = this.Aggressed; for ( int i = 0; i < list.Count; ++i ) { AggressorInfo ai = list[i]; if ( ai.Defender == target ) { if ( m_ControlMaster != null && m_ControlMaster.Player && m_ControlMaster.CanBeHarmful( target, false ) ) m_ControlMaster.DoHarmful( target, true ); else if ( m_SummonMaster != null && m_SummonMaster.Player && m_SummonMaster.CanBeHarmful( target, false ) ) m_SummonMaster.DoHarmful( target, true ); return; } } } private static Mobile m_NoDupeGuards; public void ReleaseGuardDupeLock() { m_NoDupeGuards = null; } public void ReleaseGuardLock() { EndAction( typeof( GuardedRegion ) ); } private DateTime m_IdleReleaseTime; public virtual bool CheckIdle() { if ( Combatant != null ) return false; // in combat.. not idling if ( m_IdleReleaseTime > DateTime.MinValue ) { // idling... if ( DateTime.Now >= m_IdleReleaseTime ) { m_IdleReleaseTime = DateTime.MinValue; return false; // idle is over } return true; // still idling } if ( 95 > Utility.Random( 100 ) ) return false; // not idling, but don't want to enter idle state m_IdleReleaseTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 15, 25 ) ); if ( Body.IsHuman ) { switch ( Utility.Random( 2 ) ) { case 0: Animate( 5, 5, 1, true, true, 1 ); break; case 1: Animate( 6, 5, 1, true, false, 1 ); break; } } else if ( Body.IsAnimal ) { switch ( Utility.Random( 3 ) ) { case 0: Animate( 3, 3, 1, true, false, 1 ); break; case 1: Animate( 9, 5, 1, true, false, 1 ); break; case 2: Animate( 10, 5, 1, true, false, 1 ); break; } } else if ( Body.IsMonster ) { switch ( Utility.Random( 2 ) ) { case 0: Animate( 17, 5, 1, true, false, 1 ); break; case 1: Animate( 18, 5, 1, true, false, 1 ); break; } } PlaySound( GetIdleSound() ); return true; // entered idle state } protected override void OnLocationChange( Point3D oldLocation ) { Map map = this.Map; if ( PlayerRangeSensitive && m_AI != null && map != null && map.GetSector( this.Location ).Active ) m_AI.Activate(); base.OnLocationChange( oldLocation ); } public override void OnMovement( Mobile m, Point3D oldLocation ) { base.OnMovement( m, oldLocation ); if ( ReacquireOnMovement || m_Paragon ) ForceReacquire(); InhumanSpeech speechType = this.SpeechType; if ( speechType != null ) speechType.OnMovement( this, m, oldLocation ); /* Begin notice sound */ if ( (!m.Hidden || m.AccessLevel == AccessLevel.Player) && m.Player && m_FightMode != FightMode.Aggressor && m_FightMode != FightMode.None && Combatant == null && !Controlled && !Summoned ) { // If this creature defends itself but doesn't actively attack (animal) or // doesn't fight at all (vendor) then no notice sounds are played.. // So, players are only notified of aggressive monsters // Monsters that are currently fighting are ignored // Controlled or summoned creatures are ignored if ( InRange( m.Location, 18 ) && !InRange( oldLocation, 18 ) ) { if ( Body.IsMonster ) Animate( 11, 5, 1, true, false, 1 ); PlaySound( GetAngerSound() ); } } /* End notice sound */ if ( m_NoDupeGuards == m ) return; if ( !Body.IsHuman || Kills >= 5 || AlwaysMurderer || AlwaysAttackable || m.Kills < 5 || !m.InRange( Location, 12 ) || !m.Alive ) return; GuardedRegion guardedRegion = (GuardedRegion) this.Region.GetRegion( typeof( GuardedRegion ) ); if ( guardedRegion != null ) { if ( !guardedRegion.IsDisabled() && guardedRegion.IsGuardCandidate( m ) && BeginAction( typeof( GuardedRegion ) ) ) { Say( 1013037 + Utility.Random( 16 ) ); guardedRegion.CallGuards( this.Location ); Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerCallback( ReleaseGuardLock ) ); m_NoDupeGuards = m; Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ReleaseGuardDupeLock ) ); } } } public void AddSpellAttack( Type type ) { m_arSpellAttack.Add ( type ); } public void AddSpellDefense( Type type ) { m_arSpellDefense.Add ( type ); } public Spell GetAttackSpellRandom() { if ( m_arSpellAttack.Count > 0 ) { Type type = m_arSpellAttack[Utility.Random(m_arSpellAttack.Count)]; object[] args = {this, null}; return Activator.CreateInstance( type, args ) as Spell; } else { return null; } } public Spell GetDefenseSpellRandom() { if ( m_arSpellDefense.Count > 0 ) { Type type = m_arSpellDefense[Utility.Random(m_arSpellDefense.Count)]; object[] args = {this, null}; return Activator.CreateInstance( type, args ) as Spell; } else { return null; } } public Spell GetSpellSpecific( Type type ) { int i; for( i=0; i< m_arSpellAttack.Count; i++ ) { if( m_arSpellAttack[i] == type ) { object[] args = { this, null }; return Activator.CreateInstance( type, args ) as Spell; } } for ( i=0; i< m_arSpellDefense.Count; i++ ) { if ( m_arSpellDefense[i] == type ) { object[] args = {this, null}; return Activator.CreateInstance( type, args ) as Spell; } } return null; } #region Set[...] public void SetDamage( int val ) { m_DamageMin = val; m_DamageMax = val; } public void SetDamage( int min, int max ) { m_DamageMin = min; m_DamageMax = max; } public void SetHits( int val ) { if ( val < 1000 && !Core.AOS ) val = (val * 100) / 60; m_HitsMax = val; Hits = HitsMax; } public void SetHits( int min, int max ) { if ( min < 1000 && !Core.AOS ) { min = (min * 100) / 60; max = (max * 100) / 60; } m_HitsMax = Utility.RandomMinMax( min, max ); Hits = HitsMax; } public void SetStam( int val ) { m_StamMax = val; Stam = StamMax; } public void SetStam( int min, int max ) { m_StamMax = Utility.RandomMinMax( min, max ); Stam = StamMax; } public void SetMana( int val ) { m_ManaMax = val; Mana = ManaMax; } public void SetMana( int min, int max ) { m_ManaMax = Utility.RandomMinMax( min, max ); Mana = ManaMax; } public void SetStr( int val ) { RawStr = val; Hits = HitsMax; } public void SetStr( int min, int max ) { RawStr = Utility.RandomMinMax( min, max ); Hits = HitsMax; } public void SetDex( int val ) { RawDex = val; Stam = StamMax; } public void SetDex( int min, int max ) { RawDex = Utility.RandomMinMax( min, max ); Stam = StamMax; } public void SetInt( int val ) { RawInt = val; Mana = ManaMax; } public void SetInt( int min, int max ) { RawInt = Utility.RandomMinMax( min, max ); Mana = ManaMax; } public void SetDamageType( ResistanceType type, int min, int max ) { SetDamageType( type, Utility.RandomMinMax( min, max ) ); } public void SetDamageType( ResistanceType type, int val ) { switch ( type ) { case ResistanceType.Physical: m_PhysicalDamage = val; break; case ResistanceType.Fire: m_FireDamage = val; break; case ResistanceType.Cold: m_ColdDamage = val; break; case ResistanceType.Poison: m_PoisonDamage = val; break; case ResistanceType.Energy: m_EnergyDamage = val; break; } } public void SetResistance( ResistanceType type, int min, int max ) { SetResistance( type, Utility.RandomMinMax( min, max ) ); } public void SetResistance( ResistanceType type, int val ) { switch ( type ) { case ResistanceType.Physical: m_PhysicalResistance = val; break; case ResistanceType.Fire: m_FireResistance = val; break; case ResistanceType.Cold: m_ColdResistance = val; break; case ResistanceType.Poison: m_PoisonResistance = val; break; case ResistanceType.Energy: m_EnergyResistance = val; break; } UpdateResistances(); } public void SetSkill( SkillName name, double val ) { Skills[name].BaseFixedPoint = (int)(val * 10); if ( Skills[name].Base > Skills[name].Cap ) Skills[name].Cap = Skills[name].Base; } public void SetSkill( SkillName name, double min, double max ) { int minFixed = (int)(min * 10); int maxFixed = (int)(max * 10); Skills[name].BaseFixedPoint = Utility.RandomMinMax( minFixed, maxFixed ); if ( Skills[name].Base > Skills[name].Cap ) Skills[name].Cap = Skills[name].Base; } public void SetFameLevel( int level ) { switch ( level ) { case 1: Fame = Utility.RandomMinMax( 0, 1249 ); break; case 2: Fame = Utility.RandomMinMax( 1250, 2499 ); break; case 3: Fame = Utility.RandomMinMax( 2500, 4999 ); break; case 4: Fame = Utility.RandomMinMax( 5000, 9999 ); break; case 5: Fame = Utility.RandomMinMax( 10000, 10000 ); break; } } public void SetKarmaLevel( int level ) { switch ( level ) { case 0: Karma = -Utility.RandomMinMax( 0, 624 ); break; case 1: Karma = -Utility.RandomMinMax( 625, 1249 ); break; case 2: Karma = -Utility.RandomMinMax( 1250, 2499 ); break; case 3: Karma = -Utility.RandomMinMax( 2500, 4999 ); break; case 4: Karma = -Utility.RandomMinMax( 5000, 9999 ); break; case 5: Karma = -Utility.RandomMinMax( 10000, 10000 ); break; } } #endregion public static void Cap( ref int val, int min, int max ) { if ( val < min ) val = min; else if ( val > max ) val = max; } #region Pack & Loot public void PackPotion() { PackItem( Loot.RandomPotion() ); } public void PackNecroScroll( int index ) { if ( !Core.AOS || 0.05 <= Utility.RandomDouble() ) return; PackItem( Loot.Construct( Loot.NecromancyScrollTypes, index ) ); } public void PackScroll( int minCircle, int maxCircle ) { PackScroll( Utility.RandomMinMax( minCircle, maxCircle ) ); } public void PackScroll( int circle ) { int min = (circle - 1) * 8; PackItem( Loot.RandomScroll( min, min + 7, SpellbookType.Regular ) ); } public void PackMagicItems( int minLevel, int maxLevel ) { PackMagicItems( minLevel, maxLevel, 0.30, 0.15 ); } public void PackMagicItems( int minLevel, int maxLevel, double armorChance, double weaponChance ) { if ( !PackArmor( minLevel, maxLevel, armorChance ) ) PackWeapon( minLevel, maxLevel, weaponChance ); } protected bool m_Spawning; protected int m_KillersLuck; public virtual void GenerateLoot( bool spawning ) { m_Spawning = spawning; if ( !spawning ) m_KillersLuck = LootPack.GetLuckChanceForKiller( this ); GenerateLoot(); if ( m_Paragon ) { if ( Fame < 1250 ) AddLoot( LootPack.Meager ); else if ( Fame < 2500 ) AddLoot( LootPack.Average ); else if ( Fame < 5000 ) AddLoot( LootPack.Rich ); else if ( Fame < 10000 ) AddLoot( LootPack.FilthyRich ); else AddLoot( LootPack.UltraRich ); } m_Spawning = false; m_KillersLuck = 0; } public virtual void GenerateLoot() { } public virtual void AddLoot( LootPack pack, int amount ) { for ( int i = 0; i < amount; ++i ) AddLoot( pack ); } public virtual void AddLoot( LootPack pack ) { if ( Summoned ) return; Container backpack = Backpack; if ( backpack == null ) { backpack = new Backpack(); backpack.Movable = false; AddItem( backpack ); } pack.Generate( this, backpack, m_Spawning, m_KillersLuck ); } public bool PackArmor( int minLevel, int maxLevel ) { return PackArmor( minLevel, maxLevel, 1.0 ); } public bool PackArmor( int minLevel, int maxLevel, double chance ) { if ( chance <= Utility.RandomDouble() ) return false; Cap( ref minLevel, 0, 5 ); Cap( ref maxLevel, 0, 5 ); if ( Core.AOS ) { Item item = Loot.RandomArmorOrShieldOrJewelry(); if ( item == null ) return false; int attributeCount, min, max; GetRandomAOSStats( minLevel, maxLevel, out attributeCount, out min, out max ); if ( item is BaseArmor ) BaseRunicTool.ApplyAttributesTo( (BaseArmor)item, attributeCount, min, max ); else if ( item is BaseJewel ) BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, attributeCount, min, max ); PackItem( item ); } else { BaseArmor armor = Loot.RandomArmorOrShield(); if ( armor == null ) return false; armor.ProtectionLevel = (ArmorProtectionLevel)RandomMinMaxScaled( minLevel, maxLevel ); armor.Durability = (ArmorDurabilityLevel)RandomMinMaxScaled( minLevel, maxLevel ); PackItem( armor ); } return true; } public static void GetRandomAOSStats( int minLevel, int maxLevel, out int attributeCount, out int min, out int max ) { int v = RandomMinMaxScaled( minLevel, maxLevel ); if ( v >= 5 ) { attributeCount = Utility.RandomMinMax( 2, 6 ); min = 20; max = 70; } else if ( v == 4 ) { attributeCount = Utility.RandomMinMax( 2, 4 ); min = 20; max = 50; } else if ( v == 3 ) { attributeCount = Utility.RandomMinMax( 2, 3 ); min = 20; max = 40; } else if ( v == 2 ) { attributeCount = Utility.RandomMinMax( 1, 2 ); min = 10; max = 30; } else { attributeCount = 1; min = 10; max = 20; } } public static int RandomMinMaxScaled( int min, int max ) { if ( min == max ) return min; if ( min > max ) { int hold = min; min = max; max = hold; } /* Example: * min: 1 * max: 5 * count: 5 * * total = (5*5) + (4*4) + (3*3) + (2*2) + (1*1) = 25 + 16 + 9 + 4 + 1 = 55 * * chance for min+0 : 25/55 : 45.45% * chance for min+1 : 16/55 : 29.09% * chance for min+2 : 9/55 : 16.36% * chance for min+3 : 4/55 : 7.27% * chance for min+4 : 1/55 : 1.81% */ int count = max - min + 1; int total = 0, toAdd = count; for ( int i = 0; i < count; ++i, --toAdd ) total += toAdd*toAdd; int rand = Utility.Random( total ); toAdd = count; int val = min; for ( int i = 0; i < count; ++i, --toAdd, ++val ) { rand -= toAdd*toAdd; if ( rand < 0 ) break; } return val; } public bool PackSlayer() { return PackSlayer( 0.05 ); } public bool PackSlayer( double chance ) { if ( chance <= Utility.RandomDouble() ) return false; if ( Utility.RandomBool() ) { BaseInstrument instrument = Loot.RandomInstrument(); if ( instrument != null ) { instrument.Slayer = SlayerGroup.GetLootSlayerType( GetType() ); PackItem( instrument ); } } else if ( !Core.AOS ) { BaseWeapon weapon = Loot.RandomWeapon(); if ( weapon != null ) { weapon.Slayer = SlayerGroup.GetLootSlayerType( GetType() ); PackItem( weapon ); } } return true; } public bool PackWeapon( int minLevel, int maxLevel ) { return PackWeapon( minLevel, maxLevel, 1.0 ); } public bool PackWeapon( int minLevel, int maxLevel, double chance ) { if ( chance <= Utility.RandomDouble() ) return false; Cap( ref minLevel, 0, 5 ); Cap( ref maxLevel, 0, 5 ); if ( Core.AOS ) { Item item = Loot.RandomWeaponOrJewelry(); if ( item == null ) return false; int attributeCount, min, max; GetRandomAOSStats( minLevel, maxLevel, out attributeCount, out min, out max ); if ( item is BaseWeapon ) BaseRunicTool.ApplyAttributesTo( (BaseWeapon)item, attributeCount, min, max ); else if ( item is BaseJewel ) BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, attributeCount, min, max ); PackItem( item ); } else { BaseWeapon weapon = Loot.RandomWeapon(); if ( weapon == null ) return false; if ( 0.05 > Utility.RandomDouble() ) weapon.Slayer = SlayerName.Silver; weapon.DamageLevel = (WeaponDamageLevel)RandomMinMaxScaled( minLevel, maxLevel ); weapon.AccuracyLevel = (WeaponAccuracyLevel)RandomMinMaxScaled( minLevel, maxLevel ); weapon.DurabilityLevel = (WeaponDurabilityLevel)RandomMinMaxScaled( minLevel, maxLevel ); PackItem( weapon ); } return true; } public void PackGold( int amount ) { if ( amount > 0 ) PackItem( new Gold( amount ) ); } public void PackGold( int min, int max ) { PackGold( Utility.RandomMinMax( min, max ) ); } public void PackStatue( int min, int max ) { PackStatue( Utility.RandomMinMax( min, max ) ); } public void PackStatue( int amount ) { for ( int i = 0; i < amount; ++i ) PackStatue(); } public void PackStatue() { PackItem( Loot.RandomStatue() ); } public void PackGem() { PackGem( 1 ); } public void PackGem( int min, int max ) { PackGem( Utility.RandomMinMax( min, max ) ); } public void PackGem( int amount ) { if ( amount <= 0 ) return; Item gem = Loot.RandomGem(); gem.Amount = amount; PackItem( gem ); } public void PackNecroReg( int min, int max ) { PackNecroReg( Utility.RandomMinMax( min, max ) ); } public void PackNecroReg( int amount ) { for ( int i = 0; i < amount; ++i ) PackNecroReg(); } public void PackNecroReg() { if ( !Core.AOS ) return; PackItem( Loot.RandomNecromancyReagent() ); } public void PackReg( int min, int max ) { PackReg( Utility.RandomMinMax( min, max ) ); } public void PackReg( int amount ) { if ( amount <= 0 ) return; Item reg = Loot.RandomReagent(); reg.Amount = amount; PackItem( reg ); } public void PackItem( Item item ) { if ( Summoned || item == null ) { if ( item != null ) item.Delete(); return; } Container pack = Backpack; if ( pack == null ) { pack = new Backpack(); pack.Movable = false; AddItem( pack ); } if ( !item.Stackable || !pack.TryDropItem( this, item, false ) ) // try stack pack.DropItem( item ); // failed, drop it anyway } #endregion public override void OnDoubleClick( Mobile from ) { if ( from.AccessLevel >= AccessLevel.GameMaster && !Body.IsHuman ) { Container pack = this.Backpack; if ( pack != null ) pack.DisplayTo( from ); } if ( this.DeathAdderCharmable && from.CanBeHarmful( this, false ) ) { DeathAdder da = Spells.Necromancy.SummonFamiliarSpell.Table[from] as DeathAdder; if ( da != null && !da.Deleted ) { from.SendAsciiMessage( "You charm the snake. Select a target to attack." ); from.Target = new DeathAdderCharmTarget( this ); } } base.OnDoubleClick( from ); } private class DeathAdderCharmTarget : Target { private BaseCreature m_Charmed; public DeathAdderCharmTarget( BaseCreature charmed ) : base( -1, false, TargetFlags.Harmful ) { m_Charmed = charmed; } protected override void OnTarget( Mobile from, object targeted ) { if ( !m_Charmed.DeathAdderCharmable || m_Charmed.Combatant != null || !from.CanBeHarmful( m_Charmed, false ) ) return; DeathAdder da = Spells.Necromancy.SummonFamiliarSpell.Table[from] as DeathAdder; if ( da == null || da.Deleted ) return; Mobile targ = targeted as Mobile; if ( targ == null || !from.CanBeHarmful( targ, false ) ) return; from.RevealingAction(); from.DoHarmful( targ, true ); m_Charmed.Combatant = targ; if ( m_Charmed.AIObject != null ) m_Charmed.AIObject.Action = ActionType.Combat; } } public override void AddNameProperties( ObjectPropertyList list ) { base.AddNameProperties( list ); if ( Controlled && Commandable ) { if ( Summoned ) list.Add( 1049646 ); // (summoned) else if ( IsBonded ) //Intentional difference (showing ONLY bonded when bonded instead of bonded & tame) list.Add( 1049608 ); // (bonded) else list.Add( 502006 ); // (tame) } } public override void OnSingleClick( Mobile from ) { if ( Controlled && Commandable ) { int number; if ( Summoned ) number = 1049646; // (summoned) else if ( IsBonded ) number = 1049608; // (bonded) else number = 502006; // (tame) PrivateOverheadMessage( MessageType.Regular, 0x3B2, number, from.NetState ); } base.OnSingleClick( from ); } public virtual double TreasureMapChance{ get{ return TreasureMap.LootChance; } } public virtual int TreasureMapLevel{ get{ return -1; } } public virtual bool IgnoreYoungProtection { get { return false; } } public override bool OnBeforeDeath() { int treasureLevel = TreasureMapLevel; if ( treasureLevel == 1 && this.Map == Map.Trammel && TreasureMap.IsInHavenIsland( this ) ) { Mobile killer = this.LastKiller; if ( killer is BaseCreature ) killer = ((BaseCreature)killer).GetMaster(); if ( killer is PlayerMobile && ((PlayerMobile)killer).Young ) treasureLevel = 0; } if ( !Summoned && !NoKillAwards && !IsBonded && treasureLevel >= 0 ) { if ( m_Paragon && Paragon.ChestChance > Utility.RandomDouble() ) PackItem( new ParagonChest( this.Name, treasureLevel ) ); else if ( (Map == Map.Felucca || Map == Map.Trammel) && TreasureMap.LootChance >= Utility.RandomDouble() ) PackItem( new TreasureMap( treasureLevel, Map ) ); } if ( !Summoned && !NoKillAwards && !m_HasGeneratedLoot ) { m_HasGeneratedLoot = true; GenerateLoot( false ); } if ( !NoKillAwards && Region.IsPartOf( "Doom" ) ) { int bones = Engines.Quests.Doom.TheSummoningQuest.GetDaemonBon esFor( this ); if ( bones > 0 ) PackItem( new DaemonBone( bones ) ); } if ( IsAnimatedDead ) Effects.SendLocationEffect( Location, Map, 0x3728, 13, 1, 0x461, 4 ); InhumanSpeech speechType = this.SpeechType; if ( speechType != null ) speechType.OnDeath( this ); if ( m_ReceivedHonorContext != null ) m_ReceivedHonorContext.OnTargetKilled(); return base.OnBeforeDeath(); } private bool m_NoKillAwards; public bool NoKillAwards { get{ return m_NoKillAwards; } set{ m_NoKillAwards = value; } } public int ComputeBonusDamage( List<DamageEntry> list, Mobile m ) { int bonus = 0; for ( int i = list.Count - 1; i >= 0; --i ) { DamageEntry de = list[i]; if ( de.Damager == m || !(de.Damager is BaseCreature) ) continue; BaseCreature bc = (BaseCreature)de.Damager; Mobile master = null; master = bc.GetMaster(); if ( master == m ) bonus += de.DamageGiven; } return bonus; } public Mobile GetMaster() { if ( Controlled && ControlMaster != null ) return ControlMaster; else if ( Summoned && SummonMaster != null ) return SummonMaster; return null; } private class FKEntry { public Mobile m_Mobile; public int m_Damage; public FKEntry( Mobile m, int damage ) { m_Mobile = m; m_Damage = damage; } } public static List<DamageStore> GetLootingRights( List<DamageEntry> damageEntries, int hitsMax ) { List<DamageStore> rights = new List<DamageStore>(); for ( int i = damageEntries.Count - 1; i >= 0; --i ) { if ( i >= damageEntries.Count ) continue; DamageEntry de = damageEntries[i]; if ( de.HasExpired ) { damageEntries.RemoveAt( i ); continue; } int damage = de.DamageGiven; List<DamageEntry> respList = de.Responsible; if ( respList != null ) { for ( int j = 0; j < respList.Count; ++j ) { DamageEntry subEntry = respList[j]; Mobile master = subEntry.Damager; if ( master == null || master.Deleted || !master.Player ) continue; bool needNewSubEntry = true; for ( int k = 0; needNewSubEntry && k < rights.Count; ++k ) { DamageStore ds = rights[k]; if ( ds.m_Mobile == master ) { ds.m_Damage += subEntry.DamageGiven; needNewSubEntry = false; } } if ( needNewSubEntry ) rights.Add( new DamageStore( master, subEntry.DamageGiven ) ); damage -= subEntry.DamageGiven; } } Mobile m = de.Damager; if ( m == null || m.Deleted || !m.Player ) continue; if ( damage <= 0 ) continue; bool needNewEntry = true; for ( int j = 0; needNewEntry && j < rights.Count; ++j ) { DamageStore ds = rights[j]; if ( ds.m_Mobile == m ) { ds.m_Damage += damage; needNewEntry = false; } } if ( needNewEntry ) rights.Add( new DamageStore( m, damage ) ); } if ( rights.Count > 0 ) { if ( rights.Count > 1 ) rights.Sort(); int topDamage = rights[0].m_Damage; int minDamage; if ( hitsMax >= 3000 ) minDamage = topDamage / 16; else if ( hitsMax >= 1000 ) minDamage = topDamage / 8; else if ( hitsMax >= 200 ) minDamage = topDamage / 4; else minDamage = topDamage / 2; for ( int i = 0; i < rights.Count; ++i ) { DamageStore ds = rights[i]; ds.m_HasRight = ( ds.m_Damage >= minDamage ); } } return rights; } public virtual void OnKilledBy( Mobile mob ) { if ( m_Paragon && Paragon.CheckArtifactChance( mob, this ) ) Paragon.GiveArtifactTo( mob ); } public override void OnDeath( Container c ) { MeerMage.StopEffect( this, false ); if ( IsBonded ) { int sound = this.GetDeathSound(); if ( sound >= 0 ) Effects.PlaySound( this, this.Map, sound ); Warmode = false; Poison = null; Combatant = null; Hits = 0; Stam = 0; Mana = 0; IsDeadPet = true; ControlTarget = ControlMaster; ControlOrder = OrderType.Follow; ProcessDeltaQueue(); SendIncomingPacket(); SendIncomingPacket(); List<AggressorInfo> aggressors = this.Aggressors; for ( int i = 0; i < aggressors.Count; ++i ) { AggressorInfo info = aggressors[i]; if ( info.Attacker.Combatant == this ) info.Attacker.Combatant = null; } List<AggressorInfo> aggressed = this.Aggressed; for ( int i = 0; i < aggressed.Count; ++i ) { AggressorInfo info = aggressed[i]; if ( info.Defender.Combatant == this ) info.Defender.Combatant = null; } Mobile owner = this.ControlMaster; if ( owner == null || owner.Deleted || owner.Map != this.Map || !owner.InRange( this, 12 ) || !this.CanSee( owner ) || !this.InLOS( owner ) ) { if ( this.OwnerAbandonTime == DateTime.MinValue ) this.OwnerAbandonTime = DateTime.Now; } else { this.OwnerAbandonTime = DateTime.MinValue; } CheckStatTimers(); } else { if ( !Summoned && !m_NoKillAwards ) { int totalFame = Fame / 100; int totalKarma = -Karma / 100; List<DamageStore> list = GetLootingRights( this.DamageEntries, this.HitsMax ); bool givenQuestKill = false; bool givenFactionKill = false; bool givenToTKill = false; for ( int i = 0; i < list.Count; ++i ) { DamageStore ds = list[i]; if ( !ds.m_HasRight ) continue; Titles.AwardFame( ds.m_Mobile, totalFame, true ); Titles.AwardKarma( ds.m_Mobile, totalKarma, true ); OnKilledBy( ds.m_Mobile ); if ( !givenFactionKill ) { givenFactionKill = true; Faction.HandleDeath( this, ds.m_Mobile ); } if( !givenToTKill ) { givenToTKill = true; TreasuresOfTokuno.HandleKill( this, ds.m_Mobile ); } if ( givenQuestKill ) continue; PlayerMobile pm = ds.m_Mobile as PlayerMobile; if ( pm != null ) { QuestSystem qs = pm.Quest; if ( qs != null ) { qs.OnKill( this, c ); givenQuestKill = true; } } } } base.OnDeath( c ); if ( DeleteCorpseOnDeath ) c.Delete(); } } /* To save on cpu usage, RunUO creatures only reacquire creatures under the following circumstances: * - 10 seconds have elapsed since the last time it tried * - The creature was attacked * - Some creatures, like dragons, will reacquire when they see someone move * * This functionality appears to be implemented on OSI as well */ private DateTime m_NextReacquireTime; public DateTime NextReacquireTime{ get{ return m_NextReacquireTime; } set{ m_NextReacquireTime = value; } } public virtual TimeSpan ReacquireDelay{ get{ return TimeSpan.FromSeconds( 10.0 ); } } public virtual bool ReacquireOnMovement{ get{ return false; } } public void ForceReacquire() { m_NextReacquireTime = DateTime.MinValue; } public override void OnDelete() { SetControlMaster( null ); SummonMaster = null; if ( m_ReceivedHonorContext != null ) m_ReceivedHonorContext.Cancel(); base.OnDelete(); } public override bool CanBeHarmful( Mobile target, bool message, bool ignoreOurBlessedness ) { if ( target is BaseFactionGuard ) return false; if ( (target is BaseVendor && ((BaseVendor)target).IsInvulnerable) || target is PlayerVendor || target is TownCrier ) { if ( message ) { if ( target.Title == null ) SendMessage( "{0} the vendor cannot be harmed.", target.Name ); else SendMessage( "{0} {1} cannot be harmed.", target.Name, target.Title ); } return false; } return base.CanBeHarmful( target, message, ignoreOurBlessedness ); } public override bool CanBeRenamedBy( Mobile from ) { bool ret = base.CanBeRenamedBy( from ); if ( Controlled && from == ControlMaster ) ret = true; return ret; } public bool SetControlMaster( Mobile m ) { if ( m == null ) { ControlMaster = null; Controlled = false; ControlTarget = null; ControlOrder = OrderType.None; Guild = null; Delta( MobileDelta.Noto ); } else { SpawnEntry se = this.Spawner as SpawnEntry; if ( se != null && se.UnlinkOnTaming ) { this.Spawner.Remove( this ); this.Spawner = null; } if ( m.Followers + ControlSlots > m.FollowersMax ) { m.SendLocalizedMessage( 1049607 ); // You have too many followers to control that creature. return false; } CurrentWayPoint = null;//so tamed animals don't try to go back ControlMaster = m; Controlled = true; ControlTarget = null; ControlOrder = OrderType.Come; Guild = null; Delta( MobileDelta.Noto ); } return true; } public override void OnRegionChange( Region Old, Region New ) { base.OnRegionChange( Old, New ); if ( this.Controlled ) { SpawnEntry se = this.Spawner as SpawnEntry; if ( se != null && !se.UnlinkOnTaming && ( New == null || !New.AcceptsSpawnsFrom( se.Region ) ) ) { this.Spawner.Remove( this ); this.Spawner = null; } } } private static bool m_Summoning; public static bool Summoning { get{ return m_Summoning; } set{ m_Summoning = value; } } public static bool Summon( BaseCreature creature, Mobile caster, Point3D p, int sound, TimeSpan duration ) { return Summon( creature, true, caster, p, sound, duration ); } public static bool Summon( BaseCreature creature, bool controlled, Mobile caster, Point3D p, int sound, TimeSpan duration ) { if ( caster.Followers + creature.ControlSlots > caster.FollowersMax ) { caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. creature.Delete(); return false; } m_Summoning = true; if ( controlled ) creature.SetControlMaster( caster ); creature.RangeHome = 10; creature.Summoned = true; creature.SummonMaster = caster; Container pack = creature.Backpack; if ( pack != null ) { for ( int i = pack.Items.Count - 1; i >= 0; --i ) { if ( i >= pack.Items.Count ) continue; pack.Items[i].Delete(); } } new UnsummonTimer( caster, creature, duration ).Start(); creature.m_SummonEnd = DateTime.Now + duration; creature.MoveToWorld( p, caster.Map ); Effects.PlaySound( p, creature.Map, sound ); m_Summoning = false; return true; } private static bool EnableRummaging = true; private const double ChanceToRummage = 0.5; // 50% private const double MinutesToNextRummageMin = 1.0; private const double MinutesToNextRummageMax = 4.0; private const double MinutesToNextChanceMin = 0.25; private const double MinutesToNextChanceMax = 0.75; private DateTime m_NextRummageTime; public virtual bool CanBreath { get { return HasBreath && !Summoned; } } public virtual bool IsDispellable { get { return Summoned && !IsAnimatedDead; } } public virtual void OnThink() { if ( EnableRummaging && CanRummageCorpses && !Summoned && !Controlled && DateTime.Now >= m_NextRummageTime ) { double min, max; if ( ChanceToRummage > Utility.RandomDouble() && Rummage() ) { min = MinutesToNextRummageMin; max = MinutesToNextRummageMax; } else { min = MinutesToNextChanceMin; max = MinutesToNextChanceMax; } double delay = min + (Utility.RandomDouble() * (max - min)); m_NextRummageTime = DateTime.Now + TimeSpan.FromMinutes( delay ); } if ( CanBreath && DateTime.Now >= m_NextBreathTime ) // tested: controled dragons do breath fire, what about summoned skeletal dragons? { Mobile target = this.Combatant; if ( target != null && target.Alive && !target.IsDeadBondedPet && CanBeHarmful( target ) && target.Map == this.Map && !IsDeadBondedPet && target.InRange( this, BreathRange ) && InLOS( target ) && !BardPacified ) BreathStart( target ); m_NextBreathTime = DateTime.Now + TimeSpan.FromSeconds( BreathMinDelay + (Utility.RandomDouble() * BreathMaxDelay) ); } } public virtual bool Rummage() { Corpse toRummage = null; foreach ( Item item in this.GetItemsInRange( 2 ) ) { if ( item is Corpse && item.Items.Count > 0 ) { toRummage = (Corpse)item; break; } } if ( toRummage == null ) return false; Container pack = this.Backpack; if ( pack == null ) return false; List<Item> items = toRummage.Items; bool rejected; LRReason reason; for ( int i = 0; i < items.Count; ++i ) { Item item = items[Utility.Random( items.Count )]; Lift( item, item.Amount, out rejected, out reason ); if ( !rejected && Drop( this, new Point3D( -1, -1, 0 ) ) ) { // *rummages through a corpse and takes an item* PublicOverheadMessage( MessageType.Emote, 0x3B2, 1008086 ); return true; } } return false; } public void Pacify( Mobile master, DateTime endtime ) { BardPacified = true; BardEndTime = endtime; } public override Mobile GetDamageMaster( Mobile damagee ) { if ( m_bBardProvoked && damagee == m_bBardTarget ) return m_bBardMaster; else if ( m_bControlled && m_ControlMaster != null ) return m_ControlMaster; else if ( m_bSummoned && m_SummonMaster != null ) return m_SummonMaster; return base.GetDamageMaster( damagee ); } public void Provoke( Mobile master, Mobile target, bool bSuccess ) { BardProvoked = true; this.PublicOverheadMessage( MessageType.Emote, EmoteHue, false, "*looks furious*" ); if ( bSuccess ) { PlaySound( GetIdleSound() ); BardMaster = master; BardTarget = target; Combatant = target; BardEndTime = DateTime.Now + TimeSpan.FromSeconds( 30.0 ); if ( target is BaseCreature ) { BaseCreature t = (BaseCreature)target; if ( t.Unprovokable || (t.IsParagon && BaseInstrument.GetBaseDifficulty( t ) >= 160.0) ) return; t.BardProvoked = true; t.BardMaster = master; t.BardTarget = this; t.Combatant = this; t.BardEndTime = DateTime.Now + TimeSpan.FromSeconds( 30.0 ); } } else { PlaySound( GetAngerSound() ); BardMaster = master; BardTarget = target; } } public bool FindMyName( string str, bool bWithAll ) { int i, j; string name = this.Name; if( name == null || str.Length < name.Length ) return false; string[] wordsString = str.Split(' '); string[] wordsName = name.Split(' '); for ( j=0 ; j < wordsName.Length; j++ ) { string wordName = wordsName[j]; bool bFound = false; for ( i=0 ; i < wordsString.Length; i++ ) { string word = wordsString[i]; if ( Insensitive.Equals( word, wordName ) ) bFound = true; if ( bWithAll && Insensitive.Equals( word, "all" ) ) return true; } if ( !bFound ) return false; } return true; } public static void TeleportPets( Mobile master, Point3D loc, Map map ) { TeleportPets( master, loc, map, false ); } public static void TeleportPets( Mobile master, Point3D loc, Map map, bool onlyBonded ) { List<Mobile> move = new List<Mobile>(); foreach ( Mobile m in master.GetMobilesInRange( 3 ) ) { if ( m is BaseCreature ) { BaseCreature pet = (BaseCreature)m; if ( pet.Controlled && pet.ControlMaster == master ) { if ( !onlyBonded || pet.IsBonded ) { if ( pet.ControlOrder == OrderType.Guard || pet.ControlOrder == OrderType.Follow || pet.ControlOrder == OrderType.Come ) move.Add( pet ); } } } } foreach ( Mobile m in move ) m.MoveToWorld( loc, map ); } public virtual void ResurrectPet() { if ( !IsDeadPet ) return; OnBeforeResurrect(); Poison = null; Warmode = false; Hits = 10; Stam = StamMax; Mana = 0; ProcessDeltaQueue(); IsDeadPet = false; Effects.SendPacket( Location, Map, new BondedStatus( 0, this.Serial, 0 ) ); this.SendIncomingPacket(); this.SendIncomingPacket(); OnAfterResurrect(); Mobile owner = this.ControlMaster; if ( owner == null || owner.Deleted || owner.Map != this.Map || !owner.InRange( this, 12 ) || !this.CanSee( owner ) || !this.InLOS( owner ) ) { if ( this.OwnerAbandonTime == DateTime.MinValue ) this.OwnerAbandonTime = DateTime.Now; } else { this.OwnerAbandonTime = DateTime.MinValue; } CheckStatTimers(); } public override bool CanBeDamaged() { if ( IsDeadPet ) return false; return base.CanBeDamaged(); } public virtual bool PlayerRangeSensitive{ get{ return true; } } public override void OnSectorDeactivate() { if ( PlayerRangeSensitive && m_AI != null ) m_AI.Deactivate(); base.OnSectorDeactivate(); } public override void OnSectorActivate() { if ( PlayerRangeSensitive && m_AI != null ) m_AI.Activate(); base.OnSectorActivate(); } private bool m_RemoveIfUntamed; // used for deleting untamed creatures [in houses] private int m_RemoveStep; [CommandProperty( AccessLevel.GameMaster )] public bool RemoveIfUntamed{ get{ return m_RemoveIfUntamed; } set{ m_RemoveIfUntamed = value; } } [CommandProperty( AccessLevel.GameMaster )] public int RemoveStep { get { return m_RemoveStep; } set { m_RemoveStep = value; } } } public class LoyaltyTimer : Timer { private static TimeSpan InternalDelay = TimeSpan.FromMinutes( 5.0 ); public static void Initialize() { new LoyaltyTimer().Start(); } public LoyaltyTimer() : base( InternalDelay, InternalDelay ) { m_NextHourlyCheck = DateTime.Now + TimeSpan.FromHours( 1.0 ); Priority = TimerPriority.FiveSeconds; } private DateTime m_NextHourlyCheck; protected override void OnTick() { bool hasHourElapsed = ( DateTime.Now >= m_NextHourlyCheck ); if ( hasHourElapsed ) m_NextHourlyCheck = DateTime.Now + TimeSpan.FromHours( 1.0 ); List<BaseCreature> toRelease = new List<BaseCreature>(); // added array for wild creatures in house regions to be removed List<BaseCreature> toRemove = new List<BaseCreature>(); foreach ( Mobile m in World.Mobiles.Values ) { if ( m is BaseMount && ((BaseMount)m).Rider != null ) { ((BaseCreature)m).OwnerAbandonTime = DateTime.MinValue; continue; } if ( m is BaseCreature ) { BaseCreature c = (BaseCreature)m; if ( c.IsDeadPet ) { Mobile owner = c.ControlMaster; if ( owner == null || owner.Deleted || owner.Map != c.Map || !owner.InRange( c, 12 ) || !c.CanSee( owner ) || !c.InLOS( owner ) ) { if ( c.OwnerAbandonTime == DateTime.MinValue ) c.OwnerAbandonTime = DateTime.Now; else if ( (c.OwnerAbandonTime + c.BondingAbandonDelay) <= DateTime.Now ) toRemove.Add( c ); } else { c.OwnerAbandonTime = DateTime.MinValue; } } else if ( c.Controlled && c.Commandable ) { c.OwnerAbandonTime = DateTime.MinValue; if ( c.Map != Map.Internal ) { // Every hour all pets lose 10% of max loyalty. if ( hasHourElapsed ) c.Loyalty -= (BaseCreature.MaxLoyalty / 10); if( c.Loyalty < (BaseCreature.MaxLoyalty / 10) ) { c.Say( 1043270, c.Name ); // * ~1_NAME~ looks around desperately * c.PlaySound( c.GetIdleSound() ); } if ( c.Loyalty <= 0 ) toRelease.Add( c ); } } // added lines to check if a wild creature in a house region has to be removed or not if ( !c.Controlled && ( c.Region.IsPartOf( typeof( HouseRegion ) ) && c.CanBeDamaged() || hasHourElapsed && c.RemoveIfUntamed && c.Spawner == null ) ) { c.RemoveStep++; if ( c.RemoveStep >= 20 ) toRemove.Add( c ); } else { c.RemoveStep = 0; } } } foreach ( BaseCreature c in toRelease ) { c.Say( 1043255, c.Name ); // ~1_NAME~ appears to have decided that is better off without a master! c.Loyalty = BaseCreature.MaxLoyalty; // Wonderfully Happy c.IsBonded = false; c.BondingBegin = DateTime.MinValue; c.OwnerAbandonTime = DateTime.MinValue; c.ControlTarget = null; //c.ControlOrder = OrderType.Release; c.AIObject.DoOrderRelease(); // this will prevent no release of creatures left alone with AI disabled (and consequent bug of Followers) } // added code to handle removing of wild creatures in house regions foreach ( BaseCreature c in toRemove ) { c.Delete(); } } } } anyone know whats wrong with it?
__________________
love me or hate me but u better love me cause im 270 lbs and 6' 6" |
10-20-2006, 09:05 AM | #13 (permalink) |
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
to copy the contents of the console window so that you can paste the error message into a post, do this
right click on the console window title bar to bring up the menu. right-click->Edit->Select All right-click->Edit->Copy then you can Paste the text whereever you like. Also, when you post code, put it between code tags like this [code] put code in here [/code] (edit) Code:
public override void OnBeforeSpawn( Point3D location, Map m ) { if ( Paragon.CheckConvert( this, location, m ) ) IsParagon = true; // mod to randomly add socket features to tamable creatures. These settings will yield // 2% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) // Adding new sockets will require a minimum of 100 Veterinary skill and 30 emeralds if(Tamable) XmlSockets.ConfigureRandom(this, 2.0, 0.1, 0.5, 3.0, 15.0, 50.0, SkillName.Veterinary, 100.0, typeof(Emerald), 30); base.OnBeforeSpawn( location, m ); } base.OnBeforeSpawn( location, m ); } There may be other problems but I would start with that one.
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum Last edited by ArteGordon; 10-20-2006 at 09:11 AM. |
10-20-2006, 09:51 AM | #14 (permalink) |
Newbie
|
thanks
thanks that fixed all the errors sorry about that really long page i dident know but i have another problem they nevere drop socked items i set the drop rate higher and all any advice?
__________________
love me or hate me but u better love me cause im 270 lbs and 6' 6" Last edited by garthro; 10-20-2006 at 09:54 AM. |
10-20-2006, 12:41 PM | #15 (permalink) | |
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
Quote:
If you did not mod those scripts then you would want to decide how you wanted socketed items to drop. The step #3 mods give all armor/weapons/jewelry a chance of being socketed, whether it is dropped, crafted, spawned, etc. If you just wanted to restrict it to dropped loot, you could make the mod in lootpack.cs instead of in the individual basearmor.cs, baseweapon.cs, basejewel.cs scripts. Like this Code:
public Item Mutate( Mobile from, int luckChance, Item item ) { if ( item != null ) { if ( item is BaseWeapon && 1 > Utility.Random( 100 ) ) { item.Delete(); item = new FireHorn(); return item; } if ( item is BaseWeapon || item is BaseArmor || item is BaseJewel || item is BaseHat ) { if ( Core.AOS ) { int bonusProps = GetBonusProperties(); int min = m_MinIntensity; int max = m_MaxIntensity; if ( bonusProps < m_MaxProps && LootPack.CheckLuck( luckChance ) ) ++bonusProps; int props = 1 + bonusProps; // Make sure we're not spawning items with 6 properties. if ( props > m_MaxProps ) props = m_MaxProps; if ( item is BaseWeapon ) BaseRunicTool.ApplyAttributesTo( (BaseWeapon)item, false, luckChance, props, m_MinIntensity, m_MaxIntensity ); else if ( item is BaseArmor ) BaseRunicTool.ApplyAttributesTo( (BaseArmor)item, false, luckChance, props, m_MinIntensity, m_MaxIntensity ); else if ( item is BaseJewel ) BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, false, luckChance, props, m_MinIntensity, m_MaxIntensity ); else if ( item is BaseHat ) BaseRunicTool.ApplyAttributesTo( (BaseHat)item, false, luckChance, props, m_MinIntensity, m_MaxIntensity ); // ARTEGORDONMOD // mod to randomly add sockets and socketability features to certain types of loot. These settings will yield // 10% drop rate of socketed/socketable items // 0.1% chance of 5 sockets // 0.5% of 4 sockets // 3% chance of 3 sockets // 15% chance of 2 sockets // 50% chance of 1 socket // the remainder will be 0 socket (31.4% in this case) XmlSockets.ConfigureRandom(item, 10.0, 0.1, 0.5, 3.0, 15.0, 50.0); }
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum Last edited by ArteGordon; 10-20-2006 at 12:56 PM. |
|
12-03-2006, 08:16 PM | #17 (permalink) | |
Forum Master
Join Date: Aug 2003
Posts: 5,688
|
Quote:
A few simple mods to Repair.cs would make that possible. around line 128 Code:
protected override void OnTarget( Mobile from, object targeted ) { int number; if( !CheckDeed( from ) ) return; bool hasSockets = (XmlAttach.FindAttachment(targeted, typeof(XmlSockets)) != null); Code:
if ( m_CraftSystem.CraftItems.SearchForSubclass( weapon.GetType() ) == null && !IsSpecialWeapon( weapon ) && !hasSockets) { number = (usingDeed)? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. } Code:
if ( m_CraftSystem.CraftItems.SearchForSubclass( armor.GetType() ) == null && !hasSockets) { number = (usingDeed)? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. } Code:
if ( m_CraftSystem.CraftItems.SearchForSubclass( clothing.GetType() ) == null && !hasSockets) { number = (usingDeed) ? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. } Code:
using Server.Engines.XmlSpawner2;
__________________
The first line of the first rule in the forum rules and guidelines "Be respectful of others. " For questions, information, and support for XmlSpawner and its addons, visit the XmlSpawner Support Forum |
|
03-27-2008, 11:10 PM | #18 (permalink) |
Forum Novice
Join Date: Feb 2008
Location: UOResurgence.com
Age: 28
Posts: 211
|
Hey Arte, I've tried registering for the XMLSpawner-Fan forums to ask this question over there, but I keep getting a note that the security code I've entered does not match the picture (It matches, and I tried about 10 times to get it to go through).
Anyway, what I want to do is make it so that the only way the socketable attribute is added is via crafting by using runic hammers. I'm trying to add a case 0 (commented everything else out) in the BaseRunicTool.cs file to add a single socketable. That way the higher level hammers will add more socketables. Then players will use the socket hammer to actually add the sockets. Can anyone help me out with this one? |
03-28-2008, 01:32 PM | #19 (permalink) |
Forum Novice
Join Date: Feb 2008
Location: UOResurgence.com
Age: 28
Posts: 211
|
Ok, after re-reading my request for help I realized I didn't really give much to work with. Here's the portion of my BaseRunicTool.cs that I'm working with:
Code:
publicstaticvoid ApplyAttributesTo( BaseWeapon weapon, bool isRunicTool, int luckChance, int attributeCount, int min, int max ) { m_IsRunicTool = isRunicTool; m_LuckChance = luckChance; AosAttributes primary = weapon.Attributes; AosWeaponAttributes secondary = weapon.WeaponAttributes; m_Props.SetAll( false ); if ( weapon is BaseRanged ) m_Props.Set( 2, true ); // ranged weapons cannot be ubws or mageweapon for ( int i = 0; i < attributeCount; ++i ) { int random = GetUniqueRandom( 25 ); if ( random == -1 ) break; switch ( random ) { case 0: XmlAttach.AttachTo(this, new XmlSocketable(1)); /* case 0: { switch ( Utility.Random( 5 ) ) { case 0: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitPhysicalArea,2, 50, 2 ); break; case 1: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitFireArea, 2, 50, 2 ); break; ... // case 24: GetElementalDamages( weapon ); break;*/ } } } and then here's the error I'm getting on compile: Code:
Errors: + Items/Skill Items/Tools/BaseRunicTool.cs: CS0026: Line 285: Keyword 'this' is not valid in a static property, static m ethod, or static field initializer |
03-28-2008, 03:07 PM | #20 (permalink) | ||
Forum Expert
Join Date: Mar 2004
Location: NorthCentral IL, USA
Age: 36
Posts: 3,974
|
Quote:
first change this to weapon... that should theoretically remove your error... beyond that arte is going to have to help with the rest. if you get this to work Please post what you did. I'd like to make it so certain runic values add certain sockets too... I also have this installed but at the moment it's random (like it is by default). So I'll be watching this thread and if Arte get's a chance cross post it on XMLSpawner fan's site. If he doesn't cross post it there I will to keep it from getting lost.
__________________
Quote:
Just a Simple Staff Tool You can leave me messages. Ernest Gary Gygax - Quote "I would like the world to remember me as the guy who really enjoyed playing games and sharing his knowledge and his fun pastimes with everybody else." |
||
03-28-2008, 06:40 PM | #21 (permalink) |
Forum Novice
Join Date: Feb 2008
Location: UOResurgence.com
Age: 28
Posts: 211
|
Thanks, that did remove the error. But it doesn't work. I can't register for the xmlspawnerfan.com forums, otherwise I would post it directly there. Do you mind cross-posting it for me?
|
03-28-2008, 06:57 PM | #22 (permalink) | |
Forum Expert
Join Date: Mar 2004
Location: NorthCentral IL, USA
Age: 36
Posts: 3,974
|
Done. Arte does also check here when he has a chance. I posted a link to this thread so he'll probably post the answer here. If enough people want this feature he might make a way to do it optionally as well (he's done it for other things).
__________________
Quote:
Just a Simple Staff Tool You can leave me messages. Ernest Gary Gygax - Quote "I would like the world to remember me as the guy who really enjoyed playing games and sharing his knowledge and his fun pastimes with everybody else." |
|
03-29-2008, 02:20 AM | #23 (permalink) |
Forum Novice
Join Date: Feb 2008
Location: UOResurgence.com
Age: 28
Posts: 211
|
Thanks for the crosspost. I do want to make sure I'm clear on something, though. If Arte can find a way to add a "socketable" via the BaseRunicTool.cs script, that means that both monster loot and player runic craftables will have the socketable feature... right? The maximum number of sockets will be based on the runic hammer type (i.e. valorite vs. shadow) or loot intensity drop? No need to further modify the Lootpack.cs other than to add in the augments as loot?
That's ultimately what I'm going for. Then I plan on making the sockethammer available for sale and modify the augments to suit my shard. That way there's no randomness in crafting anymore, it's all based on getting your desired augments and applying them to the weapon/armor of your choice. |
03-29-2008, 11:19 AM | #24 (permalink) | ||
Forum Expert
Join Date: Mar 2004
Location: NorthCentral IL, USA
Age: 36
Posts: 3,974
|
Quote:
At this point in time I work on trying to mod BaseRunic myself I'll let you know if I come up with an alternative. Currently testing Code:
int sock; if (max > 6) sock = 6; else if (max <= 1) sock = 1; else sock = max; XmlAttach.AttachTo(jewelry, new XmlSocketable(sock)); PS) One of my crafters just got exited that he created 2 Socketed items with 6 sockets max (in a row) so my code is working. Now I have to decide if I want it to go off Max or try to get an average of min/max then use the average number instead.
__________________
Quote:
Just a Simple Staff Tool You can leave me messages. Ernest Gary Gygax - Quote "I would like the world to remember me as the guy who really enjoyed playing games and sharing his knowledge and his fun pastimes with everybody else." Last edited by Greystar; 03-29-2008 at 12:29 PM. |
||
Bookmarks |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
|
|